Finding your way around a version control system can be tricky. It can be massively overwhelming for a newbie, but being well-versed with the terminology and the basics of a version control system like Git is one of the baby steps to start contributing to open source.
Being familiar with Git can also help you out of sticky situations in your open source journey. Git is powerful and makes you feel in control—there is not a single way in which you cannot revert to a working version.
Here is an example to help you understand the importance of cherry-picking. Suppose you have made several commits in a branch, but you realize it's the wrong branch! What do you do now? Either you repeat all your changes in the correct branch and make a fresh commit, or you merge the branch into the correct branch. Wait, the former is too tedious, and you may not want to do the latter. So, is there a way? Yes, Git's got you covered. Here is where cherry-picking comes into play. As the term suggests, you can use it to hand-pick a commit from one branch and transfer it into another branch.
There are various reasons to use cherry-picking. Here are three of them.
Avoid redundancy of efforts
There's no need to redo the same changes in a different branch when you can just copy the same commits to the other branch. Please note that cherry-picking commits will create a fresh commit with a new hash in the other branch, so please don't be confused if you see a different commit hash.
In case you are wondering what a commit hash is and how it is generated, here is a note to help you: A commit hash is a string generated using the SHA-1 algorithm. The SHA-1 algorithm takes an input and outputs a unique 40-character hash. If you are on a POSIX system, try running this in your terminal:
$ echo -n "commit" | openssl sha1
This outputs a unique 40-character hash, 4015b57a143aec5156fd1444a017a32137a3fd0f
. This hash represents the string commit
.
A SHA-1 hash generated by Git when you make a commit represents much more than just a single string. It represents:
sha1(
meta data
commit message
committer
commit date
author
authoring date
Hash of the entire tree object
)
This explains why you get a unique commit hash for the slightest change you make to your code. Not even a single change goes unnoticed. This is because Git has integrity.
Undoing/restoring lost changes
Cherry-picking can be handy when you want to restore to a working version. When multiple developers are working on the same codebase, it is very likely for changes to get lost and the latest version to move to a stale or non-working version. That's where cherry-picking commits to the working version can be a savior.
How does it work?
Suppose there are two branches, feature1
and feature2
, and you want to apply commits from feature1
to feature2
.
On the feature1
branch, run a git log
command, and copy the commit hash that you want to cherry-pick. You can see a series of commits resembling the code sample below. The alphanumeric code following "commit" is the commit hash that you need to copy. You may choose to copy the first six characters (966cf3
in this example) for the sake of convenience:
commit 966cf3d08b09a2da3f2f58c0818baa37184c9778 (HEAD -> master)
Author: manaswinidas <me@example.com>
Date: Mon Mar 8 09:20:21 2021 +1300
add instructions
Then switch to feature2
and run git cherry-pick
on the hash you just got from the log:
$ git checkout feature2
$ git cherry-pick 966cf3.
If the branch doesn't exist, use git checkout -b feature2
to create it.
Here's a catch: You may encounter the situation below:
$ git cherry-pick 966cf3
On branch feature2
You are currently cherry-picking commit 966cf3d.
nothing to commit, working tree clean
The previous cherry-pick is now empty, possibly due to conflict resolution.
If you wish to commit it anyway, use:
git commit --allow-empty
Otherwise, please use 'git reset'
Do not panic. Just run git commit --allow-empty
as suggested:
$ git commit --allow-empty
[feature2 afb6fcb] add instructions
Date: Mon Mar 8 09:20:21 2021 +1300
This opens your default editor and allows you to edit the commit message. It's acceptable to save the existing message if you have nothing to add.
There you go; you did your first cherry-pick. As discussed above, if you run a git log
on branch feature2
, you will see a different commit hash. Here is an example:
commit afb6fcb87083c8f41089cad58deb97a5380cb2c2 (HEAD -> feature2)
Author: manaswinidas <me@example.com>
Date: Mon Mar 8 09:20:21 2021 +1300
add instructions
Don't be confused about the different commit hash. That just distinguishes between the commits in feature1
and feature2
.
Cherry-pick multiple commits
But what if you want to cherry-pick multiple commits? You can use:
git cherry-pick <commit-hash1> <commit-hash2>... <commit-hashn>
Please note that you don't have to use the entire commit hash; you can use the first five or six characters.
Again, this is tedious. What if the commits you want to cherry-pick are a range of continuous commits? This approach is too much work. Don't worry; there's an easier way.
Assume that you have two branches:
feature1
includes commits you want to copy (fromcommitA
(older) tocommitB
).feature2
is the branch you want the commits to be transferred to fromfeature1
.
Then:
- Enter
git checkout <feature1>
. - Get the hashes of
commitA
andcommitB
. - Enter
git checkout <branchB>
. - Enter
git cherry-pick <commitA>^..<commitB>
(please note that this includescommitA
andcommitB
). - Should you encounter a merge conflict, solve it as usual and then type
git cherry-pick --continue
to resume the cherry-pick process.
Important cherry-pick options
Here are some useful options from the Git documentation that you can use with the cherry-pick
command:
-e
,--edit
: With this option,git cherry-pick
lets you edit the commit message prior to committing.-s
,--signoff
: Add a "Signed-off-by" line at the end of the commit message. See the signoff option in git-commit(1) for more information.-S[<keyid>]
,--gpg-sign[=<keyid>]
: These are GPG-sign commits. Thekeyid
argument is optional and defaults to the committer identity; if specified, it must be stuck to the option without a space.--ff
: If the current HEAD is the same as the parent of the cherry-picked commit, then a fast-forward to this commit will be performed.
Here are some other sequencer subcommands (apart from continue):
--quit
: You can forget about the current operation in progress. This can be used to clear the sequencer state after a failed cherry-pick or revert.--abort
: Cancel the operation and return to the presequence state.
Here are some examples of cherry-picking:
git cherry-pick master
: Applies the change introduced by the commit at the tip of the master branch and creates a new commit with this changegit cherry-pick master~4 master~2
: Applies the changes introduced by the fifth and third-last commits pointed to by master and creates two new commits with these changes
Feeling overwhelmed? You needn't remember all the commands. You can always type git cherry-pick --help
in your terminal to look at more options or help.
3 Comments