Git, CI/CD & GitOpsX · Merge ConflictsConflicts
When merges conflict — the three cases the algorithm cannot resolve
What you'll learn
- Identify the three cases that produce a merge conflict and the algorithm response to each
- Explain why a "same line modified by both sides" conflict is the most common case
- Distinguish a delete/modify conflict from a modify/delete conflict and explain the resolution asymmetry
- Recognise why binary file conflicts cannot be merged textually and require an all-or-nothing choice
- Predict from `git status` output which case applies to each conflicted path
Prerequisites
Practice
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
A merge conflict is the answer the three-way merge produces when
none of its automatic rules apply. The recursive strategy can
combine two trees if and only if every conflicted path falls into
one of the clean cases (unchanged-in-both, changed-in-one,
changed-identically-in-both). When a path falls into the
“changed-differently-in-both” category — by edit, by deletion, or
by binary content — the algorithm stops and asks a human. There
are exactly three cases that produce this stop. Knowing which
case applies from a single git status is what turns a
conflict from a stuck merge into a 30-second decision.
Case 1 — both sides modify the same lines
This is the case the previous lessons anticipated. Two branches both edited the same lines of the same file to different new values. The algorithm knows the base, the ours version, and the theirs version, and it can see that the lines differ from base in two different directions, so it cannot pick. It writes conflict markers into the working tree, stages all three blobs at the conflicted path, and prints a per-path report:
git status
# On branch feature/iam-rotation
# 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: terraform/iam/main.tf
git diff --name-only --diff-filter=U
# terraform/iam/main.tf
The phrase “both modified” in git status is the signature of
case 1. The fix is to open the file, read the conflict markers,
choose the combined content, remove the markers, and stage the
result with git add. Every conflict-resolver lesson in this
part assumes this case unless the status output says otherwise.
flowchart TB
A["Base file\nline: retention = 30"] --> B["Ours\nretention = 90"]
A --> C["Theirs\nretention = 7"]
B --> D["Conflict\nboth modified"]
C --> D
D --> E["Human chooses\nretention = 30 or 90 or 7\nor some new value"]
The lines outside the overlap merge automatically. Only the lines both sides touched are written into the conflict hunk. Most “both modified” conflicts in an infrastructure repository touch one or two hunks in a file with hundreds of lines; the rest of the file is already merged cleanly.
Case 2 — delete/modify and modify/delete
When one branch deletes a file and the other modifies it, the algorithm reports the conflict in a deliberately asymmetric way:
git status
# Unmerged paths:
# (use "git add <file>..." to mark resolution)
# deleted by them: ansible/inventory/staging.ini
# deleted by us: ansible/inventory/production.ini
git diff --name-only --diff-filter=U
# ansible/inventory/staging.ini
# ansible/inventory/production.ini
The two git status lines tell you which side deleted the file
and which side kept it. “Deleted by them” means the branch being
merged in no longer has the file, while the current branch still
has it. “Deleted by us” means the current branch removed it,
while the merged-in branch still has it. They are different
decisions: in one case, the file’s survival is a question; in
the other, the file’s contents are a question.
The repository treats this case as a per-path conflict: the
file exists in the working tree, the index has entries for
all three slots at that path, and the resolution is to either
keep the file (git add the working tree version) or complete
the deletion (git rm). The two operations are not equivalent,
and the engineer must reason about which side is correct.
Case 3 — both sides modify a binary file
Git does not attempt a textual merge of files it cannot diff as
text. PNG diagrams, compiled .tfstate files, vendored Go
binaries, and release tarballs are all binary in Git’s eyes;
when both branches change the same path, the algorithm cannot
produce a unified diff, much less a merged file:
git status
# Unmerged paths:
# both modified: diagrams/network-topology.png
# architecture/state/terraform.tfstate
git diff --name-only --diff-filter=U
# diagrams/network-topology.png
# architecture/state/terraform.tfstate
The status output is identical to case 1 (“both modified”), but
the resolution is fundamentally different. There are no hunks,
no conflict markers, no textual history to consult. The
engineer must pick one side wholesale — git checkout --ours <file> or git checkout --theirs <file> — and accept that any
work on the other side is discarded. A binary conflict on a
Terraform state file (covered in X-05 in depth) is the worst
case because the change is not just a file edit; it is a
record of cloud resources that diverged in two directions.
What git status is telling you
The per-path line in git status is a complete case
identifier once you know what each phrase means:
both modified— case 1 (textual conflict in the file) or case 3 (binary conflict in the file). The ambiguity is resolved by opening the file; binary files contain no conflict markers, textual ones do.both added— both sides added the same path. Identical content merges automatically; divergent content is a textual conflict.deleted by them— case 2 (deletion on theirs, modification on ours).deleted by us— case 2 (deletion on ours, modification on theirs).added by them/added by us— one side added the path the other never touched. Take the added version, or reject it deliberately.
These five phrases are the entire vocabulary of a merge
conflict’s per-path report. Recognising them in git status
output is what makes a large conflict list navigable.
Production discipline
Three rules for recognising conflict cases in a production merge:
- Read
git statusfirst, not the files. The status output classifies every conflicted path by case before you open a single file. Resolving in case order (textual, then delete/modify, then binary) ensures the slower binary decisions get the engineer’s attention rather than being glossed over after a long textual resolution. - Treat delete/modify as a semantic decision, not a syntactic one. A deletion in an infrastructure repository is often a production change. Confirm with the deleting branch’s author that the deletion is intended before accepting it.
- Run
git merge-treein CI before opening a PR. A pipeline that pre-checks the merge outcome withgit merge-treecan warn the PR author that conflicts are coming before they sit down to resolve them. The earlier the warning, the cheaper the resolution.
Cross-course references
- Linux for Production Sysadmins — Part XXVIII (ChangeMgmt) covers file deletion under change control; the delete/ modify conflict in a Git repository maps to the same dual- control principle.
- Ansible for Production Sysadmins — Part XXXVII (RepoArch) discusses inventory file merges; the most common Ansible conflict is a delete/modify on a host group file.
- Terraform for Production Sysadmins — Part XIX (PR) and Part XX (State) cover Terraform state file conflicts, which are case 3 binary conflicts with the highest possible blast radius in this course.
Quiz
Knowledge check · 4 questions
Q1. After a merge, `git status` prints `both modified: architecture/state/terraform.tfstate`. Which conflict case is this, and what is the resolution shape?
Q2. The `git status` phrase `deleted by them` means the branch that was just merged in no longer has the file, while the current branch still has it.
Q3. Name the three cases that produce a merge conflict, and the `git status` phrase that distinguishes a case-2 conflict from a case-1 conflict.
Q4. Diagnose the conflict cases in a multi-file IaC merge and recommend the order of resolution.
An engineer merges `feature/iam-rotation` into `main`. `git status` lists five conflicted paths: `terraform/iam/main.tf` shows `both modified`, `ansible/inventory/staging.ini` shows `deleted by them`, `ansible/inventory/production.ini` shows `deleted by us`, `diagrams/network-topology.png` shows `both modified`, and `architecture/state/terraform.tfstate` shows `both modified`. The merged-in branch is two weeks old; the current branch has been edited daily.
Passing score: 75%. Answers are checked in this browser.