Each commit in a repository corresponds to a full tree of files. Usually, these files have been created over a number of commits. But sometimes, it’s necessary to take the delta between two commits, and apply it to a different branch.
One common case where this occurs is when an issue has been identified, and subsequently fixed, but needs to be backported to a previous release branch.
In this case, you don’t want to take the current state of the tree (which might have unfinished or untested changes); you just want to take the delta associated with that change.
In other version control systems, you would just create a diff based on the most recent change, and then patch the change on to your release branch. Instead, with Git, we can use the
cherry-pick command to do the work for us:
$ git checkout master $ echo Working >> file.txt $ git commit -m "Working" file.txt $ echo BugFix >> bugfix.txt $ git commit -m "BugFix" bugfix.txt $ echo More Working >> file.txt $ git commit -m "More working" file.txt # We want to apply 'bugfix' to release $ git checkout release10 $ git cherry-pick master~1 [release10 41037ab] BugFix 0 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 bugfix.txt
This has allowed us to take a single change – described here with
master~1 – and copy the delta into the release branch.
Sets of changes
We can pick commit sets (ranges of revisions) to pick if we wanted to. Had we had several changes, we could have
master~3..master~1. Unlike just generating a diff and then patching the current tree, this will copy the commits (and their relationship) over to the new branch.
We’ve actually seen pick in use already; when we covered rebasing last week. When you create a series of commands for rebasing, you’re actually giving it instructions to pick or edit existing changes:
@ edit 7bf9271 Typo @ pick 756281e First @ fixup 07e9061 First @ pick 13aba60 Second
pick” here means the same as “
git cherry-pick” for the single change.
In fact, “
edit” is really a short-hand for “
git cherry-pick -e”, and “
fixup” and “
squash” are short-hands for
git cherry-pick -n.
Finally, it’s worth noting that when you copy a change using this mechanism, the commit hash will change (notably because it will have a different parent hierarchy).
Sometimes that doesn’t matter, but if you want to record where the original change came from, you can run
git cherry-pick -x. This inserts a commit message indicating where the original change came from:
# From example above $ git checkout release10 $ git cherry-pick -x master~1 $ git cherry-pick master~1 [release10 41037ab] BugFix (cherry picked from commit 938a4c0bbb3985524192aa8a926ea6757263e94b) 0 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 bugfix.txt
However, note that this usually only makes sense if the change that’s being cherry-picked is from a public branch (so that the referenced change is visible). Another way of representing this change is to create a merge node between the release branch and the ongoing development branch as a way of showing that the merge has occurred.
Creating new history
Whenever you are cherry-picking, especially if you are reordering commits, you’re creating new history. However, you’ve never really thrown away old history; it’s all available from the reflogs. You’re not destroying history, you’re creating alternate histories. All cherry-picking gives you is the ability to apply patches from other branches in a safe and error-less manner.
Come back next week for another instalment in the Git Tip of the Week series.