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.
git replace Syntax:
Section titled “git replace Syntax:”git replace [-f] <object> <replacement>git replace [-f] --edit <object>git replace [-f] --graft <commit> [<parent>...]git replace [-f] --convert-graft-filegit replace -d <object>...git replace [--format=<format>] [-l [<pattern>]]Creation Options:
Section titled “Creation Options:”| Option | Description |
|---|---|
-f, --force | Overwrite existing replace reference |
--edit | Edit replacement object interactively |
--graft | Replace commit with new parent relationships |
--convert-graft-file | Convert .git/info/grafts to replace refs |
Management Options:
Section titled “Management Options:”| Option | Description |
|---|---|
-d, --delete | Delete replace references |
-l, --list | List replace references |
--format=<format> | Output format for listing |
Parameters:
Section titled “Parameters:”| Parameter | Description |
|---|---|
<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 |
Understanding Object Replacement:
Section titled “Understanding Object Replacement:”Replace Reference Mechanism:
Section titled “Replace Reference Mechanism:”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 reference2. Git commands see replacement instead of original3. Original object remains in database4. Can be removed with git replace -dObject Types and Constraints:
Section titled “Object Types and Constraints:”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 changeReplace vs Other Operations:
Section titled “Replace vs Other Operations:”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 corruptionBasic Replace Operations:
Section titled “Basic Replace Operations:”Creating Replacements:
Section titled “Creating Replacements:”# Replace one commit with anothergit replace <old-commit> <new-commit>
# Force replacement (bypass type checking)git replace -f <old-object> <new-object>
# Replace with interactively edited versiongit replace --edit <commit>
# List all replace referencesgit replace -l
# Delete a replace referencegit replace -d <original-object>Commit Replacement Examples:
Section titled “Commit Replacement Examples:”# Fix a commit messagegit show <bad-commit> > /tmp/commit.txt# Edit /tmp/commit.txt to fix the messagegit hash-object -t commit -w /tmp/commit.txt > /tmp/new-commitgit replace <bad-commit> $(cat /tmp/new-commit)
# Change author informationgit show <commit> | sed 's/Author: Old Name/Author: New Name/' | git hash-object -t commit -w - > /tmp/new-commitgit replace <commit> $(cat /tmp/new-commit)
# Fix parent relationshipsgit replace --graft <commit> <new-parent1> <new-parent2>Listing and Managing Replacements:
Section titled “Listing and Managing Replacements:”# List all replacementsgit replace -l
# List with full formatgit replace --format=short -l
# Filter by patterngit replace -l "abc*"
# Show replacement detailsgit show refs/replace/<sha1>
# Verify replacement existsgit replace --format=full -l <sha1>Advanced Replacement Scenarios:
Section titled “Advanced Replacement Scenarios:”Graft Operations:
Section titled “Graft Operations:”# 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 parentsgit replace --graft <merge-commit> <parent1> <parent2> <parent3>
# Convert grafts file to replace refsgit replace --convert-graft-fileComplex Object Replacement:
Section titled “Complex Object Replacement:”# Replace tree objectgit mktree < tree.txt > /tmp/new-treegit replace <old-tree> $(cat /tmp/new-tree)
# Replace blob objectecho "new content" | git hash-object -w --stdin > /tmp/new-blobgit replace <old-blob> $(cat /tmp/new-blob)
# Replace tag objectgit mktag < tag.txt > /tmp/new-taggit replace <old-tag> $(cat /tmp/new-tag)Batch Replacement Operations:
Section titled “Batch Replacement Operations:”# Replace multiple commitscommits_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 replacementsgit replace -d $(git replace -l)
# Conditional replacementif git cat-file -t "$object" | grep -q "commit"; then git replace "$object" "$replacement"fiConfiguration and Best Practices:
Section titled “Configuration and Best Practices:”Git Configuration for Replace:
Section titled “Git Configuration for Replace:”# Configure replace behaviorgit config advice.graftFileDeprecated false # Suppress graft warnings
# Configure reflog for replacementsgit config core.logAllRefUpdates true # Enable reflog
# Configure replacement visibilitygit config core.useReplaceRefs true # Use replace refs (default)Safe Replace Practices:
Section titled “Safe Replace Practices:”# Always backup before replacementsgit branch backup-before-replace
# Verify objects existgit cat-file -t <object> || exit 1git cat-file -t <replacement> || exit 1
# Test replacement effectsgit log --oneline <object> # Should show replacementgit log --oneline <replacement> # Should show original
# Document replacementsecho "<object> -> <replacement>: <reason>" >> .git/REPLACE_LOG
# Clean up when donegit replace -d <object>Replace Strategy Guidelines:
Section titled “Replace Strategy Guidelines:”# 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 replacementsIntegration with Development Workflows:
Section titled “Integration with Development Workflows:”Repository Surgery:
Section titled “Repository Surgery:”#!/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"Collaborative History Fixing:
Section titled “Collaborative History Fixing:”# Fix published history collaborativelycollaborative_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"Automated Quality Fixes:
Section titled “Automated Quality Fixes:”# Automated commit quality fixesautomated_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_fixesTroubleshooting Common Issues:
Section titled “Troubleshooting Common Issues:”Replacement Not Taking Effect:
Section titled “Replacement Not Taking Effect:”# Debug replacement visibilitydebug_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"Invalid Replacement Objects:
Section titled “Invalid Replacement Objects:”# Validate replacement objectsvalidate_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"Graft Conversion Issues:
Section titled “Graft Conversion Issues:”# Handle graft conversion problemsfix_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_conversionPerformance Issues with Many Replacements:
Section titled “Performance Issues with Many Replacements:”# Optimize performance with many replacementsoptimize_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_replacementsRepository Sharing Issues:
Section titled “Repository Sharing Issues:”# Handle replacements in shared repositorieshandle_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_repoReal-World Usage Examples:
Section titled “Real-World Usage Examples:”Fixing Published Commits:
Section titled “Fixing Published Commits:”#!/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"Repository Migration and Cleanup:
Section titled “Repository Migration and Cleanup:”# Clean up repository during migrationrepository_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_cleanupHistorical Data Correction:
Section titled “Historical Data Correction:”# Correct historical data without breaking buildshistorical_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"Collaborative Code Review Fixes:
Section titled “Collaborative Code Review Fixes:”# Apply fixes from code review without force pushcode_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"Repository Integrity Maintenance:
Section titled “Repository Integrity Maintenance:”# Maintain repository integrity with replacementsintegrity_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_maintenanceWhat’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
Can replace change commit parents?
Section titled “Can replace change commit parents?”Yes, use git replace —graft
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.
How do I undo a replacement?
Section titled “How do I undo a replacement?”Use git replace -d
Can replace work with any object type?
Section titled “Can replace work with any object type?”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.
How do I list all current replacements?
Section titled “How do I list all current replacements?”Use git replace -l to list all replace references, or git replace —format=full -l for detailed information.
Can replace fix author information?
Section titled “Can replace fix author 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.
Can replace be used in CI/CD pipelines?
Section titled “Can replace be used in CI/CD pipelines?”Yes, for automated fixes, but ensure replacements are tested and documented. Consider if rebase would be more appropriate.
How do I verify a replacement is working?
Section titled “How do I verify a replacement is working?”Use git log or git show on the replaced object - Git should show the replacement content transparently.
Can replace affect git blame?
Section titled “Can replace affect git blame?”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.
How do I migrate from grafts to replace?
Section titled “How do I migrate from grafts to replace?”Use git replace —convert-graft-file to convert .git/info/grafts to replace references.
Can replace work across repositories?
Section titled “Can replace work across repositories?”Replacements are per-repository. To apply the same fix to multiple repos, recreate the replacement in each repo.
What’s the safest way to use replace?
Section titled “What’s the safest way to use replace?”Test replacements locally first, document them, and consider if they need to be shared with collaborators.
Applications of the git replace command
Section titled “Applications of the git replace command”- Published Commit Fixes: Correct commits without breaking published history
- Author Information Correction: Fix author names/emails in published commits
- Parent Relationship Changes: Modify commit ancestry without rewriting history
- Object Corruption Repair: Replace corrupted objects with corrected versions
- Collaborative History Fixes: Apply fixes to shared repositories transparently
- Repository Migration Cleanup: Clean up data during repository migrations