Alexander Mills
Alexander Mills

Reputation: 100496

merge commits that occurred before doing a rebase

Say I am on a feature branch, and I do:

git fetch origin 
git merge origin/dev

I do that a few times, then a week later I do:

git fetch origin
git rebase origin/dev

what happens to the merge commits I created earlier? Do they get thrown away? I am wondering if rebase can coexist with merge after the fact.

(Note that origin/dev is the integration branch in this case).

Upvotes: 0

Views: 46

Answers (1)

torek
torek

Reputation: 490168

Without -p or --preserve-merges, or the newfangled --rebase-merges option, git rebase simply discards merges.

I say "simply" here, and the "discard a merge commit" part is simple, but the overall process is a little complicated. The key is to first read and understand the web site Think Like (a) Git, and sit down and work through a bunch of examples of reachability. Draw some graphs, e.g.:

       G-----H         R   <-- tip1
      /       \       /
...--F---L-----M--N--O--S   <-- tip2
      \               \
       J---K-----------P   <-- tip3

(I left out Q as it looks too much like O. I left out I by accident, oops. :-) ) Which commits are reachable from M, or P, or K? When you can look at this or some other graph and quickly enumerate the reachable commits, you are ready for the next step.

What git rebase does is to enumerate those commits that are reachable from the HEAD commit, excluding commits reachable from the other commit that you name. That is, given git rebase origin/dev, Git finds all the commits that are reachable from origin/dev. These commits are excluded from the rebase, no matter what else might happen.

Then, with the full list of "cannot possibly include these commits", Git makes a list of maybe-included commits. These are the HEAD commit and all commits reachable from the HEAD commit. So if we attach HEAD to the name tip1, for instance, the maybe-included commits are R (to which tip1 points), O (reachable from R), N (reachable from O), M (reachable from N), H and L (both reachable from M), G (reachable from H), F (reachable from both L and G), and any commits before F.

Now that rebase has a list of maybe include and definitely exclude, Git tosses all the definitely exclude commits out of the list. The result is a smaller list "maybe include"s. At this point, a default rebase also throws out all merge commits: commits with at least two parents.

Whatever is left, these are commits with one parent, so Git can run git cherry-pick on them. Git checks out—as a detached HEAD—the --onto commit, or the target commit (origin/dev) if you did not specify --onto, and cherry-picks each commit from that list, one commit at a time. That builds the new chain. Once the chain is built, Git moves the old branch name to the new HEAD commit, and the rebase is complete.

Upvotes: 1

Related Questions