$ git status # On branch stable-1.12 # Your branch is ahead of 'origin/stable-1.12' by 41 commits.
Tag Archives: cherry pick
For more half-baked ideas, see my ideas tag
Consider these three git commits A, B and C:
As far as git’s linear history is concerned, they are three independent commits and git only records the parent relationship A -> B -> C.
But could I cherry pick just patch C, omitting A and B? git won’t tell me, but I can examine the patches themselves (A, B, C) and answer this question (the answer is no, since the change in C depends on both changes A and B being applied first). The real dependency tree looks like this:
C ^ ^ / \ A --> B
Many other real dependency trees could have been possible. With another choice of A, B and C these might have been completely independent of each other, or (A, B) might have to be applied together, with C being independent.
The half-baked idea is whether we can write an automatic tool which can untangle these dependencies from the raw git commits? (Or whether such a tool exists already, I cannot find one)
There would be one important practical use for such a tool. When cherry picking commits for the stable branch, I would like to know which previous commits that the commit I’m trying to apply depends on. This gives me extra information: I can decide that applying this commit is too disruptive — perhaps it depends on an earlier feature which I don’t want to add. I can decide to go back and apply the older commits, or that a manual backport is the best way.
The information you can derive from patches doesn’t tell the whole story. There are two particular problems, one revealed by the choice of patches A, B and C above. With a trivial change to B, it is possible to apply A and B independently. There is only a dependency A -> B because a little bit of the context of patch A “leaked” into patch B. It is also possible for two features to logically depend on each other, but not overlap in any way: Consider the case where you add a log collector and log file processor in separate commits. The log file processor might be completely useless without the log collector, but the commits could appear completely independent if you just examine the patches.
git cherry-pick is yet another great/unknown feature of git. We use it in libguestfs to cherry pick the best bug fixes from the development branch into the stable branch. The whole process is quite effortless.
First I will list out all the main branch [ie. devel/unstable] commits which have happened since the last stable release, and I’ll look at each commit. The only difficulty is evaluating each commit to see whether it meets our criteria for a sufficiently safe change that users of our stable branch will want.
Secondly, I have our stable branch checked out in my working directory, and “git pull” to make sure that is up to date.
Then, for each commit I want to cherry pick, I simply do:
$ git cherry-pick -x sha1_of_the_commit
And usually that’s all I have to do. Git’s patch conflict resolution is much better than plain “patch”. It’s able to work minor miracles even where the code in the two branches has diverged quite a way. If it’s unable to apply the patch directly, then you’ll see a message like this:
Automatic cherry-pick failed. After resolving the conflicts, mark the corrected paths with 'git add <paths>' or 'git rm <paths>' and commit the result. When commiting, use the option '-c 94e310d' to retain authorship and message.
This is fairly self-explanatory. Use “git status” to see which files are problematic:
$ git status # On branch stable-1.2 # Your branch is ahead of 'origin/stable-1.2' by 15 commits. # # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: tools/virt-cat # modified: tools/virt-edit # modified: tools/virt-ls # modified: tools/virt-tar # modified: tools/virt-win-reg # # Unmerged paths: # (use "git reset HEAD <file>..." to unstage) # (use "git add <file>..." to mark resolution) # # both modified: perl/lib/Sys/Guestfs/Lib.pm
Edit the file to manually resolve the conflict, add the file, and then commit with the “-c” option noted in the original message.
The result is a series of stable commits like this, and hopefully some happy users.