Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Git merge conflict - What does it mean to "change the same part of the same file differently"?

Chapter 3.2 of the Git book states:

…Occasionally, this process doesn’t go smoothly. If you changed the same part of the same file differently in the two branches you’re merging, Git won’t be able to merge them cleanly.…

How should the ambiguous "change the same part of the same file differently" expression be interpreted? Does Git internally perform a line by line comparison in the conflicted file across branches?


Example:

Suppose on a branch L I add an extra newline at the beggining of README.md. Would that mean that a modification on any line different than the first to README.md on other branch R would trigger a conflict upon a merge?

You would expect a naive line by line comparison to fail since all lines have been shifted a position in L and some fraction of lines in R remain unshifted.

like image 692
Simón Ramírez Amaya Avatar asked Apr 10 '19 21:04

Simón Ramírez Amaya


1 Answers

Remember that Git is doing not one but two diffs:

  • there is a merge base commit B with some files,
  • and a left-side L commit with some files,
  • and a right-side R commit with some files.

The merge base B is the same in both git diff commands:

git diff --find-renames <hash-of-B> <hash-of-L>    # what we changed
git diff --find-renames <hash-of-B> <hash-of-R>    # what they changed

So, suppose we and they both modified file F.ext. It's clear from the first diff which lines of F.ext we changed: the ones listed as deleted in the diff hunk regarding B, plus one more "in between" line at the edge for safety—the edge goes forwards, so if we replaced original line 3, we "touched" lines 3 and "3-and-a-half". If we didn't delete any lines—if we inserted a line after line 3 before line 4—then we touched line "3-and-a-half".

Meanwhile, it's also clear from the second diff which lines of F.ext they changed. The same rules apply: if they replaced original line 3 with a replacement line 3, then they touched line 3 (which includes line "3.5"). So if we touched line 3, or line 3.5, we have a conflict. Otherwise, there is no conflict: Git can just take the change from whichever side made a change to the line(s) in question.

(Note that git diff presents the diff as a unified context diff. The merge code uses the raw, non-unified diff. It therefore has a list that says "at line X of original, delete D lines and insert I new replacement lines", where at most one of D or I can be zero. The touched span is line X through line X + D + 0.5, more or less. With my weird "plus a half", I am really just waving hands vigorously here to cover up the empty-span problem, which is a problem. You'll have to experiment with Git to see exactly what it does in every case.)

like image 118
torek Avatar answered Oct 01 '22 17:10

torek