Cherry-Picking With Git, For Fun And For Profit

(This is more as a reminder to myself in the future, rather than a full-fledged posting.)

As I learn more about Git and how we use it in Fedora packaging, I keep discovering cool new things about it that I love. One particular aspect that I’m quickly growing to adore is the feature to cherry-pick between commits in related branches. Suppose I’m currently working on the master branch of a package which is kept in sync with f16 and f17 branches.

Now, a new version comes up or I make a patch to fix a bug. After committing the changes to master, I can easily copy those commits to the release branches with a simple cherry-pick operation:

$ fedpkg switch-branch f17
$ git cherry-pick master

This copies the most recent commit from the master branch into the current one. (See the Git documentation for more information on specifying commit IDs.) Compare this to CVS, where we had to manually diff the devel/ branch directory and apply that to each branch by hand…it’s so much nicer with Git. 🙂

Then it’s a simple matter to push the changes to the Fedora repository and run builds for them through Koji, by running fedpkg push and fedpkg build in each branch. Sweet!

Also, the fullscreen mode of WordPress’ new post editor is fantastic – I owe someone a beverage for that.

Now to get some sleep, so I can finish this Algorithm Analysis project tomorrow. Ta for now!

But if an annoying person has polluted the package with cherry-picking, one way to fix it is to first merge master into fn, then fn back into master (which will be a fast-forward up to the merge commit), and now the branches are fast-forwardable again (just with a slightly messed history).

Oh, also effective, if the branches have diverged:
1. Revert all the commits on fn since the last time master was merged into fn (or the branch point if there was no merge), in reverse committing order.
2. If the content is now identical to the content of the corresponding master commit, just skip to step 4.
3. Otherwise, revert the merge, but lie to git revert, telling it that the merge was actually the opposite direction of how it actually happened, i.e. tell it that the parent commit to use is the master branch commit, not the fn branch commit which was the actual parent. Reverting the merge that way will tell git that you want to discard all the changes from the fn branch version and keep the master one instead.
4. Now merge master into the branch.
5. If you want to restore fast-forwardability, merge the branch back into master (see above).

Another important thing to know is that git can usually do a trivial merge if the contents are the same, even if the history is different. So if someone committed the same changes with different commits, you should still be able to merge. If there are now other changes in master on top of it, merge the commit which had identical contents as the branch first (using the commit hash rather than master as the git merge argument), then merge master. (That trick was extremely useful after the conversion from CVS. These days, it’s seldom needed, but still sometimes, e.g. for automated rel-eng bumps.)