Read git-merge-one-file Before You Debug a Merge

A merge script left one path in a broken state, and you need to know exactly what git-merge-one-file did with it. This guide walks through its seven arguments and the cases it handles. This is a Git 2.43.0 guide, matching the git-man package installed on this machine.

Allow about 15 minutes. You need Git and a disposable repository, or an existing merge you are prepared to abort. No elevated privileges are required. The helper edits the index and working tree, so do not experiment with it in a repository containing uncommitted work unless you have a verified backup.

1. Recognise what this command is

git-merge-one-file is Git's standard per-file merge helper. It is meant for git merge-index, called after git read-tree -m has already done the trivial part of a merge. It resolves one path from an original blob, an ours blob and a theirs blob, then updates the index and working file.

That makes it a plumbing command, not the normal way to merge a branch. Start with git merge for an ordinary branch merge. Reach for this helper when diagnosing old merge machinery, reproducing a single-file merge, or working out why git merge-index left a path unresolved.

$ git --version
git version 2.43.0
$ git merge-one-file
usage: git merge-one-file <orig blob> <our blob> <their blob> <path> <orig mode> <our mode> <their mode>

Checkpoint: Seeing that usage line is expected. It confirms the installed helper and also shows why calling it with just a filename is not valid.

2. Read the seven arguments correctly

The helper takes three object names, one pathname and three file modes, in this order:

  1. orig blob: the file's blob in the common ancestor.
  2. our blob: the version from the current side of the merge.
  3. their blob: the version from the other side.
  4. path: the repository pathname being merged.
  5. orig mode: the ancestor's mode.
  6. our mode: the current side's mode.
  7. their mode: the other side's mode.

For a missing file, the relevant blob ID and mode are empty. Do not substitute the word empty; the caller passes an actually-empty argument, so quote variables when reproducing a call:

git merge-one-file \
    "$ORIG_BLOB" "$OUR_BLOB" "$THEIR_BLOB" "$PATH" \
    "$ORIG_MODE" "$OUR_MODE" "$THEIR_MODE"

Only run this with variables that came from a controlled merge-index operation. Blob IDs describe repository objects, while modes such as 100644 describe ordinary non-executable files. A mode change is part of the merge decision, not decoration.

3. Understand the cases it handles

The helper first deals with changes that do not need content merging:

Those cases can print messages such as Removing path or Adding path. A non-zero result can still mean the input describes an unsupported or conflicting state, so read the message on standard error and inspect the index; do not infer success just because a file exists in the working tree.

For genuinely different content, the helper invokes Git's three-way file merger. It writes the merged content to the working file and updates the index if the merge succeeds. A conflict leaves conflict markers in the file and returns status 1 with an error such as ERROR: content conflict in path. The helper also reports a permissions conflict when the two modes differ.

Symbolic-link changes and conflicting submodule changes are explicitly not merged by this helper. They fail outright rather than pretending ordinary text merging is safe, because changing a link target or a submodule commit can alter what later commands actually execute.

4. Inspect a failure without overwriting it

When a merge stops, begin with the ordinary Git inspection commands:

$ git status
$ git diff -- path/to/file
$ git diff --cached -- path/to/file
$ git ls-files --unmerged -- path/to/file

The last command shows the staged ancestor, ours and theirs entries while the index still holds an unresolved conflict. The working file can contain conflict markers while the index retains all three source stages. Keep a copy of the file if you want to compare several repair attempts.

Do not run the helper repeatedly on a live unresolved path hoping another attempt will improve it. It reads and writes merge state, and a second invocation may not see the same index stages. If the merge is disposable, save any useful diff and abort it:

$ git diff > /tmp/merge-review.patch
$ git merge --abort

Warning: git merge --abort is state-changing and can discard merge results that are not committed. Check the saved patch before using it, and only for an operation that is genuinely safe to abandon. If there was no merge operation for Git to abort, stop and restore the index and working tree from your own backup or recovery process rather than guessing.

5. Keep the normal workflow in charge

For a normal conflict, use the high-level workflow instead: edit the file, remove the conflict markers, run the relevant tests, stage the resolved path with git add, and continue the merge. The helper is useful for understanding the lower layer, not for replacing those safety checks.

Common traps

Done means