out of time
-
Git Tip of the Day: git cherry-tree
The problem
Recently I came across a situation in which I needed to make some fairly major changes to our codebase, and those changes needed to apply to two different branches - both a version branch that’s currently in QA, and the master branch. The changes involved reverting a few commits as well as making new changes that were too big to comfortably fit into one commit. One option would be to simply make all of the commits in one branch, and then cherry-pick them by hand into the other, but that seemed awfully manual for such a powerful tool as git.
The Solution
Enter git cherry-tree, a little alias I came up with that basically creates a series of patches based on the commit diff between two branches, and pipes the result directly into a third branch. Before I explain how to use it, here’s how to add the alias to your config:
git config --global alias.cherry-tree \!sh\ -c\ \'git-format-patch\ --stdout\ \$0..\$1\ \|\ \git\ am\ -3\'How to use it
For the sake of this example, we’ll call our branches
qaandmaster. My goal is to branchqa, make a series of commits, and then apply the changes in those commits tomasteras well. The result should be equivalent to individually cherry-picking the commits that I’ve made, but easier and more reliable. Here we go:git checkout qa git checkout -b qa-big-changes # make and commit the changes git checkout master git checkout -b master-big-changes git cherry-tree qa qa-big-changesThat last command says, essentially, “Sequentially apply each commit that is in qa-big-changes, but not qa, to the current branch.” Unless you’re a naturally lucky person, you’ll probably have conflicts - when this happens, the process will stop midstream, telling you which files are in conflict. Let’s say config/environment.rb is in conflict:
# open config/environment.rb and fix the conflict git add config/environment.rb git am -3 --resolvedNote that, unlike with a normal merge conflict, you don’t want to commit after fixing the conflicts - just add the conflicted files to the index. The last command just says, “OK, problem solved, pick up where you left off.” Note also that, since each commit from your other branch is applied individually, this can happen more than once (of course, with different conflicts, in different commits).
Once the entire patch has run cleanly, master-big-changes will have a series of commits equivalent to the commits that you made to qa-big-changes (they won’t be the same commits, though - just like a cherry-pick). Then you can merge your changes into the respective branches:
git checkout qa git merge qa-big-changes git branch -d qa-big-changes git checkout master git merge master-big-changes git branch -d master-big-changesAs a final note, the use of master-big-changes isn’t strictly necessary - you can just do it directly in master. I just feel safer doing this process to a separate branch and then merging it in. However, making your original set of changes in qa-big-changes, rather than directly in qa, is required for this process.