Reputation: 2466
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.
Upvotes: 6
Views: 2364
Reputation: 7035
What torek said is right, but I just want to emphasize the basics a little more, since that seems to be where you're struggling.
When you merge two branches, each branch represents one history of effort or changes. For example, imagine that you and your co-worker, Bob are both working on the same project, from different computers, at the same time.
When you started, you had exactly the same version that Bob had. But since you're working independently, it's easy to see that you and Bob might both modify the README file at the same time. Perhaps you notice that there's a word misspelled in the first sentence and correct it. Meanwhile, Bob decides that the introduction is confusing, so he rewrites the first two paragraphs. In Bob's version, the misspelling is still there, but it's now on a different line.
Now, there is a conflict between your version and Bob's version of the README. When you and Bob decide to merge your changes together, that conflict will be detected by git merge
, and it will complain to you. You don't want the merge to throw away your changes, since Bob did not notice the spelling mistake. But, you don't want to throw away Bob's work either. Therefore, the decision to combine the changes from your README and Bob's README must be done by a human.
Now, in this case, you and Bob changed the same line of the README. But, what if you had touched different lines? As a precaution, git merge
will still complain if your changed lines are close enough to Bob's changed lines, because there could still be a conflict.
But, git merge
does not (and cannot) detect every possible conflict. Sometimes, the result of merging will not make sense, even though git merge
did not complain. For example, if at the same time that you fix the spelling mistake, you also add a sentence at the end of the README which refers to something mentioned in the first paragraph, your changes might not make sense together with Bob's. However, git can't tell that the last line and the first paragraph are related, because it cannot guess the meaning or semantics of the file it's merging. The same is true of code files.
Upvotes: 4
Reputation: 488103
Remember that Git is doing not one but two diffs:
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.)
Upvotes: 3