Skip to content
Foxipa

Suggest an edit

Tell us what should be corrected, clarified, or improved. Submission will be connected to Foxipa’s contribution service later.

No information is sent anywhere yet. Your suggestion is only prepared in your browser.

Learning

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.

Levels: Intermediate ~35 min read

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.

bash
git switch main
git merge topic
printf 'exit=%s\n' $?
git status --short --branch

Quick 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.

bash
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 topic

Quick 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.

bash
git merge topic
printf 'exit=%s\n' $?
git status --short
git rev-parse HEAD
git rev-parse MERGE_HEAD

Quick 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.

bash
git status
git status --short --branch

Quick 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.

bash
git ls-files --unmerged
git ls-files --stage

Quick 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.

bash
cat conflict.txt
git ls-files --unmerged -- conflict.txt

Quick 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.

bash
git diff
git diff --cc -- conflict.txt

Quick 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.

bash
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.

Was this page helpful?

Mark this learning resource complete to track your learning progress.