2

I have hit a case in which merge silently ignores some changes from the merged branch, which I would expect (rebase would take them).

Here is the case:

*    merge slave into master: line not back
|\
| *  oops, was bad, put the line back (slave branch)
| |
| *  slave branch, also delete the same line
* |  master branch, delete a line
|/
*  initial

When merging slave into master, the line remains removed, so the commit that put it back was not merged. A rebase would.

If the line was not removed but modified, this would be a conflict, which would be fine for me. A rebase would accept the line back, fine too. But skipping the commit with no warning at all... I would like to know for which cases it was designed so.

NB: Searching the web for similar questions found many... but all concerning whole files that were removed before being merged. I can admit reasons for this, even if I would prefer a conflict (rather than losing a commit). However, for changes inside a file, I find this more disturbing, and found no mention.

NB: I checked it on Mercurial, same result, this is not specific to Git.

Shadok
  • 144
  • 5
  • This is another interesting case for [how rebase can differ from merge](https://stackoverflow.com/q/44546974/1256452). I added a link to this question to my answer there. Thanks! :-) – torek Jun 30 '17 at 16:54

1 Answers1

3

The reason is that a (file-level) merge doesn't see the path of changes; it sees three versions of the file: base, ours, and theirs.

In the case where the branch removed a line and then put it back, theirs and base are identical (at least on the line in question). The fact that, somewhere in the history of theirs, the lines would not have been identical is not known to the merge tool.

So for theirs the line is not part of a changed hunk; but for ours it is, so the change in ours is accepted.

I'll agree that it's not what you'd want from this particular merge, but I can't see any reasonably efficient way to do better. Also, in 18+ years of programming I've never run into this scenario - so "disturbing" might be a little strong.

(I'd additionally note, your characterization of this as "losing a commit" is not really accurate. The commit was included in the merge result, just in a counterproductive way. The bottom line is, automatic merge algorithms can never be perfect; but the ones we use are really good most of the time.)

I suppose if you really want to memorialize the fact that the branch considered what to do with the line and decided to keep it, you could make some "meaningless" change to the restored line. Then, as you note for the case of a changed line, it will come up as a conflict.

But really, you run unit tests, don't you? If this line is important enough to have been removed in two change lines and re-added in one of them, surely you have a test case that will fail if it's handled wrongly? And you know to test your merge results, right?

Point being, it's not good behavior for the tool... but it's a rare bad behavior that should be caught and corrected in a mature development process anyway. I wouldn't read too much into it.

Mark Adelsberger
  • 42,148
  • 4
  • 35
  • 52
  • Thanks! I should have thought of that (the first line of the answer, which explains all). I spontaneously thought that a merge was computed like a rebase, only organised differently afterwards. I would like that, because it seems to me the only way to lose no data. – Shadok Jul 03 '17 at 08:10
  • Well, there are pros and cons to applying patches one at a time ("computed like a rebase, only organized differently afterwards"); but if it's what you want, with a little work you could make it happen. (Write a script to: create a temp branch; rebase that; check out "ours"; start a merge of "theirs" with `--no-commit`; update the index from the (rebased) temp branch; finish the merge; delete the temp branch). But by definition you'd be creating an "evil merge" which may cause trouble later. And like I said, the "cons" of a regular merge rarely matter... – Mark Adelsberger Jul 03 '17 at 13:06
  • You're right about the evil merge. Actually, I think the best would be to merge normally, but to notice the potential problem (using a simulated rebase and detecting differences?) and report it. If it's not technically a conflict, it's something worth detecting. – Shadok Jul 04 '17 at 09:58