Home / Alt manpages / git-replace(1)

  • git-replace(1)
  • User command
  • linux

Test Alternate Git Objects Safely with git replace

git replace lets Git read one object in place of another, without touching the branch, tag or original object at all.

That makes it handy for testing a repaired commit, examining an alternate tree, or trying different parentage before you commit to rewriting history. The result is local to the repository and controlled entirely by refs under refs/replace/.

This guide targets Git 2.43.0, the version installed with the local git-man package. Allow about 10 minutes if you already have the two object IDs. No command here needs elevated privileges.

Before you start

  1. Work in the repository whose object database contains both objects.
  2. Choose an object to hide temporarily and a replacement object of the same Git type.
  3. Save the two full object IDs. Short IDs work fine for normal Git revision parsing, but full IDs make a replacement reference explicit and easier to audit later.

For the examples below, swap in your own values for these shell variables. They are placeholders, not commands that discover suitable objects for you:

OLD_OBJECT=1111111111111111111111111111111111111111
NEW_OBJECT=2222222222222222222222222222222222222222

1. Inspect the objects first

Check that both IDs exist and identify their types before creating anything. This catches the most common mistake early: trying to replace a commit with a tree or blob.

git cat-file -t "$OLD_OBJECT"
git cat-file -t "$NEW_OBJECT"
git cat-file -p "$OLD_OBJECT" | sed -n '1,12p'
git cat-file -p "$NEW_OBJECT" | sed -n '1,12p'

For a commit, the first command prints commit; for a tree or file object it prints tree or blob. The two type outputs must match unless you deliberately use --force. For initial testing, do not bypass that safeguard.

Checkpoint

Both object IDs exist and their types match, so a plain replacement is safe to create.

2. Create the replacement

Run the two-object form to create a replacement reference. Git names the reference after OLD_OBJECT and stores NEW_OBJECT as its target; the original object stays in the object database untouched.

git replace "$OLD_OBJECT" "$NEW_OBJECT"

A successful creation is normally silent. If a replacement reference already exists for the old object, the command fails rather than overwriting it, a useful stop that gives you a chance to inspect the existing mapping before deciding whether it is yours to change.

git replace --format=long -l "$OLD_OBJECT"

Expected output has this shape, with your actual IDs and object type:

1111111111111111111111111111111111111111 (commit) -> 2222222222222222222222222222222222222222 (commit)

3. Prove which object Git is reading

Most Git commands now follow the replacement by default. A commit-oriented check makes the change visible without moving a branch:

git show --no-patch --format='%H %s' "$OLD_OBJECT"
git --no-replace-objects show --no-patch --format='%H %s' "$OLD_OBJECT"

The first command reports the replacement commit's subject. The second disables replacement lookup for that one invocation and reports the original commit's subject instead. The option must appear immediately after git. The environment variable GIT_NO_REPLACE_OBJECTS=1 gives the same bypass for commands launched under that environment.

Replacement references are not a general transport mechanism. The manpage excludes reachability operations such as pruning, pack transfer and fsck from replacement lookup, so do not treat a successful local inspection as proof another clone, a fetch or repository maintenance will see the same view.

4. List and filter mappings

With no arguments, git replace lists every replacement reference in short form, showing the replaced IDs. Use a format when the target or object types matter:

git replace
git replace --format=medium -l
git replace --format=long -l "$OLD_OBJECT"
  • short shows the replaced object ID.
  • medium shows the old and replacement IDs.
  • long adds each object's type.

A pattern passed to -l limits which object names are listed. If a filter prints nothing, check the ID and the repository before assuming the mapping has disappeared.

5. Remove the mapping

Deleting the replacement reference restores normal lookup. This does not delete either object, branch or commit:

git replace --delete "$OLD_OBJECT"
git replace --format=long -l "$OLD_OBJECT"

The delete command prints a message such as Deleted replace ref '...', and the following list should then produce no output. If you need the mapping later, record the full output of git replace --format=medium -l first: there is no separate undo command, so recreating the mapping from the recorded IDs is the practical recovery.

6. Other ways to create replacements

git replace --edit OBJECT pretty-prints the existing object to a temporary file, opens your configured editor, parses the result, and creates a replacement object of the same type. Use this only when you understand the object's format. The --raw option supplies raw contents while editing; the installed documentation says this currently affects trees and may need an editor that handles binary data cleanly.

git replace --graft COMMIT PARENT... creates a new commit with the supplied parents and replaces the named commit with it. With no parent arguments, it can create a parentless replacement. Treat this as history surgery for local analysis, not a shared rewrite. git replace --convert-graft-file converts entries in .git/info/grafts to replacement commits and deletes that graft file after success, so back it up first.

Common traps

  • Different types. A commit, tree and blob cannot normally replace one another. Fix the object selection instead of reaching for --force.
  • Unexpected history. Replacement affects commands that resolve or traverse ordinary objects, so a command can show history different from the branch's stored history.
  • Hard reset. The manpage warns that git reset --hard to a replaced commit can move the branch to the replacement commit. Avoid it while a mapping is active, or remove the mapping and verify the target before resetting.
  • Overwriting. git replace --force OLD NEW overwrites an existing mapping. This is destructive to the previous mapping, so capture it with git replace --format=long -l OLD first.

Done means

  • The old and new objects exist and their types match.
  • git replace --format=long -l shows the intended mapping.
  • A normal Git command shows the replacement, while git --no-replace-objects shows the original.
  • The mapping is deleted when the experiment ends, and a final listing confirms no stale replacement remains.