Computing and the Command Line

Merging and Conflicts

git merge brings another branch's work into the one you're on. If your branch hasn't moved, it's a fast-forward: the pointer just slides ahead. If both have moved, git makes a three-way merge commit from the two tips and their common ancestor. When both changed the same lines, you get a conflict: read the markers, decide what the file should say, add, and commit, or back out with git merge --abort.

  • 5 min
  • 7 steps
  • 2 questions
  • Lesson 32 of 80

In this lesson

  1. Merging
  2. Fast-forward
  3. Three-way merge
  4. Conflicts
  5. Keeping conflicts small
  6. Your turn
  7. So

Merging

When work on a branch is done, you merge it into another branch, usually main. Switch to the branch that should receive the work, then name the branch to bring in 1:

me@linuxbox:~/garden$ git switch main
me@linuxbox:~/garden$ git merge fence

What git does next depends on whether both branches have moved.

Fast-forward

If main hasn’t changed since notes split off from it, main is simply behind. Git doesn’t need to combine anything; it moves main forward to where notes is. That’s a fast-forward 1:

me@linuxbox:~/garden$ git merge notes
Updating d2de332..2776660
Fast-forward
 notes.txt | 1 +
 1 file changed, 1 insertion(+)

No new commit is made.

Left, a fast-forward: main at commit d2de and notes one commit ahead at 2776; git merge notes slides main forward with no new commit, printing Fast-forward. Middle, a three-way merge: 043d is the common ancestor of e86b on main and d595 on fence; git merge fence makes a new merge commit d2de with two parents, and main moves to it. Right, fence.txt in conflict: between <<<<<<< HEAD and ======= is cedar posts, 10 ft apart, your branch; between ======= and >>>>>>> spacing is cedar posts, 6 ft apart, the other branch. Edit, delete the markers, then git add fence.txt and git commit, or git merge --abort. Bottom, the real output: the merge graph, and git merge spacing printing CONFLICT (content): Merge conflict in fence.txt, with git status listing both modified: fence.txt.
Fast-forward, merge commit, or a conflict for you to settle. Credit: StudyCorner diagram · CC BY 4.0 · Source

Quick check

When does git merge make a fast-forward instead of a merge commit?

Three-way merge

If both branches have new commits, git combines them. It finds their common ancestor, the last commit they share, and compares each branch’s tip with it: changes made on only one side are kept, changes on both sides are combined. The result is a new merge commit with two parents 1:

me@linuxbox:~/garden$ git merge --no-edit fence
Merge made by the 'ort' strategy.
 fence.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 fence.txt
me@linuxbox:~/garden$ git log --oneline --graph
*   d2de332 Merge branch 'fence'
|\
| * d595886 Plan the fence
* | e86b128 Add lettuce
|/
* 043dfca Plan the beds

Without --no-edit, git opens your editor with the message “Merge branch ‘fence’” for you to accept or expand. (‘ort’ is the name of git’s current merge algorithm.)

Most merges are like this: changes in different files, or different parts of the same file, combine on their own.

Conflicts

If both branches changed the same lines differently, git can’t know which you want. It stops with a conflict 1:

me@linuxbox:~/garden$ git merge spacing
Auto-merging fence.txt
CONFLICT (content): Merge conflict in fence.txt
Automatic merge failed; fix conflicts and then commit the result.
me@linuxbox:~/garden$ git status
On branch main
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
    both modified:   fence.txt

Git has written both versions into the file, between conflict markers 1:

<<<<<<< HEAD
cedar posts, 10 ft apart
=======
cedar posts, 6 ft apart
>>>>>>> spacing
  • Between <<<<<<< HEAD and =======: your version, from the branch you’re on.
  • Between ======= and >>>>>>> spacing: the other branch’s version.

To resolve it 1:

  1. Edit the file to say what it should: one version, the other, or a mix. Delete all three marker lines.
  2. git add fence.txt to mark it resolved.
  3. git commit to finish the merge (the message is filled in for you).
me@linuxbox:~/garden$ nano fence.txt                 # leave: cedar posts, 8 ft apart
me@linuxbox:~/garden$ git add fence.txt
me@linuxbox:~/garden$ git commit --no-edit

Changed your mind partway through? git merge --abort puts everything back as it was before the merge 1.

Editors like VS Code show conflicts with buttons for “accept current,” “accept incoming,” or both, which does the same edit for you. However you resolve it, search the file for <<<<<<< before committing: a leftover marker is an easy mistake to commit.

Quick check

In a conflicted file, what’s between <<<<<<< HEAD and =======?

Keeping conflicts small

  • Merge often, so branches don’t drift far apart.
  • Keep branches focused on one thing.
  • Pull in main’s changes partway through a long branch (git merge main while on the branch), so conflicts come in small pieces.

Your turn

Exercises

  1. Make a branch, commit a new file on it, switch back, and merge it. Was it a fast-forward? Why?
  2. Make two branches from the same commit that each add a different new file. Merge both into main. Look at git log --oneline --graph.
  3. Create a conflict on purpose: two branches each change the same line of beds.txt. Merge, read the markers, resolve it by keeping both lines, and commit.
  4. Start another conflicting merge and back out of it with git merge --abort. Check git status.
Answers
  1. Yes, if main had no new commits since the branch split off; git just moved main forward.
  2. The first merge is a fast-forward; the second makes a merge commit, because main moved (it now has the first branch’s file) since the second branch split off.
  3. Edit the file so both lines remain and no marker lines are left, then git add beds.txt and git commit. git log --oneline --graph shows the merge commit with two parents.
  4. git status reports a clean working tree on main, as if the merge never started.

So

Switch to the receiving branch and git merge the other. If yours hasn’t moved, it’s a fast-forward; if both have, git makes a merge commit with two parents from their common ancestor. Conflicts show both versions between <<<<<<<, =======, and >>>>>>>: edit to what it should be, git add, git commit, or git merge --abort.

Lesson complete

Nice work.

1day streak
0/1today's goal
–correct

Up next · 5 min

Rebasing

Next lesson
Sources for this lesson
  1. 1
    Scott Chacon, Ben Straub. Pro Git, 2nd edition. Apress; free online at git-scm.com. 2014. verifiedFree CC BY-NC-SA 3.0 book, maintained online. Ch. 1: version control; Git's 2005 origin when the Linux kernel lost free use of BitKeeper; snapshots, not differences (unchanged files stored once); nearly every operation local; integrity through 40-character SHA-1 checksums; the three states (modified, staged, committed) and three areas (working tree, staging area or index, .git directory); first-time setup with system/global/local config levels, user.name and user.email baked into commits, core.editor, git config --list --show-origin. Ch. 2: git init, status (and -s), add, diff and diff --staged, commit (-m, -a), .gitignore, log options, amending, undoing, remotes, tags, aliases. Ch. 3: branches as movable pointers, HEAD, merging and conflicts, remote branches, rebasing and its rule. Ch. 7: reset demystified, stashing, revision selection. Ch. 8: core.autocrlf true on Windows, input on Linux and macOS. Ch. 10: objects (blob, tree, commit) and references.