3

Explain the meaning of the Git merge markers below with reference to the code segments 1, 2 and 3.

/* Code from beginning of file */

<<<<<<< HEAD
      /* code segment 1 */
||||||| merged common ancestors
      /* code segment 2 */
=======
      /* code segment 3 */
>>>>>>> master

/* code to end of file */

This question is intended to elicit a simple explanation without reference to the convoluting factors found in other questions.

authentictech
  • 422
  • 6
  • 23
  • I recognize the simple and easily-answered nature of this question but I think an answer to it should be on StackOverflow and I could not find one. – authentictech May 15 '19 at 13:45
  • Possibly a dup but isn't there a case for a more simple question/answer than that one? There is little chance a beginner at Git would follow [this](https://stackoverflow.com/questions/16990657/git-merge-diff3-style-need-explanation). – authentictech May 15 '19 at 13:56
  • https://stackoverflow.com/questions/10657315/git-merge-left-head-marks-in-my-files – phd May 15 '19 at 14:05
  • @phd That is a "what to do in this situation?" question whereas mine is a "what do these mean?" question. – authentictech May 15 '19 at 14:12

2 Answers2

8

These chevron-ish markers (<<<<<<< and the like) are conflict markers. This particular set, which includes the ||||||| markers, is from the diff3 conflict style. The default merge conflict style omits the middle section.

The sections in the first part, <<<<<<< through |||||||, come from the current commit. The sections in the second part, ||||||| through =======, come from the merge base (which we'll define in a moment). The sections in the third part, ======= through >>>>>>>, come from the other commit.

Longer but more useful

To understand what these mean, remember that any git merge operation has not two but three inputs. One of the three inputs is your current checkout, aka the HEAD or @ commit. Normally, this is the newest commit in the branch you have checked out. The second input is based on the git merge command that you ran.1 For instance, if you ran:

git merge theirbranch

then the second input is the commit at the tip of branch theirbranch; if you ran:

git merge origin/master

then the second input is the commit to which your origin/master points. Either way, both of these are commits—snapshots full of files, where the files in your HEAD commit are the same as, or different from, the files in their commit. The differences between your files and their files are not directly relevant though: the key commit is a third commit, called the merge base.

Git finds the merge base commit for you automatically. The merge base is, in effect, the best shared commit that comes before the other two. Remember that the goal of a merge is to combine work, and in order to do so, Git needs to find out what you changed and what they changed. But each commit is a snapshot, not a set of changes—so Git has to work backwards from your commit and their commit, to find a commit that both of you shared when you started. That's the merge base.

Having found the merge base, Git now performs two diffs. One compares the merge base to your current commit: that's what you changed. The second diff compares the merge base to their commit: that's what they changed. Then Git combines the two sets of changes. The combined changes get applied to the merge base, to give the final merge result.

When you change a file and they don't touch the file at all, combining your changes with their nothing means the result is your changes. Applying those to the base file produces your file. Likewise, when they change a file and you don't, combining your nothing with their changes means the result is their changes, and applying those to the base file produces their file. So these are very easy.

The hard part occurs when you and they both changed the same file. Now Git really does have to combine different changes. If your changes are to lines that they didn't touch, and their changes are to lines that you didn't touch, Git can combine those: it just takes both changes. If you changed some line and they made the exact same change, Git can combine that too: it just takes one copy of the change. The really hard part, which results in a merge conflict, occurs when you changed some lines, and they changed the same lines, in a different way.

For that case, Git writes the conflicted changes into the work-tree copy of the file, surrounded by these conflict markers. The part above the conflict marker region has been successfully combined—or at least, Git thinks it has—and the same applies to the part below the marker region. The part in between is where Git could not decide whether to prefer your change, in the first segment, or theirs, in the last. The middle part, shown only if you choose diff3 style, is what the original lines looked like.


1Note that if you ran git pull to get to this point, git pull ran git merge for you. So you may not have run git merge directly, but you did invoke git merge.

The cherry-pick and revert commands use the Git merge machinery too. So do git stash and some cases of git apply or git am. So you can see these merge conflicts for these commands as well. The definition of merge base for these operations is different, though, so it becomes harder to see how you got the conflict.

Another side effect of diff3 vs merge

When there is a conflict, if you have the diff3 style selected, Git has to show you the base version—the entire section where the conflict occurs. But when you have the merge style selected, Git can omit the base version, and show just the --ours and --theirs versions. This means that if it can partially combine the conflicted region, it does so, leaving only the uncombined region surrounded by markers. For instance:

<<<<<<< HEAD
please fix a spelling error
and ok, I changed this
||||||| merged common ancestors
please fix a speeling error
and change this
=======
please fix a spelling error
and change this to something different
>>>>>>> theirs

Here, we and they fixed the spelling error the same way (replacing speeling with spelling), but we changed the second line differently. With the diff3 style, you see the base version and both end-point versions.

If we choose the merge style, Git sees that we and they fixed the spelling error the same way, and we see instead:

please fix a spelling error
<<<<<<< HEAD
and ok, I changed this
=======
and change this to something different
>>>>>>> theirs

Often, this is kind of nice—but sometimes it means you can't tell at all what the original base was, because the conflict is between something and nothing, i.e., one of us or them deleted a line when the other of us or them changed it. I find the diff3 style far easier to read in general, but the merge style is the default.

torek
  • 448,244
  • 59
  • 642
  • 775
1

The sort of beginner-level answer I was looking for was something like this:

Segment 1 is the code that exists in your currently checked out branch - which is at the "HEAD" position as noted by the <<<<<<< - that currently conflicts with segment 3.

Segment 3 is the code that exists in the other branch you are trying to merge with - the "master" branch as noted by the >>>>>>> - that conflicts with segment 1.

Segment 2 is the code that existed in the common ancestor of both to-be-merged branches. It is displayed by Git to help you more easily decide how to resolve the merge between segments 1 and 3.

There are several ways you could decide to resolve the merge conflict between segments 1 and 3. Possible ways of doing this are:

  1. delete segment 1 and keep segment 3;

  2. delete segment 3 and keep segment 1;

  3. manually merge segments 1 and 3 to include changes in both.

  4. revert back to segment 2 from the common ancestor, discarding segments 1 and 3;

  5. delete all segments and write completely new code

Ultimately, you need to decide what to do to resolve the conflict using your own judgement. Just be sure to remove all the merge markers from the code before committing the merge.

authentictech
  • 422
  • 6
  • 23
  • I answered my own question in a manner that makes sense to me as a Git beginner as I find many answers on the Git topic to be geared towards, or contain details that would only be familiar to, a more intermediate knowledge level. Of course, there's a chance I have said something wrong. If I have, please comment here and I will correct/update the answer. – authentictech Aug 10 '19 at 12:25