Overview
This guide shows practical ways to fix Git rename conflicts you may see during merges or rebases:
- CONFLICT (rename/modify)
- CONFLICT (rename/rename)
- CONFLICT (rename/delete) or (delete/rename)
Key idea: resolve both the final file path and the final file content, then stage the result.
Notes on terminology:
- ours: the current branch (HEAD) during merge
- theirs: the branch being merged into the current branch
- During rebase, ours/theirs semantics per commit can be counterintuitive. Use git status to verify.
Quickstart (cheat sheet)
- Inspect conflicts
- git status
- git ls-files -u (see index stages)
- Decide the final file path name.
- Merge or choose the correct file content.
- Remove the path you will not keep: git rm path-to-drop
- Stage the path you will keep: git add final-path
- Complete the operation: git commit (merge) or git rebase --continue (rebase)
- Optional: enable rerere so Git learns your resolution next time: git config --global rerere.enabled true
Minimal working example (rename/modify)
This reproduces a typical conflict and shows a clean resolution.
# Setup
mkdir -p /tmp/rename-conflict-demo && cd /tmp/rename-conflict-demo
git init
git config user.email "[email protected]"
git config user.name "You"
echo "port: 8080" > config.yaml
git add config.yaml
git commit -m "init config"
# Ensure main branch name
git branch -M main
# Branch A renames the file
git checkout -b feature-rename
mkdir -p app
git mv config.yaml app/config.yaml
git commit -m "move config to app/config.yaml"
# Main branch modifies the original path
git checkout main
echo "# added on main" >> config.yaml
git add config.yaml
git commit -m "modify config.yaml on main"
# Merge: expect CONFLICT (rename/modify)
git merge feature-rename || true
echo "--- STATUS ---"
git status --porcelain -b
Resolve by keeping the rename and carrying over the modification:
# Inspect the two versions present in the working tree
# Typically you will see both files: app/config.yaml and config.yaml
git diff --no-index app/config.yaml config.yaml || true
# Open both files and apply the changes from config.yaml into app/config.yaml.
# For this tiny example, append the comment into the renamed file:
echo "# added on main" >> app/config.yaml
# Drop the old path, keep the renamed path
git rm config.yaml
git add app/config.yaml
git commit -m "Merge feature-rename: keep rename and include main changes"
You have resolved a rename/modify conflict by choosing the final path (app/config.yaml) and merging content.
What Git is telling you
- rename/modify: one side renamed a path; the other side modified the original path. You must decide the final name and combine content.
- rename/rename: both sides renamed the same file (possibly to different names). Decide the final name, then merge content.
- delete/rename: one side deleted; the other renamed. Decide whether the file survives (under the new name) or is deleted.
Step-by-step recipes
A) Fix CONFLICT (rename/modify)
Common choices:
- Keep the rename and include the other side’s changes
- Compare files
- git diff --no-index new/path old/path
- Edit new/path to include missing changes.
- Remove the old path: git rm old/path
- Stage and finish: git add new/path; git commit or git rebase --continue
- Keep the old path, drop the rename
- Move back: git mv new/path old/path
- Choose content if needed
- Take ours: git checkout --ours -- old/path
- Take theirs: git checkout --theirs -- old/path
- Stage and finish: git add old/path; git commit or git rebase --continue
- Keep both (rare)
- Choose one as final path; save the other under a different name
- git mv new/path chosen/path
- git mv old/path backup/path
- Merge content as desired, then stage and commit.
Tips:
- mergetool can help with content merges but not with path decisions. Handle renames with git mv / git rm first.
- To inspect base/ours/theirs blobs explicitly:
- git ls-files -u
- git show :1:old/path # base
- git show :2:new/path # ours
- git show :3:old/path # theirs
B) Fix CONFLICT (rename/rename)
- Identify both new names: Git will show something like A renamed to B in ours and to C in theirs.
- Choose the final name (say B):
- If both files exist, pick one as a starting point: git mv C B or git mv B C
- Merge content
- git diff --no-index B C
- Edit B to include desired changes.
- Remove the extra path and stage
- git rm C
- git add B
- Finish with commit or rebase --continue.
C) Fix CONFLICT (delete/rename)
- Keep the rename
- Ensure renamed file is present and correct: edit if needed
- git add new/path
- Finish with commit
- Keep the deletion
- Remove the renamed file: git rm new/path
- Finish with commit
Strategy options that influence renames
Merge with content preference:
- git merge -X ours branch # prefers ours on content conflicts
- git merge -X theirs branch # prefers theirs on content conflicts Note: these do not automatically pick a final path for rename conflicts; verify the path result.
Tune rename detection sensitivity:
- git merge -X find-renames=90% other Higher percentage requires closer similarity to count as a rename.
Config knobs:
- merge.renameLimit: raise if you have many renames
- diff.renames=true: make diffs track renames by default
Pitfalls and how to avoid them
- Forgetting to remove the old path: results in duplicate files. Always git rm the path you are dropping.
- Overwriting desired changes with checkout --ours/--theirs: confirm with git diff before staging.
- Assuming mergetool resolves the path: it typically resolves content only. Use git mv/git rm for names.
- Rebase surprises: ours/theirs semantics differ per picked commit. Rely on git status, not intuition.
- Losing executable bits or symlink state: check git diff --staged --summary before committing.
Performance notes
- Large rename sets can slow merges. Consider:
- Increase merge.renameLimit temporarily.
- Use find-renames= to tune similarity threshold.
- Break big merges into smaller ones when possible.
- Enable sparse-checkout to limit working tree size if unrelated paths are huge.
- Enable rerere to auto-apply repeated resolutions in long-lived branches:
- git config --global rerere.enabled true
Tiny FAQ
Q: Why do I see both the old and new file after merge? A: Git can leave both to let you decide the final path. Remove the one you won’t keep and stage the other.
Q: How do I pick all their changes but keep our rename? A: Apply their changes to the renamed path: edit new/path to include the diff from old/path, then git rm old/path and git add new/path.
Q: Can I automate rename conflict resolution? A: Partially. Use -X ours/theirs for content, find-renames to improve detection, and rerere to learn prior resolutions. Always verify the final path.
Q: Does rebase handle this differently than merge? A: The conflict types are the same, but ours/theirs labels are per-commit during rebase. Use git status and proceed with rebase --continue after staging the fix.