Git Replace & Grafts Cheat Sheet¶
# 1. Enable replace globally
git config --global replace.enabled true
# 2. Re-parent a commit (graft) while keeping its SHA
git commit-tree -p <newparent1> [-p <newparent2> ...] \
-m "$(git log --format=%B -n 1 <oldcommit>)" \
<oldcommit>^{tree} > /tmp/grafted
git replace <oldcommit> $(cat /tmp/grafted)
# 3. Replace a blob (e.g. remove a leaked secret) with a sanitized version
git replace <oldblob> $(git hash-object -w -t blob < /path/to/clean-file)
# 4. Replace an entire tree (swap a directory snapshot)
git replace <oldtree> $(git write-tree --prefix=corrected/)
# 5. List active replacements
git replace -l
# 6. Delete a replacement (revert to original object)
git replace -d <oldobject>
# or
git update-ref -d refs/replace/<oldobject>
# 7. Show what <old> resolves to right now
git cat-file -p <old> # without -p shows the original
git -c replace.enabled=false cat-file -p <old> # bypass replace
# 8. Push replace refs to a remote (share with team/CI)
git push origin --refs='*:refs/replace/*'
# 9. Fetch replace refs on another clone
git fetch origin 'refs/replace/*:refs/replace/*'
# 10. Temporarily disable replace for one command
git -c replace.enabled=false <subcommand>
GIT_NO_REPLACE_OBJECTS=1 git <subcommand>
# 11. Verify replacement is in effect
git show <oldcommit> # shows grafted parents
git log --format=%P -n 1 <old> # lists parents after replace
# 12. Update a replace ref to point at a new replacement
git update-ref refs/replace/<old> <newreplacement>
# 13. Graft via legacy .git/info/grafts
echo "<commit> <parent1> <parent2> ..." >> .git/info/grafts
Common Recipes¶
Fix a merge commit that should have been a fast-forward (no SHA change):
NEW=$(git commit-tree -p <only-parent> -m "$(git log --format=%B -n 1 <mergecommit>)" <mergecommit>^{tree})
git replace <mergecommit> $NEW
Remove a large binary blob from all commits (preserves SHAs):
Backport fix onto correct base without rebasing:
GRAFTED=$(git commit-tree -p <correct-base> -m "$(git log --format=%B -n 1 <backport>)" <backport>^{tree})
git replace <backport> $GRAFTED
Pitfalls¶
- Replace refs are local by default — push them explicitly to share.
- They are not transferred by a plain
git cloneorgit fetch. - Avoid circular replaces (A→B and B→A): Git will detect and break the cycle.
- Replace does not work for unreachable (dangling) objects unless you read them explicitly.
- Always test in a throwaway clone before pushing replace refs to a shared remote.