Skip to content

replace Git Command Guide

The git replace command creates replace references in the refs/replace/ namespace that allow substituting one object for another. This enables fixing commits, changing parent relationships, or modifying objects without rewriting the entire repository history.

Terminal window
git replace [-f] <object> <replacement>
git replace [-f] --edit <object>
git replace [-f] --graft <commit> [<parent>...]
git replace [-f] --convert-graft-file
git replace -d <object>...
git replace [--format=<format>] [-l [<pattern>]]
OptionDescription
-f, --forceOverwrite existing replace reference
--editEdit replacement object interactively
--graftReplace commit with new parent relationships
--convert-graft-fileConvert .git/info/grafts to replace refs
OptionDescription
-d, --deleteDelete replace references
-l, --listList replace references
--format=<format>Output format for listing
ParameterDescription
<object>Object to be replaced (SHA-1)
<replacement>Replacement object (SHA-1)
<commit>Commit to graft
<parent>New parent commits for grafting
<pattern>Pattern to filter listed refs
Replace Reference Structure:
├── refs/replace/<original-sha1> -> <replacement-sha1>
├── Transparent to most Git operations
├── Stored in .git/refs/replace/ directory
├── Can replace any object type
└── Preserves original repository history
Replacement Process:
1. Create replace reference
2. Git commands see replacement instead of original
3. Original object remains in database
4. Can be removed with git replace -d
Supported Replacements:
├── Commits -> Commits (most common)
├── Trees -> Trees
├── Blobs -> Blobs
├── Tags -> Tags
└── Annotations -> Annotations
Type Safety:
├── Same object types by default
├── -f flag bypasses type checking
├── Merge commits can replace non-merges
└── Parent relationships can change
Comparison with Alternatives:
├── git replace: Transparent object substitution
├── git rebase: Rewrites commit history
├── git filter-branch: Rewrites entire branches
├── git commit --amend: Changes latest commit only
└── Manual editing: Requires full repository rewrite
When to Use Replace:
├── Fix commits without breaking published history
├── Correct author information
├── Change parent relationships
├── Fix merge commits
└── Correct object corruption
Terminal window
# Replace one commit with another
git replace <old-commit> <new-commit>
# Force replacement (bypass type checking)
git replace -f <old-object> <new-object>
# Replace with interactively edited version
git replace --edit <commit>
# List all replace references
git replace -l
# Delete a replace reference
git replace -d <original-object>
Terminal window
# Fix a commit message
git show <bad-commit> > /tmp/commit.txt
# Edit /tmp/commit.txt to fix the message
git hash-object -t commit -w /tmp/commit.txt > /tmp/new-commit
git replace <bad-commit> $(cat /tmp/new-commit)
# Change author information
git show <commit> | sed 's/Author: Old Name/Author: New Name/' | git hash-object -t commit -w - > /tmp/new-commit
git replace <commit> $(cat /tmp/new-commit)
# Fix parent relationships
git replace --graft <commit> <new-parent1> <new-parent2>
Terminal window
# List all replacements
git replace -l
# List with full format
git replace --format=short -l
# Filter by pattern
git replace -l "abc*"
# Show replacement details
git show refs/replace/<sha1>
# Verify replacement exists
git replace --format=full -l <sha1>
Terminal window
# Change commit parent (remove from history)
git replace --graft <commit> <new-parent>
# Make commit an initial commit (no parents)
git replace --graft <commit>
# Change merge commit parents
git replace --graft <merge-commit> <parent1> <parent2> <parent3>
# Convert grafts file to replace refs
git replace --convert-graft-file
Terminal window
# Replace tree object
git mktree < tree.txt > /tmp/new-tree
git replace <old-tree> $(cat /tmp/new-tree)
# Replace blob object
echo "new content" | git hash-object -w --stdin > /tmp/new-blob
git replace <old-blob> $(cat /tmp/new-blob)
# Replace tag object
git mktag < tag.txt > /tmp/new-tag
git replace <old-tag> $(cat /tmp/new-tag)
Terminal window
# Replace multiple commits
commits_to_replace=("abc123" "def456" "ghi789")
replacements=("new123" "new456" "new789")
for i in "${!commits_to_replace[@]}"; do
git replace "${commits_to_replace[$i]}" "${replacements[$i]}"
done
# Bulk delete replacements
git replace -d $(git replace -l)
# Conditional replacement
if git cat-file -t "$object" | grep -q "commit"; then
git replace "$object" "$replacement"
fi
Terminal window
# Configure replace behavior
git config advice.graftFileDeprecated false # Suppress graft warnings
# Configure reflog for replacements
git config core.logAllRefUpdates true # Enable reflog
# Configure replacement visibility
git config core.useReplaceRefs true # Use replace refs (default)
Terminal window
# Always backup before replacements
git branch backup-before-replace
# Verify objects exist
git cat-file -t <object> || exit 1
git cat-file -t <replacement> || exit 1
# Test replacement effects
git log --oneline <object> # Should show replacement
git log --oneline <replacement> # Should show original
# Document replacements
echo "<object> -> <replacement>: <reason>" >> .git/REPLACE_LOG
# Clean up when done
git replace -d <object>
Terminal window
# Choose replacement type based on situation:
# - Commit content fix: git replace <old> <new>
# - Parent relationship change: git replace --graft <commit> <parents>
# - Author/email fix: Create new commit with corrected info
# - Object corruption: Replace with corrected object
# - History cleanup: Use for published history fixes
# General rules:
# - Test replacements thoroughly
# - Document all replacements
# - Use -f sparingly (type safety)
# - Prefer grafts for parent changes
# - Clean up temporary replacements
#!/bin/bash
# Repository surgery with replace
fix_commit_history() {
local bad_commit="$1"
local fix_type="$2"
echo "Fixing commit history for $bad_commit"
case "$fix_type" in
"author")
# Fix author information
new_commit=$(git show "$bad_commit" |
sed 's/Author: Old Name/Author: Correct Name/' |
git hash-object -t commit -w --stdin)
git replace "$bad_commit" "$new_commit"
;;
"message")
# Fix commit message
new_commit=$(git show "$bad_commit" |
sed '1,/^$/s/.*bad message.*/Fixed commit message/' |
git hash-object -t commit -w --stdin)
git replace "$bad_commit" "$new_commit"
;;
"parent")
# Change parent relationship
git replace --graft "$bad_commit" <correct-parent>
;;
esac
echo "Replacement created. Test with: git log --oneline"
}
fix_commit_history "abc123" "author"
Terminal window
# Fix published history collaboratively
collaborative_fix() {
local broken_commit="$1"
local fixed_commit="$2"
echo "Applying collaborative fix"
# Verify the fix
git show "$fixed_commit"
read -p "Does this fix look correct? (y/N): " confirm
if [[ "$confirm" == "y" ]]; then
# Apply the replacement
git replace "$broken_commit" "$fixed_commit"
# Notify collaborators
echo "Replacement applied. Run 'git fetch --all' to get the fix"
# Document the change
echo "$(date): $broken_commit -> $fixed_commit (collaborative fix)" >> .git/COLLABORATIVE_FIXES
else
echo "Fix cancelled"
fi
}
collaborative_fix "broken123" "fixed456"
Terminal window
# Automated commit quality fixes
automated_fixes() {
echo "Applying automated quality fixes"
# Fix commits with missing signoffs
git rev-list HEAD | while read -r commit; do
if ! git show -s --format=%B "$commit" | grep -q "Signed-off-by:"; then
# Create fixed commit with signoff
fixed_commit=$(git show "$commit" |
sed '/^---$/i\
Signed-off-by: Automated Fix <fix@example.com>' |
git hash-object -t commit -w --stdin)
git replace "$commit" "$fixed_commit"
echo "Added signoff to $commit"
fi
done
echo "Automated fixes applied"
}
automated_fixes
Terminal window
# Debug replacement visibility
debug_replacement() {
local object="$1"
echo "Debugging replacement for $object"
# Check if replacement exists
if git show-ref "refs/replace/$object" >/dev/null 2>&1; then
replacement=$(git show-ref "refs/replace/$object" | awk '{print $1}')
echo "Replacement exists: $object -> $replacement"
else
echo "No replacement found for $object"
return 1
fi
# Test visibility
echo "Original object:"
git cat-file -t "$object"
echo "What Git sees:"
git cat-file -t "$object" # Should show replacement type
# Check configuration
if [ "$(git config core.useReplaceRefs)" = "false" ]; then
echo "Replace refs are disabled"
git config core.useReplaceRefs true
fi
}
debug_replacement "abc123"
Terminal window
# Validate replacement objects
validate_replacement() {
local original="$1"
local replacement="$2"
echo "Validating replacement: $original -> $replacement"
# Check object existence
for obj in "$original" "$replacement"; do
if ! git cat-file -t "$obj" >/dev/null 2>&1; then
echo "Object $obj does not exist"
return 1
fi
done
# Check object types
orig_type=$(git cat-file -t "$original")
repl_type=$(git cat-file -t "$replacement")
if [ "$orig_type" != "$repl_type" ]; then
echo "Type mismatch: $orig_type -> $repl_type"
echo "Use -f to force replacement"
return 1
fi
echo "Replacement is valid"
return 0
}
validate_replacement "commit123" "commit456"
Terminal window
# Handle graft conversion problems
fix_graft_conversion() {
echo "Fixing graft conversion issues"
# Check grafts file
if [ -f .git/info/grafts ]; then
echo "Found grafts file"
cat .git/info/grafts
else
echo "No grafts file found"
return 1
fi
# Convert grafts to replace refs
git replace --convert-graft-file
# Verify conversion
git replace -l
# Remove old grafts file
mv .git/info/grafts .git/info/grafts.backup
echo "Graft conversion complete"
}
fix_graft_conversion

