Git cherry-picking multiple commits from one branch into another branch

How do you move an entire feature you worked on in git to another branch( e.g. the master branch)? For that you would need to pick multiple commits to git from an older feature branch, say 6.1 to an.other branch, say master. Use the git cherry-pick  command to pick your earlier commits from your 6.1 branch to the master branch.

git-cherry-pick-diagram

Before you do this, you would need a list of all your commits in branch 6.1. You can use the following git log command to view all commits by you:

git log --author=ninad

Or if you want to search by a particular ticket you can use grep:

git log --grep=apple

Above command finds everything with the word apple in your git logs. you can combine the two commands to display all logs containing the word apple having  the author ninad as:

git log --author=ninad --grep=apple

If your team uses Gerrit, another way which I found very convenient is to use the web UI of Gerrit to search through all the logs with GUI. Go to Gerrit UI > Projects > Find your project > click on shortlog

So now you have a list of commit id’s from branch 6.1 that you are ready to cherry pick one by one into your master branch. I recommend creating your own feature branch from the master branch first and then merging this back with the master branch when you are done. Use:

git checkout master //Switch to master

git pull //Make sure you have the latest master

git branch -b //Your_branch_name

let’s say your commit id is 57228f616d8e91008c8328c5822a8a481e15728a. For cherry picking use the command:

git cherry-pick  -x 57228f616d8e91008c8328c5822a8a481e15728a

This would pull your commit in branch 6.1 having the above commit id into your new feature branch you just created.

Let’s say you do cherry-pick the next commit-id and git complaints:

git cherry-pick  -x 118921c16d8e91008c8328c5822a8a481e15728a

error: could not apply 118921c...tkt-7875 added tag to main pom.xml
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add ' or 'git rm '
hint: and commit the result with 'git commit'

Whoops! Git is complaining because the commit you are trying to cherry-pick is modifying the same parts of a file that had already been modified by  the previous commit(in this case previous commit was your cherry-pick having commit id starting with 5722).
The reason Git does this is because it does not know which version of the file to keep. For example, let’s say line number 5 of pom.xml was edited in the previous commit and it was again edited in this commit starting with 1189, now git would throw  the above message because it would not know which file’s content to keep at line number 5.

This brings us to the important next section of resolving git merge conflicts. I recommend choosing your strategy based on whether the file causing a merge conflict is a maven pom.xml.
1) If the conflicting file is not a maven pom.xml file:

Use a git-mergetool to resolve conflicts. You can type in

git-mergetool

This will cause a GUI like display with four windows to pop up on the screen. To quit this screen recursively without saving at any point type in Esc(:) + qa. I had a lot of trouble understanding the four screens and how to merge contents of the file to resolve the conflict. git-mergetool used vimdiff when I tried this out, in vimdiff the top left window labeled ‘LOCAL’ contains the current version of your file, the middle window labeled ‘BASE’ contains the common version of your file before your previous commit, the right window labeled ‘REMOTE’ contains your file version from remote branch you are trying to merge from(in this case it starts with commit id 1189), the bottom window contains the final version of the file that you would like to save and resolve this conflict.

Using Ctrl+W allows you to navigate through the windows and using :diffo and :difft toggles diff off and diff this(on) respectively.

2) If the conflicting file is a maven pom.xml file:

This is trickier to resolve with git-mergetool because pom files are typically large with version changes that are very hard to track manually. For this I recommend keeping you current maven pom.xml file that is valid by typing:

git checkout --ours path/to/pom.xml

This tells git to keep our current version, but the conflict is not yet resolved. Now you can use a web UI tool like Gerrit to see what was actually added/deleted in the pom.xml when you cherry-picked the commit starting with 1189. You would need to manually copy those added lines to your current pom.xml. Use:

vi path/to/pom.xml //open the file

//Use vi editor to copy the changes made in your remote pom.xml i.e.
//(starting with commit id 1189 in this case)
//Save the file

Note that after you have done Step 1 or 2 above, you still need to add the pom.xml file and commit it. Use:

git add path/to/pom.xml

git status //check that all conflicts have been resolved

git commit -m 'Comment to commit with'

Only after you commit will git get out of the ‘*+CHERRY-PICKING’ mode and return to your feature branch. You can now continue to add the remaining commit ids.

After you are done you can see all your commits in one line using:

git log --pretty=oneline

Let’s say you cherry picked 11 commits and you don’t want to push each commit the remote master branch. To avoid doing this, you can squash the last 9 commits using

git rebase -i HEAD~9

Now you can tell git what to do with each commit. The screen will look something like:

pick 57228f6 TKT-7875 added 
squash 118921c Merging pom.xml
squash b02a8b9 Comment c
squash 03146cc Comment d
squash aba5885 Comment e
squash 5807756 Comment f
squash 59b907  Comment g 
squash c973496 Comment h 
squash 57228f6 Comment i

# Rebase c01d320..57228f6 onto c01d320
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

Keep the fist line as picked and change the remaining lines to squash. This will make git squash all the rest of the commits to the first cherry-picked commit it. Git will squash all commits into one and show you a screen with the heading ‘This is a combination of 9 commits’. You can add or remove any comments on this point, this will be the comment displayed next to your squashed commit, then save and quit. Git should display a message saying ‘Successfully rebased and updated refs/heads/Your_feature_branch.’

Done!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s