Cherry picking in git is one of the very important commands that you will come across when working in tech. It’s not hard, but also not easy as well.

What is cherry picking ?

It is a technique where you pick changes from commit or commits from one branche and apply on to your another branch. This is one of many ways ( eg merge or rebase ) you can bring in changes from other branches to your branch. Note that cherry-pick is similar to rebase i.e it replays the commits up similar how rebase does, but instead of specific branch, we try to replay changes to a specific commit.

When should we use cherry picking ?

It is mostly used when there is a hotfix, bring in changes or features which has not yet been release but you want on your branch, but you can use it any time when you need changes from other developers into your branch. Suppose a developer is working on auth, and your service needs to have aut but auth has yet not been realease to main then you can cherry pick auth commits from other developer branch and use it in yours.

How do i cherry pick a specific commit ?

To chery pick a specific commit, first check if you are on correct branch. you should be on branch which will get the cherry-picked commits from other branch.

First get the commit hash of from the branch you want to pick specific commit. Once you do that issue in order to cherry pick a specific commit you just issue a command git cherry-pick HASH_OF_COMMIT_FROM_OTHER_BRANCH

If you have a branch called branch1 which has commits A,B,C,D,E,F,G (All of these letter are commit ID or commit HASH ) and you want commits B,D,F into your working branch dev-876 then you would issue following command

$ git cherry-pick -x B

If everything goes ok, you will get successfully you will see output like this

$ git cherry-pick -x 84743de
[feature d8d8d68] added fetfile.txt git commit -m added
Date: Fri Aug 6 17:03:03 2021 +0300
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 featfile.txt

In above command -x makes the cherry pick commit looks nice by adding something like (cherry picked from commit cbeb8e856c45b8asdkjhkjkahsdk31c180) into the commit message. This way you can know that commit was the result of cherry picking.

If conflict occured, then go through each file that has the conflict ( as shown below ), resolve the conflict. By resolve, i mean go through each file which has conflict and check the code. Sometime you need to be careful, as whie brining in the change, dont remove your own.

$ git cherry-pick -x 84743de
CONFLICT (content): Merge conflict in src/components/ConditionalActionsComponent.tsx
+ fe-se fe-s
Auto-mergings /AddRequirementModal,test.tsx
fe-service-desk
CONFLICT (content): Merge conflict in src/utils/__tests_/DocumentUtils.test.ts
fe-service-desk error: could not apply 892bbc... 74-hotfix added the delete feature on requirements table

In the above cherry-pick operation we saw there is two conflict in ConditionalActionsComponent.tsx and DocumentUtils.test.ts file. Go to each file and resolve the conflict. Once conflicts has been resolved, you would need to go a git add . and continue cherry picking using git cherry-pick --continue

$ git add .
$ git cherry-pick --continue

if at any point you feel there is some issue then feel free to abort.

$ git cherry-pick --abort

You can message me here if you are unsure and i will help.

Once the cherry pick of one commit is completed, you repeat the same step i.e

$ git cherry-pick -x D
$ git cherry-pick -x F

How do i cherry pick multiple commits ?

As you saw when you need to cherry pick multiple commits is a hassel but fear not there is a easier way to group them.

If you have a branch called branch1 which has commits A,B,C,D,E,F,G (All of these letter are commit ID or commit HASH ) and you want commits B,D,F into your working branch dev-876 then you would issue following command

$ git checkout dev-876
$ git cherry-pick -x B D F

If everything is okay, it will succed and if conflict is detected, you would need to resolve it and continue as mentioned above using

$ git add .
$ git cherry-pick --continue

Note: Cherry picking a specific commit is different to cherry picking a merge commit.

How do i cherry pick a merge commit ?

Cherry picking a merge commits are different to other commits. To cherry pick a merge commit, your should first know what is merge commit.

A merge commit is just like other commits i.e it will have commit hash, Author,Date, commit msg except merge commit has another parameter called Merge which shows at least two predecessors.

If you do a git log you can see how normal commits differs from a merge commit.

This is a normal commit

commit bb8e5e0a020dc82999c5b6488eefac7675554e40
Author: Prabesh Thapa <[email protected]>
Date:   Mon Nov 7 19:01:41 2022 -0800

    add(bc): added add funtionnality basic

And this is a merge commit

commit df36ea6c03a0beb95238318cde4e480b37e0d970
Merge: a4c63ca bb8e5e0
Author: Prabesh Thapa <[email protected]>
Date:   Tue Nov 8 17:19:25 2022 -0800

    Merge pull request #7 from pgaijin66/feat/add-files/interactive-chhose-files-to-stage

    add(bc): added interactive add funtionnality

Saw the difference ?? Look closely, normal commit is different to merge commit with Merge: a4c63ca bb8e5e0

Those two paramers show two parents of that commit, where a4c63ca is the main line and bb8e5e0 is the branch. Both of them are parent of the merge commit df36ea6c03a0beb95238318cde4e480b37e0d970.

Now you know what is a merge commit and how does it look like, cherry picking merge commit is similar to regular cherry pick but here you would need to specify cherry-pick command against which parent the diff should be calculated which can be specified using -m option.

To undestand this, lets come to cherry picking again

If you have a branch called branch1 which has commits history like this

- A - D - E - F -   branch1
   \     /
    B - C           forked branch

and you want to cherry pick changes from commit E from branch1 into your destination branch dev-876, then if you try to cherry pick then you will be prompted with following error

$ git checkout dev-876 # First switching to branch which where we will bring in the cherry picked commit.
$ git cherry-pick E
fatal: Commit E is a merge but no -m option was given.

hmm, what happened here ?

As we already discussed the commit E is a merge commit hence it has two parents D and C. This can be explained better with real example

let’s see the follwoing merge commit

commit df36ea6c03a0beb95238318cde4e480b37e0d970
Merge: a4c63ca bb8e5e0
Author: Prabesh Thapa <[email protected]>
Date:   Tue Nov 8 17:19:25 2022 -0800

    Merge pull request #7 from pgaijin66/feat/add-files/interactive-chhose-files-to-stage

    add(bc): added interactive add funtionnality

In the above example if you want to cherry-pick changes from parent 1 which is a4c63ca to my branch dev-876

Then i can do this by

$ git checkout dev-876
$ git cherry-pick df36ea6c03a0beb95238318cde4e480b37e0d970 -m 1

or if you want to cherry-pick changes from parent 2 which is bb8e5e0

Then can do this by

$ git cherry-pick df36ea6c03a0beb95238318cde4e480b37e0d970 -m 2

Suggestion 1

To avoid merge commits you can rebase your changes before pushing them to remote also while merging the PR do a rebase instead of merge. It will make your life much easier.

Suggestion 2

One rule of thumb i use before cherry picking is, first creating a branch from original branch where you want the picked commits to be implemented and working on it ( cherry-picking ) and then doing a PR to merge to the my intended branch. This is because when we cherry-pick, it collapses all the changes made in parent into one single commit.

Eg. If you have a branch called branch1 which has commits A,B,C,D,E,F,G and you want commits B,D,F into your working branch dev-876 then first of all instead of directly cherry picking from dev-876 i create a branch off it again something like dev-876-cherry-pick ( i call it a "contingency branch" ). It gives me much confidence that if anything happens it will not affect my original dev branch i.e dev-876.

$ git checkout dev-876
$ git checkout -b branch1-cherry-pick