Steve Bennett
Steve Bennett

Reputation: 126637

How to rebase after a merge?

We have a sequence of events like this:

  1. Create branch A, and add a few commits to it
  2. Time passes, hundreds of commits added to master
  3. Master is merged into A
  4. Time passes, maybe another 50 commits added to master

Is it possible to turn the merge in step 3 into a rebase? If nothing else, it would make the impending merge simpler as there would be less history to look at.

We haven't had rerere enabled.

Upvotes: 16

Views: 7894

Answers (1)

DanielSank
DanielSank

Reputation: 3442

Consider the following Git graph:

A...C...D...F
^       ^   ^ master
 \       \
  G...H...I...J
              ^ feature

The ... means "lots of commits".

Suppose we want to essentially rebase, i.e. take the commits in feature's history but not in master's history and reform them as a linear sequence of commits in master. This confounded by the fact that we merged master into feature at commit I. If we try to rebase, Git screws up because it tries to apply e.g. C on top of master and finds conflicts.


The best method is from this answer, copied below for convenience:

git checkout feature
git branch -m feature-old
git checkout main
git checkout -b feature
git merge --squash feature-old
git commit

We can solve this problem by grabbing the actual changes from feature and packing them into a new commit. Here's a way to do this using only very basic Git commands:

(1) git checkout feature
(2) git merge master
(3) git reset A
(4) git add -A  # This stages all working copy changes
(5) git commit -m "Every change between A and J"

In step (2), the feature branch has all changes in both master and J. After step (3), HEAD points at A, but our working copy has all the changes from master and J, and steps (4) and (5) stage and commit those changes.

At this point our graph looks like this

A...C...D...F
^           ^ master
 \
  J'
  ^ feature

Note that J' contains everything in A...F. Now we do

git rebase master

Git happily applies the changes in J' as a new commit J'', but the only changes to add are those in G...J because the other changes are already in master. So now we have

  A...F<--J''
master^   ^feature

with all of the changes in our feature branch squashed into commit J''. At this point, you can reset to F and re-apply changes in J'' in a more granular fashion (even using git add --patch) if you want.


Another way to do essentially the same thing is with read-tree as explained in this other answer

git checkout master
git read-tree -u -m feature

Yet another way to do the same thing, stolen from this answer, is

git diff master > feature.patch
git checkout master
patch -p1 < feature.patch

Upvotes: 24

Related Questions