I’ve been working on a project recently where we occasionally deal with some pretty gnarly rebases. I recently learned how to recover from them much more easily.
- The master branch runs in production
- The team contributes to a very active and long-lived feature branch.
- We branch off the feature branch and merge contributions back into it.
Anytime there’s an update to master, we have to rebase the feature branch against it. (We rebase on Wednesdays in order not to fall too far behind.) That means any personal working branch checked out before the rebase winds up with a ton of extraneous commits and merge conflicts when making a pull request on GitHub:
This is a screenshot of a real pull request. I’ve obscured my colleagues to protect the innocent.
Sometimes I’ve slogged through and reconciled the conflicts, but it took a long time and felt like a waste of effort. Other times I’ve just given up, deleted my local working branch, recreated the work on a fresh branch, and kept going.
A few weeks ago, my colleague Tiffany Tse taught me a technique that sidesteps this mess with
git cherry-pick. Note that this really only works if you pushed up your local working branch to remote (in my case, GitHub).
Here’s how it works:
- Check out the rebased feature branch and do a pull so it’s up to date and includes all the rebase changes:
$ git checkout feature/branch
Switched to branch 'feature/branch'
$ git pull
Already up to date.
- Delete your local working branch that’s been caught in the rebase:
$ git branch -D my-working-branch
Deleted branch my-working-branch (was 478fe9e).
- Checkout a new branch with the same name as the old one:
$ git checkout -b my-working-branch
Switched to a new branch 'my-working-branch'
- This is where the magic happens. On GitHub2 find the commit(s) you need to rescue from rebase hell. In this example, it’s this one. Use its hash to
git cherry-pickjust that commit for your fresh branch:
$ git cherry-pick 478fe9e68a71fd706149f95e0719cdd9bdc4197d
[my-working-branch 77d97fc] adding a feature
Date: Sat Jan 11 13:39:23 2020 -0500
1 file changed, 3 insertions(+)
- Finally, force-push the new branch to remote. Force-pushing wipes out all the rebased junk and re-applies the smaller batch of changes you were making:
$ git push origin +my-working-branch
+branchname shortcut was also new to me. It’s functionally equivalent to
In just a few days this has definitely saved me time and hassle, and it was something I’d never seen in many months of periodically searching for how to make this process less painful. Maybe it’ll save you some time too.
Try it out on a toy repo
To help understand the problem and this particular solution, I created a GitHub repo that recreates the problem:
masteris up to date.
feature/branchwas two commits ahead of master, but has been rebased against master.
my-working-branchis one commit ahead of the non-rebased feature branch. A pull request against the feature branch produces merge conflicts.
Feel free to clone the repo to sandbox the problem and test the
git cherry-pick approach to rebasing.
1. Some argue this whole scheme is a bad idea; I take the point but we deal with the world as it is.
2. ...or whatever service you use, of course. You could also comb through
git reflog if that’s your thing.