Resolving rename and delete conflicts Jump to heading

Content conflicts are the familiar kind: both sides edited the same lines. Tree conflicts are stranger. One side deleted a file the other side edited. Both sides renamed the same file to different names. One side moved a directory while the other added a file inside it. Git reports these with terse labels — CONFLICT (modify/delete), CONFLICT (rename/rename) — and leaves the working tree in a state that looks like nothing is wrong, because there may be no conflict markers at all. Resolving them means deciding which structural change wins and making sure the other side’s edits are not silently dropped. This page goes through each kind, within 3-way merge fundamentals.

When to use this approach Jump to heading

  • A merge, rebase or cherry-pick reports modify/delete, rename/delete, rename/rename or rename/add.
  • git status shows files as “deleted by us”, “deleted by them”, “both added” or “added by them”.
  • A refactor moved files while other branches were still editing them, as in coordinating a large refactor without conflict storms.
  • Edits seem to have disappeared after a merge that involved renames.

Step 1 — Read the status codes, not just the message Jump to heading

git status --short gives a two-letter code per conflicted path. The letters tell you what each side did.

git status --short | grep -E '^(DD|AU|UD|UA|DU|AA|UU)'
What each conflict status code meansUU is an ordinary content conflict. UD and DU mean one side modified a file the other deleted. AA means both sides added a file at the same path. AU and UA usually come from a rename on one side colliding with an addition or edit on the other.OursTheirsUUmodifiedmodifiedUDmodifieddeletedDUdeletedmodifiedAAaddedaddedAU / UAaddedmodified (or reverse)the first letter is ours, the second is theirs — U means updated, D deleted, A added

Two details trip people up. In a rebase, “us” is the branch you are rebasing onto and “them” is your commit, so the letters read backwards from what you expect. And Git’s rename detection decides whether something is reported as rename/delete or as a separate delete and add, which depends on how similar the files are.

Step 2 — Resolve modify/delete: decide whether the file should exist Jump to heading

One side deleted the file; the other changed it. Git keeps the modified version in the working tree so nothing is lost, and waits for you to choose.

# See what the modifying side changed, relative to the base
git log --oneline -3 MERGE_HEAD -- src/legacy/importer.py
git diff "$(git merge-base HEAD MERGE_HEAD)" MERGE_HEAD -- src/legacy/importer.py

# Option A: the deletion wins — confirm the edit is not needed elsewhere first
git rm src/legacy/importer.py
# Option B: the file survives with the edit
git add src/legacy/importer.py

The dangerous outcome is Option A without reading the diff: the other side’s edit may have been a bug fix that needs to be carried to wherever the functionality moved. If the file was deleted because its code moved, apply the edit to the new location before removing the old file.

# Verification: no paths remain in a conflicted state
git diff --name-only --diff-filter=U

Step 3 — Resolve rename/delete and rename/modify Jump to heading

When one side renamed a file and the other edited it, Git usually handles it automatically: rename detection matches the old and new paths and applies the edit to the new name. When one side renamed and the other deleted, Git reports a conflict and leaves the renamed file in place.

Choosing between a rename and a deleteIf the delete was intentional because the code is obsolete, remove the renamed file too. If the rename moved code that is still needed, keep the renamed file and treat the delete as superseded. If both sides were reorganising, decide the final layout explicitly.Why did the other side delete the file?code is obsoleteDelete bothgit rm new pathit was moving it tooKeep one pathagree a locationby mistakeKeep the renamegit add new pathask the person who deleted it — the history rarely says why
# Rename detection threshold: lower it when files were renamed and heavily edited together
git merge -X find-renames=40% feature/reorg
# Inspect how Git paired paths
git diff --name-status -M40% "$(git merge-base HEAD MERGE_HEAD)" MERGE_HEAD | grep '^R'

A rename that Git failed to detect looks like one side deleting a file and adding an unrelated one. Lowering the similarity threshold lets Git pair them, which turns a confusing delete/add into a rename it can merge edits through.

Step 4 — Resolve rename/rename: one file, two new names Jump to heading

Both sides renamed the same file to different paths. Git keeps both new paths in the working tree with the merged content and asks you to pick.

git status --short
# AU src/core/parser.py         (ours renamed old_parser.py here)
# UA src/parsing/parser.py      (theirs renamed it here)

# Keep one location, remove the other, and make sure imports match
git rm --cached src/parsing/parser.py && rm src/parsing/parser.py
git add src/core/parser.py
git grep -n 'parsing.parser' -- '*.py'     # fix references that assumed the other location

Rename/rename conflicts almost always mean two people reorganised the same area independently. The code choice is easy; the conversation about which layout the project wants is the real resolution.

Step 5 — Recover an edit lost in a structural resolution Jump to heading

If you suspect an edit vanished because a file was resolved as deleted, the edit still exists in the commit that made it. Find it and reapply it to the surviving location.

# Commits on the merged branch that touched the old path
git log --oneline "$(git merge-base HEAD^1 HEAD^2)..HEAD^2" -- src/legacy/importer.py
# Re-apply a specific change to the new location with a path-rewritten patch
git format-patch -1 <sha> --stdout -- src/legacy/importer.py |
  sed 's#src/legacy/importer.py#src/ingest/importer.py#g' | git apply -3
Carrying an edit across a moveA bug fix was made to the old path on one branch while another branch moved the file. After the merge deleted the old path, the fix is recovered from its original commit, rewritten to the new path and applied with a three-way fallback.fix commitpatchnew locationformat-patch, old pathrewrite pathgit apply -3-3 falls back to a three-way merge if the moved file has also changed

Validation checklist Jump to heading

Frequently Asked Questions Jump to heading

Why are there no conflict markers in a modify/delete conflict? Jump to heading

There is nothing to mark: one side has no content at all. Git leaves the surviving version in the working tree and records the conflict in the index. git status is the only clear signal.

Can I tell Git to always prefer deletions or always keep files? Jump to heading

git checkout --ours or --theirs works per path once you know which side you want, but there is no safe global rule. A blanket preference for deletions loses bug fixes; a blanket preference for keeping files resurrects code that was removed on purpose.

How does rename detection decide a file was renamed? Jump to heading

It compares deleted and added files and pairs those whose content is at least the threshold similar — fifty percent by default. Files renamed and heavily rewritten in the same commit fall below it, which is why committing a rename separately from its edits makes later merges far smoother.