Performance Issues with Many Replacements:

Section titled “Performance Issues with Many Replacements:”
Terminal window
# Optimize performance with many replacements
optimize_replacements() {
echo "Optimizing replacement performance"
# Count replacements
replace_count=$(git replace -l | wc -l)
echo "Found $replace_count replacements"
if [ "$replace_count" -gt 100 ]; then
echo "Many replacements detected - consider consolidation"
# Pack replace refs
git pack-refs --all
# Optimize repository
git gc --aggressive
fi
# Check for redundant replacements
git replace -l | while read -r line; do
original=$(echo "$line" | cut -d' ' -f1)
replacement=$(echo "$line" | cut -d' ' -f2)
# Check if replacement points to another replacement
if git show-ref "refs/replace/$replacement" >/dev/null 2>&1; then
echo "Chained replacement detected: $original -> $replacement"
fi
done
}
optimize_replacements
Terminal window
# Handle replacements in shared repositories
handle_shared_repo() {
echo "Handling replacements in shared repository"
# Check if replacements should be shared
if [ -d .git/refs/replace ]; then
echo "Replace refs found"
# Decide whether to push replacements
read -p "Push replace refs to remote? (y/N): " push_replace
if [[ "$push_replace" == "y" ]]; then
# Push replace refs
git push origin refs/replace/*:refs/replace/*
else
echo "Replace refs will remain local"
fi
fi
# Document local replacements
git replace -l > .git/LOCAL_REPLACEMENTS.txt
}
handle_shared_repo
#!/bin/bash
# Fix published commits without rewriting history
fix_published_commit() {
local bad_commit="$1"
local fix_description="$2"
echo "Fixing published commit: $bad_commit"
echo "Issue: $fix_description"
# Show the problematic commit
git show "$bad_commit"
# Create fixed version
case "$fix_description" in
"wrong-author")
# Fix author information
fixed_commit=$(git show "$bad_commit" |
sed 's/Author: Wrong Name/Author: Correct Name/' |
git hash-object -t commit -w --stdin)
;;
"typo-in-message")
# Fix commit message
fixed_commit=$(git show "$bad_commit" |
sed 's/Typo here/Corrected text/' |
git hash-object -t commit -w --stdin)
;;
"missing-file")
# Add missing file to commit
git read-tree --prefix=/tmp/fix/ "$bad_commit"
# Add missing file to /tmp/fix/
git add /tmp/fix/missing-file.txt
fixed_tree=$(git write-tree --prefix=/tmp/fix/)
fixed_commit=$(git commit-tree "$fixed_tree" -p "$bad_commit" -m "Fixed: $(git show -s --format=%s "$bad_commit")")
;;
esac
# Apply replacement
git replace "$bad_commit" "$fixed_commit"
# Verify fix
echo "Original commit:"
git show "$bad_commit"
echo "Fixed commit:"
git show "$fixed_commit"
# Document the fix
echo "$(date): Fixed $bad_commit - $fix_description" >> .git/FIXES.txt
echo "Replacement applied. Original history preserved."
}
fix_published_commit "abc123" "wrong-author"
Terminal window
# Clean up repository during migration
repository_cleanup() {
echo "Repository cleanup and migration"
# Fix author information across commits
git log --all --format='%H %an <%ae>' | while read -r commit author; do
if [[ "$author" == *"old-domain.com"* ]]; then
new_author=$(echo "$author" | sed 's/old-domain.com/new-domain.com/')
fixed_commit=$(git show "$commit" |
sed "s/Author: $author/Author: $new_author/" |
git hash-object -t commit -w --stdin)
git replace "$commit" "$fixed_commit"
echo "Fixed author for $commit"
fi
done
# Remove sensitive files from history
sensitive_files=("passwords.txt" "secrets.key" "*.log")
for file in "${sensitive_files[@]}"; do
git log --all --full-history -- "$file" | while read -r line; do
if [[ "$line" =~ ^commit ]]; then
commit=$(echo "$line" | cut -d' ' -f2)
# Create commit without sensitive file
git read-tree --empty
git read-tree --prefix=/ "$commit"
git rm --cached "$file" 2>/dev/null || true
clean_tree=$(git write-tree)
clean_commit=$(git commit-tree "$clean_tree" -p "$commit" -m "$(git show -s --format=%B "$commit")")
git replace "$commit" "$clean_commit"
echo "Removed $file from $commit"
fi
done
done
echo "Repository cleanup complete"
}
repository_cleanup
Terminal window
# Correct historical data without breaking builds
historical_correction() {
local correction_type="$1"
echo "Applying historical correction: $correction_type"
case "$correction_type" in
"timezone-fix")
# Fix timezone information in old commits
git rev-list --all | while read -r commit; do
commit_date=$(git show -s --format=%ci "$commit")
if [[ "$commit_date" == *" 00:00:00"* ]]; then
# Fix timezone offset
corrected_commit=$(git show "$commit" |
sed 's/ 00:00:00 +0000/ 12:00:00 +0000/' |
git hash-object -t commit -w --stdin)
git replace "$commit" "$corrected_commit"
echo "Fixed timezone for $commit"
fi
done
;;
"encoding-fix")
# Fix encoding issues in commit messages
git rev-list --all | while read -r commit; do
message=$(git show -s --format=%B "$commit")
if echo "$message" | grep -q "Ã"; then # UTF-8 encoding issue
fixed_message=$(echo "$message" | iconv -f utf-8 -t utf-8 -c)
fixed_commit=$(git show "$commit" |
sed "1,/^$/s/.*/$fixed_message/" |
git hash-object -t commit -w --stdin)
git replace "$commit" "$fixed_commit"
echo "Fixed encoding for $commit"
fi
done
;;
"merge-fix")
# Fix incorrect merge parent relationships
git rev-list --merges --all | while read -r merge_commit; do
parents=$(git show --no-patch --format=%P "$merge_commit")
parent_count=$(echo "$parents" | wc -w)
if [ "$parent_count" -gt 2 ]; then
# Octopus merge - check if all parents are valid
echo "$parents" | tr ' ' '\n' | while read -r parent; do
if ! git cat-file -t "$parent" >/dev/null 2>&1; then
echo "Invalid parent $parent in merge $merge_commit"
# Fix by removing invalid parent
valid_parents=$(echo "$parents" | tr ' ' '\n' | grep -v "$parent" | tr '\n' ' ')
git replace --graft "$merge_commit" $valid_parents
fi
done
fi
done
;;
esac
echo "Historical correction complete"
}
historical_correction "timezone-fix"
Terminal window
# Apply fixes from code review without force push
code_review_fixes() {
local reviewed_commit="$1"
echo "Applying code review fixes to $reviewed_commit"
# Get review feedback (simulated)
fixes=("remove-debug-logs" "fix-typo" "add-error-handling")
# Apply each fix
for fix in "${fixes[@]}"; do
case "$fix" in
"remove-debug-logs")
# Remove debug console.log statements
fixed_commit=$(git show "$reviewed_commit" |
sed '/console\.log/d' |
git hash-object -t commit -w --stdin)
;;
"fix-typo")
# Fix specific typo
fixed_commit=$(git show "$reviewed_commit" |
sed 's/teh/the/g' |
git hash-object -t commit -w --stdin)
;;
"add-error-handling")
# Add error handling code
# This would require more complex tree manipulation
echo "Complex fix - manual intervention required"
continue
;;
esac
# Apply the fix
git replace "$reviewed_commit" "$fixed_commit"
reviewed_commit="$fixed_commit" # Chain fixes
echo "Applied fix: $fix"
done
echo "Code review fixes applied"
echo "Original commit replaced with fixed version"
}
code_review_fixes "review123"
Terminal window
# Maintain repository integrity with replacements
integrity_maintenance() {
echo "Repository integrity maintenance"
# Find and fix corrupted objects
git fsck --full 2>&1 | grep "bad object" | while read -r line; do
bad_object=$(echo "$line" | awk '{print $3}')
# Try to reconstruct from similar objects
similar_objects=$(git cat-file --batch-check='%(objectname) %(objecttype)' |
grep "^$bad_object" | head -5)
if [ -n "$similar_objects" ]; then
# Use most recent similar object as replacement
replacement=$(echo "$similar_objects" | tail -1 | cut -d' ' -f1)
git replace "$bad_object" "$replacement"
echo "Replaced corrupted object $bad_object"
fi
done
# Fix commits with missing parents
git rev-list --all | while read -r commit; do
parents=$(git show --no-patch --format=%P "$commit")
for parent in $parents; do
if ! git cat-file -t "$parent" >/dev/null 2>&1; then
echo "Commit $commit has missing parent $parent"
# Try to find correct parent
possible_parent=$(git log --oneline --all | grep -i "$(git show -s --format=%s "$commit" | cut -d' ' -f1-3)" | head -1 | cut -d' ' -f1)
if [ -n "$possible_parent" ]; then
git replace --graft "$commit" "$possible_parent"
echo "Fixed parent for $commit"
fi
fi
done
done
# Verify fixes
if git fsck --full >/dev/null 2>&1; then
echo "✓ Repository integrity restored"
else
echo "✗ Some integrity issues remain"
fi
}
integrity_maintenance

