Branching, merging, and conflicts
What a merge actually combines, why conflicts happen, and how to resolve one so that both intentions survive.
A branch is a line of commits
Creating a branch makes a new pointer at the current commit. As you commit, that pointer moves and the original stays put. Two people working "on main" each have a local main that moves independently. Pushing and pulling reconcile them, and that reconciliation is a merge or a rebase.
How a merge decides
Merging branch B into A, Git finds the merge base, the nearest commit both share. It then compares base→A and base→B:
- Regions changed on only one side take that side's version automatically.
- Regions changed identically on both sides are fine.
- Regions changed differently on both sides are conflicts.
If A has no commits since the base, there is nothing to combine, so Git just moves A forward. That is a fast-forward. Otherwise it records a merge commit with two parents. Teams that want every feature visible as a unit use --no-ff to force a merge commit.
Reading a conflict
A conflicted file contains markers:
<<<<<<< HEAD
version: 2.4.1
rollback: deployctl rollback --to 2.4.0
=======
version: 2.4.0
rollback: deployctl undo
>>>>>>> fix-rollback-command
The markers show the two results but hide the base. Without it you cannot tell who changed what. Switch to the three-way view, git checkout --conflict=diff3 RELEASE.md or permanently git config merge.conflictStyle diff3 (zdiff3 on recent Git), and a third section shows the original. Now it is clear that one branch bumped the version and the other fixed the rollback command. The correct resolution keeps both changes. Picking a side throws away a colleague's fix and makes the release note wrong in a way no tool will flag.
The workflow:
git statuslists conflicted paths.- For each, read base, ours, and theirs, and decide what the combined truth is.
- Edit the file to that truth and remove all markers.
git addthe file, thengit commit(orgit merge --continue).- Build and test. Textually clean merges can be semantically broken.
If you lose the thread, git merge --abort returns you to where you started.
Rebase: replaying instead of joining
git rebase main takes your branch's commits and replays them one by one on top of main, which gives a linear history. Each replayed commit is a new commit with a new ID. Conflicts are resolved per replayed commit, followed by git rebase --continue.
Pro Git states the rule plainly: do not rebase commits that exist outside your repository and that others may have built on. Rebasing a private feature branch before review is tidy. Rebasing main after colleagues have pulled it forces everyone to untangle duplicated history. Shared branches move forward through merges or reverts, not rewrites. The “Releases, tags, and shared history” notes revisit this with force-pushes.
Keep branches short
Most painful conflicts come from long-lived branches that drift from main. Small branches merged often keep each merge base recent and each conflict small enough to understand. That is a delivery practice as much as a Git one, and the Continuous Delivery track returns to it as trunk-based development.
Key terms
- Merge base
- The most recent common ancestor of the two commits being merged. Each side's changes are measured from it.
- Fast-forward
- A merge where the target has no new commits, so Git just moves the branch pointer forward. No merge commit is created.
- Conflict
- Both sides changed the same region of a file relative to the base, so Git cannot choose automatically.
- Rebase
- Replaying commits on top of another base, which produces new commits with new IDs.
Read further
- Pro Git, 2nd edition, 3.2 "Basic Branching and Merging" and 3.3 "Branch Management" (Free to read online (CC BY-NC-SA 3.0))
The difference between a fast-forward and a merge commit, the conflict markers, andgit mergetool. Note whatgit branch --mergedtells you. - Pro Git, 2nd edition, 3.6 "Rebasing" (Free to read online (CC BY-NC-SA 3.0))
How rebase replays commits on a new base, and the section "The Perils of Rebasing" with its rule about commits that exist outside your repository. - Pro Git, 2nd edition, 7.8 "Advanced Merging" (Free to read online (CC BY-NC-SA 3.0))
Aborting a merge, ignoring whitespace, viewing the base version of a conflicted file, anddiff3conflict style. It turns guessing into reading.