Skip to content

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):

CLEAN=$(git hash-object -w -t blob <<<"[file removed — see LFS]")
git replace <oldblob> $CLEAN

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 clone or git 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.