What’s the difference between replace and rebase?

Section titled “What’s the difference between replace and rebase?”

replace substitutes individual objects transparently; rebase rewrites entire commit ranges and changes history. replace preserves published history while rebase requires force push.

How do I replace a commit with a corrected version?

Section titled “How do I replace a commit with a corrected version?”

Create the corrected commit with git commit-tree or hash-object, then use git replace to substitute it.

Yes, use git replace —graft to change parent relationships without rewriting history.

Are replacements shared with collaborators?

Section titled “Are replacements shared with collaborators?”

No, replacements are local by default. To share, push the refs/replace/* references to the remote repository.

Use git replace -d to delete the replace reference, restoring visibility of the original object.

Yes, but objects must be the same type unless -f is used. Commits, trees, blobs, and tags can all be replaced.

What’s the difference between replace and grafts?

Section titled “What’s the difference between replace and grafts?”

Grafts were the old mechanism stored in .git/info/grafts; replace is the new mechanism using refs. Use git replace —convert-graft-file to migrate.

Use git replace -l to list all replace references, or git replace —format=full -l for detailed information.

Yes, create a new commit with corrected author info and replace the original commit with it.

What’s the impact of replace on performance?

Section titled “What’s the impact of replace on performance?”

Minimal impact - Git transparently substitutes objects. Many replacements may slow some operations.

Yes, for automated fixes, but ensure replacements are tested and documented. Consider if rebase would be more appropriate.

Use git log or git show on the replaced object - Git should show the replacement content transparently.

Yes, git blame will show the replacement commit instead of the original, which can be useful for fixing blame information.

What’s the difference between replace and filter-branch?

Section titled “What’s the difference between replace and filter-branch?”

replace is surgical (affects one object), filter-branch is comprehensive (can rewrite entire repository history).

Can replace be used to remove sensitive data?

Section titled “Can replace be used to remove sensitive data?”

Yes, but it’s better to use filter-branch or BFG for removing sensitive data from entire history.

Use git replace —convert-graft-file to convert .git/info/grafts to replace references.

Replacements are per-repository. To apply the same fix to multiple repos, recreate the replacement in each repo.

Test replacements locally first, document them, and consider if they need to be shared with collaborators.

  1. Published Commit Fixes: Correct commits without breaking published history
  2. Author Information Correction: Fix author names/emails in published commits
  3. Parent Relationship Changes: Modify commit ancestry without rewriting history
  4. Object Corruption Repair: Replace corrupted objects with corrected versions
  5. Collaborative History Fixes: Apply fixes to shared repositories transparently
  6. Repository Migration Cleanup: Clean up data during repository migrations