Git Merge Conflicts: Detecting and Inspecting the Unresolved State
Use real Git to detect and inspect an unresolved merge: non-zero merge status, unmerged paths, index stages, conflict markers, HEAD, and MERGE_HEAD.
Intermediate
A merge conflict is an unresolved merge state
V436 established merge-base reasoning, and V437 established fast-forward versus true-merge outcomes. This lesson begins where a true merge cannot immediately produce one result: Git has attempted the integration and leaves an unresolved merge state. A conflict is therefore not just a textual marker in a file. It is observable repository state: the merge command fails, paths can be unmerged, the index can contain multiple stages for a conflicting path, and the merge machinery records the incoming commit. Conflict resolution itself is deliberately deferred to the next lesson.
git switch main
git merge topic
printf 'exit=%s\n' $?
git status --short --branchQuick check
After a conflicting `git merge topic`, which description is most precise?
The histories require a true merge, but the file-level result cannot be written automatically.The merge is in progress and the conflicting path is unmerged.
Build a deterministic conflict you can inspect
Use a disposable repository with a shared base and incompatible edits to the same tracked path. The important point is that both sides start from a common ancestor and then change the same path differently. The conflict fixture is intentionally small so every state transition can be inspected directly.
git init -b main
git config user.name "Foxipa Git Test"
git config user.email "git-test@example.invalid"
printf "base\n" > conflict.txt
git add conflict.txt
git commit -m "Add conflict base"
git switch -c topic
printf "feature\n" > conflict.txt
git commit -am "Feature change"
git switch main
printf "main\n" > conflict.txt
git commit -am "Main change"
git merge topicQuick check
Why must the fixture include a common base before the two branch-specific edits?
Both branches contain different edits to the same path after a shared base commit.Git can identify the base, current side, and incoming side of the conflict.
The merge command reports failure, but the repository is not simply unchanged
A conflicting merge exits non-zero because Git cannot complete the merge automatically. That non-zero result does not mean the repository has returned to its pre-merge state. Git has populated merge state that lets the conflict be inspected and, later, resolved or aborted. Always inspect state after a failed merge instead of treating the exit status as the whole story.
git merge topic
printf 'exit=%s\n' $?
git status --short
git rev-parse HEAD
git rev-parse MERGE_HEADQuick check
What should you verify after the conflicting merge returns non-zero?
The merge has failed because of a conflict.Status, HEAD, and merge metadata expose the unresolved operation.
`git status` exposes unmerged paths
During an unresolved conflict, `git status` classifies the affected path as unmerged. In short format, a common content conflict appears as `UU path`. The long format explicitly says that there are unmerged paths and that the merge has not yet been completed. This is a state observation, not a resolution step.
git status
git status --short --branchQuick check
What does `UU conflict.txt` mean in this context?
The merge has stopped with `conflict.txt` unresolved.`git status --short` reports `UU conflict.txt`.
The index records the conflict with stages
The index is the most precise local evidence for an unresolved path. `git ls-files --unmerged` shows the unmerged entries. For a normal three-way content conflict, the same pathname can appear at stage 1, stage 2, and stage 3: stage 1 is the common ancestor, stage 2 is the current branch side, and stage 3 is the incoming side. This is why an unmerged path is not represented by one ordinary stage-0 index entry.
git ls-files --unmerged
git ls-files --stageQuick check
For a three-way content conflict, what do stages 1, 2, and 3 represent?
The conflicting path has multiple unmerged index entries.`git ls-files --unmerged` shows stage 1, 2, and 3 entries for the path.
Conflict markers are working-tree evidence, not the index itself
Git commonly writes conflict markers into the working-tree file so the competing text can be inspected. In the default marker form, `<<<<<<< HEAD` introduces the current branch side, `=======` separates the sides, and `>>>>>>> topic` closes the incoming side. These markers are a representation in the working tree; the index simultaneously retains the separate stage entries. Do not confuse the marker text with the index representation.
cat conflict.txt
git ls-files --unmerged -- conflict.txtQuick check
Which statement correctly distinguishes conflict markers from index stages?
The file contains conflict markers and the index has unmerged stages.Both representations are visible and serve different purposes.
`git diff` can expose the unresolved working-tree state
In the unresolved state, `git diff` can show the conflict in combined form. The output identifies the competing sides and the working-tree result containing the conflict markers. The important curriculum point is observational: use diff output to inspect what remains unresolved; do not treat this lesson as the procedure for choosing or staging a resolution. The resolution workflow belongs to V439.
git diff
git diff --cc -- conflict.txtQuick check
What is the role of `git diff` in this lesson?
The merge is still unresolved and the path has unmerged index entries.Diff output provides evidence about what remains to be resolved.
The receiving branch tip has not advanced to a merge commit
A conflict is a partially completed integration state. The current branch tip remains at the pre-merge commit while `MERGE_HEAD` identifies the commit Git is trying to merge. Therefore, “the merge failed” and “nothing happened” are not equivalent statements. The branch history has not gained a merge commit, but merge-state metadata and the working tree/index have changed.
before=$(git rev-parse HEAD)
incoming=$(git rev-parse topic)
git rev-parse HEAD
git rev-parse MERGE_HEAD
git rev-list --parents -n 1 HEAD
printf 'before=%s\nincoming=%s\n' "$before" "$incoming"Quick check
During this unresolved merge, what is the relationship between `HEAD` and `MERGE_HEAD`?
The merge has not produced a merge commit.HEAD still resolves to the current branch tip and MERGE_HEAD resolves to the incoming tip.
Do not collapse conflict detection, resolution, and completion into one step
This lesson ends at precise conflict detection and inspection. V439 will own the act of resolving conflict content and converting an unmerged path back into an ordinary index entry. V440 will own the merge state machine around continuing and aborting a merge. This separation prevents the learner from treating “edit the file” as the entire Git model: the working tree, index, HEAD, and merge metadata all participate in the state transition.
Practice what you learned
Use the dedicated Git code-completion exercise for this lesson.
Official-source boundary
This lesson uses only official Git documentation: `git-merge`, `git-status`, `git-ls-files`, and the official Git diff/index documentation needed to inspect unresolved entries. The current official documentation baseline is Git 2.56.0. The local verifier is Git 2.47.3; all execution claims in the verification suite are observations from that executable. Conflict resolution and merge continuation/abort are intentionally deferred to later curriculum ownership.
Thanks for your feedback.
Mark this learning resource complete to track your learning progress.