Eki Eqbal Eki Eqbal - 21 days ago 4
Git Question

Is there a fast way to rebase a long history of commits to master branch?

I've been working the past two weeks in a feature, and I created a separate branch for it. The problem that the task took me long and I didn't rebase to master periodically. I ended up with 100+ commits and now when I'm trying to rebase the branch to master using

git rebase master
is taking forever. As I need to check the conflict for each each commit, modify it,
git add
and then
git rebase --continue

Is there an easier way to do that?


This depends in part how you've done your 100+ commits and what's been happening in the mean time. Four strategies might be useful:

Reduce the number of commits to rebase

If you have a beautiful clear stream of 100 commits that never modify the same code twice, I'm sorry, but you are stuck.

However, from 100 commits in two weeks, I'm guessing your commit history does not like that. Perhaps you've modified function X 5 times within the commit history, and each one of those commits is going to cause a conflict. If only you had one single commit that modified function X, you'd have 20% of the work to do.

So the answer here is to tidy up your commit history before you rebase onto master. This is another use for 'git rebase'. Rebase it onto its existing base, but use git rebase -i to interactively squash together and reorder the commits. Make sure you have the minimum number of commits and that each of those does something independent. You can take the opportunity to change your commit logs too. Key backup branches (git branch) and git diff to those after each commit to check you've not changed anything.

In particular, if you have internal merge commits, flattening these is helpful.

Inevitably when I do this I find stuff I did wrong, or could have done better, even if it's 'only' code formatting or commit messages. You can take the opportunity to fix these. I'm guessing you should be down to 10 or 20 commits when this is done - far more manageable.

Rebase in stages

If you've already done that, you are probably going to have to bite the bullet. However, if a colleague has been refactoring whilst you've been working, sometimes what you are best doing is as follows. Imagine the tree looks a bit like this:

    A --- B --- C --- D --- E --- F -- .... -- X --- Y --- Z (master)
      \----- 1 --- 2 --- 3 --- 4 .... 97 --- 98 --- 99 --- 100 (you)

What you want to do ultimately is to rebase onto Z. However, this gives you loads of conflicts. Let's suppose commits D and E are two megacommits from a colleague refactoring something your code is dealing with. Sometimes, it's easier to:

  • Rebase onto C (this shouldn't be much work)
  • Take a deep breath and rebase onto E
  • Rebase onto master (this shouldn't be much work)

Avoiding complexity on the 'master' side

Sometimes things have got really complicated on the tree you are trying to merge into. A classic case is that (using the above diagram), commit G reverts commit C, then commit H redoes (or almost redoes) it, and in the mean time there are merges (particularly of things you've might have merged in too). Thanks colleague, that's made rebasing really simple. Other things that cause difficulties are complex merge commits and nasty renames. Each commit tends to give you the same conflicts again and again for no apparent reason. git rebase Aarrggh.

One technique here is to flatten a copy of the master tree, i.e. branch master (locally), then rebase it squashing the whole thing into a single commit from A to Z. You can flatten your stuff a little first too (see above). Then rebase onto the flattened master. Now you've got the 'right' set of commits, you can easily rebase that onto master (well, onto 'Z' as master may change) because the code's exactly the same.

If all else fails

Sometimes git rebase seems to go into a fugue state of conflicts, and it's time to break out git format-patch and git am to reapply it in whole or in bits. This is really useful if someone has renamed a file (as you can fix the filename in the patch in your editor) or renamed a common class/variable/whatever (as you can find/replace in the patch).

Learn from your mistakes

Next time, rebase onto master more frequently. It's less painful if you do it as you go along.

Also, if you have two people working on the same area of code and it produces a pile of conflicts, perhaps your code's not well abstracted enough, or perhaps you are working on top of each other and could divide up your work better?