Skip to content

Instantly share code, notes, and snippets.

@mlogan
Created September 4, 2014 17:12
Show Gist options
  • Save mlogan/b90e5a6024e395f3c008 to your computer and use it in GitHub Desktop.
Save mlogan/b90e5a6024e395f3c008 to your computer and use it in GitHub Desktop.
An interactive rebasing technique

Here's a cool rebasing technique that can save some time. I don't recommend using this if you aren't familiar with using rebase, there's probably a lot of ways to shoot yourself in the foot here.

Say you have a big pull request, with a bunch of commits, that look like: (oldest first, like in interactive rebase view, not git log --oneline view)

c129b43 Implementation of FooClass
3ffa239 Tests for FooClass:fizzle()
b3512ab Use FooClass::fizzle() to fizzle the frobnulator.
32941e2 Refactor the frobnulator to reduce number of fizzle() calls.

You send it out for review, and your reviewer points out that FooClass isn't technically fizzling, it's actually fozzling, and therefore requests that you rename FooClass::fizzle() to FooClass::fozzle()

So, you do the search and replace, and now your commits look like this:

c129b43 Implementation of FooClass
3ffa239 Tests for FooClass:fizzle()
b3512ab Use FooClass::fizzle() to fizzle the frobnulator.
32941e2 Refactor the frobnulator to reduce number of fizzle() calls.
6b623af Rename FooClass::fizzle() to FooClass::fozzle()

Being a compulsive rebaser, you don't want to keep 6b623af around, so you open up an interactive rebase session to squash it into c21c8e4:

$ git rebase -i origin/master

pick c129b43 Implementation of FooClass
f 6b623af Rename FooClass::fizzle() to FooClass::fozzle()
pick 3ffa239 Tests for FooClass:fizzle()
pick b3512ab Use FooClass::fizzle() to fizzle the frobnulator.
pick 32941e2 Refactor the frobnulator to reduce number of fizzle() calls.

But wait - that's not going to work, because 6b623af actually has some edits that you want to apply to the next three commits as well. So the rebase shown above is going to have conflicts. You could split 6b623af into little pieces that can each be squashed into the appropriate commit, but that's confusing and laborious.

There's an easier way! git rebase --interactive will quite happily process the same commit multiple times, so instead of the above rebase, do this:

pick c129b43 Implementation of FooClass
f 6b623af Rename FooClass::fizzle() to FooClass::fozzle()
pick 3ffa239 Tests for FooClass:fizzle()
f 6b623af Rename FooClass::fizzle() to FooClass::fozzle()
pick b3512ab Use FooClass::fizzle() to fizzle the frobnulator.
f 6b623af Rename FooClass::fizzle() to FooClass::fozzle()
pick 32941e2 Refactor the frobnulator to reduce number of fizzle() calls.
f 6b623af Rename FooClass::fizzle() to FooClass::fozzle()
pick 6b623af Rename FooClass::fizzle() to FooClass::fozzle()

We've simply pasted the commit as a fixup after every commit that we want to amend, and then left the original pick line at the end.

When you run the rebase, you'll get conflicts at each point, but that's ok, just revert any conflicting changes and then do git rebase --continue. The changes you reverted will be picked up later on when there are no conflicts!

The final pick line is a safety check - if you've done everything right, it will produce an empty commit, which rebase will complain about, and then you can run git rebase --skip, and you're done!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment