git rebase on master gives conflict but "plain" merge to master does NOT

Viewed 99

When I rebase my branch my-feature on master I get a conflict:

[my-feature]$ git rebase master
Applying: ...
Using index info to reconstruct a base tree...
M   vars/file.txt
Falling back to patching base and 3-way merge...
Auto-merging vars/file.txt
CONFLICT (content): Merge conflict in vars/file.txt
error: Failed to merge in the changes.

If I instead do a plain merge of my-feature into master it works fine:

[master] $ git merge my-feature
Merge made by the 'recursive' strategy.
..

I have tried to do a fresh clone of my repo to make sure everything is in sync with the remote and in a clean state.

How is it possible that a rebase fails when a plain merge does not?

Been using git for some years now an never seen this before and always assumed that if a merge goes well (no conflicts) its guaranteed that a rebase will as well and vice versa.

2 Answers

There are multiple causes for this. It usually happens when there's a change made in the middle that creates a conflict (when rebasing) but that a later revision takes back. Merge won't have to deal with the conflicting change because it doesn't care about the middle revisions.... just the tips (and the last common ancestor, roughly) to do the merge.

This is from an edit and I am not 100% sure I agree with it but alas :-): Another reason this can happen is if your my-feature branch contains new merge commits in it. If those merge commits required conflict resolution, then they may need to be resolved again.

How is it possible that a rebase fails when a plain merge does not?

It doesn't "fail". It has a conflict. There is a big difference. A conflict is a form of success — a form of success that requires you to exercise some judgment. That's all it is.

As for how it can be possible to have a conflict during a rebase when you do not have a conflict during a merge, it's because the relative histories of the branches and the way they are being applied are different. If they were the same, a rebase would be a merge!

So, for instance, here's a possible scenario:

A -- B -- C -- D (master)
     \
      X -- Y (feature)

If you merge feature into master, the starting point is B because that is where the histories diverged. Git then takes the whole history B-C-D and the whole history B-X-Y and performs both of them on B. It expresses the totality of the result as a new commit, a merge commit, and it makes D and Y the parents of that commit.

If you rebase feature onto master, the starting point is B (again) but for a different reason: it is because that is the parent of the first commit (X) on feature that is not in common. Git then completely ignores the history B-C-D; instead, it does directly to D whatever was done to get from B to X and makes a commit out of that — and then does to the resulting commit whatever was done to get from X to Y and makes a commit out of that. It then moves the name feature to point to that last new commit.

Well, it may not be so easy to "do directly to D" what was done to get from B to X. That is why a rebase can be a source of a conflict when a true merge is not.

To put it another way: rebasing here means to pretend that X grew out of D. But X did not grow out of D; it grew out of B. Things may have happened in C and D that make it hard to grow X out of D instead B.

(To put it still another way: A merge summarizes history. A rebase rewrites history.)

Related