Skip to content

merge-index Git Command Guide

The git merge-index command runs a specified merge program for all files in the Git index that need merging during merge conflicts or other index operations. It passes SHA-1 hashes, file modes, and paths to the merge program for conflict resolution.

Terminal window
git merge-index [-o] [-q] <merge-program> (-a | ( [--] <file>...) )
OptionDescription
-oSkip the check for files needing merging
-q, --quietDo not complain about failed merges
-a, --allRun merge against all files in the index that need merging
--Do not interpret any more arguments as options
ParameterDescription
<merge-program>Program to run for each merge operation
<file>...Specific files to run merge on (if not using -a)
Index Entry States:
0: Regular file (no conflict)
1: Base (common ancestor) - created during merge
2: Ours (HEAD version)
3: Theirs (MERGE_HEAD version)
merge-index processes entries with multiple versions (stages)

The merge program receives these arguments:

  1. SHA1-base - Base version (or empty string)
  2. SHA1-our - Our version
  3. SHA1-their - Their version
  4. filename - File being merged
  5. mode-base - Base file permissions
  6. mode-our - Our file permissions
  7. mode-their - Their file permissions
Terminal window
# Run merge on all files needing it
git merge-index git-merge-one-file -a
# Silent operation (don't complain about failures)
git merge-index -q git-merge-one-file -a
Terminal window
# Merge only specific conflicted files
git merge-index git-merge-one-file conflicted-file.txt another-conflict.c
# Use different merge tools for different file types
git merge-index -a -- \
git merge-one-file-script.sh
Terminal window
# Use git merge-file for three-way merging
git merge-index git merge-file -a
# Custom merge script
cat > my-merge-prog << 'EOF'
#!/bin/bash
# Arguments: $1=base-sha, $2=our-sha, $3=their-sha, $4=file, $5=mode-base, $6=mode-our, $7=mode-their
echo "Merging $4"
echo "Base: $1 ($5)"
echo "Ours: $2 ($6)"
echo "Theirs: $3 ($7)"
# Your custom merge logic here
exit 0
EOF
chmod +x my-merge-prog
git merge-index ./my-merge-prog -a
#!/bin/bash
# Smart conflict resolution based on file type
resolve_conflicts() {
local resolver_script=$(mktemp)
cat > "$resolver_script" << 'EOF'
#!/bin/bash
base_sha=$1
our_sha=$2
their_sha=$3
filename=$4
case "${filename##*.}" in
json|yml|yaml)
# Prefer our changes for config files
git cat-file -p "$our_sha" > "$filename"
echo "Resolved $filename: kept our version"
;;
md|txt|rst)
# Merge documentation with union strategy
git merge-file --union "$filename" <(git cat-file -p "$base_sha") <(git cat-file -p "$their_sha")
echo "Resolved $filename: union merge"
;;
*)
# Use standard merge for source code
if git merge-file "$filename" <(git cat-file -p "$base_sha") \
<(git cat-file -p "$our_sha") \
<(git cat-file -p "$their_sha"); then
echo "Resolved $filename: automatic merge"
else
echo "Conflict in $filename requires manual resolution"
exit 1
fi
;;
esac
EOF
chmod +x "$resolver_script"
git merge-index "$resolver_script" -a
rm "$resolver_script"
}
resolve_conflicts
Terminal window
# Show current index state for merging files
git ls-files --stage | grep "^100644 [123] " | head -10
# Count files needing merge by stage
for stage in 1 2 3; do
echo "Stage $stage files: $(git ls-files --stage | grep "^...... $stage " | wc -l)"
done
# Show conflicted files
git diff --cached --name-only | while read file; do
git ls-files --stage "$file" | awk '{print $3}' | sort | uniq | wc -l | tr -d '\n'
echo " versions of $file"
done
Terminal window
# Process large numbers of conflicts in batches
merge_in_batches() {
local batch_size="${1:-10}"
local temp_script=$(mktemp)
cat > "$temp_script" << 'EOF'
#!/bin/bash
echo "Processing merge for $4"
git merge-one-file "$4" 2>/dev/null && echo "$4" || echo "$4 needs manual merge"
EOF
chmod +x "$temp_script"
# Process files in batches
git ls-files -u | awk '{print $4}' | sort | uniq | \
while read file; do
echo "$file"
done | \
xargs -n "$batch_size" | \
while read batch; do
echo "Processing batch: $batch"
for file in $batch; do
bash "$temp_script" "" "" "" "$file"
done
echo "--- End of batch ---"
done
rm "$temp_script"
}
merge_in_batches 5
Terminal window
# Configure default merge driver
git config merge.default driver-name
# Configure merge tool for index operations
git config mergetool.default name
# Set up automatic merge for certain file types
echo "*.json merge=our-json" >> .gitattributes
git config merge.our-json.driver "cp %A %B" # Always take ours for JSON
Terminal window
# Set up custom merge driver
git config merge.jsonmerge.name "resolve JSON conflicts"
git config merge.jsonmerge.driver "jq -s '.[0] * .[1]' %O > %A"
# Use in .gitattributes
echo "*.json merge=jsonmerge" >> .gitattributes
Terminal window
# Disable auto-GC during merge operations
git config gc.auto 0
# Use parallel processing for multiple files
git merge-index -a --parallel 4 git-merge-one-file-script
# Limit merge operations
ulimit -t 300 # 5 minute timeout per merge
git merge-index timeout 300 git-merge-one-file -a
#!/bin/bash
# Validate merge will succeed before attempting
validate_index_merge() {
local merge_program="${1:-git-merge-one-file}"
local test_mode="${2:-false}"
# Create test environment
local test_dir=$(mktemp -d)
local failures=0
# Copy conflicted files to test
git ls-files -u | awk '{print $4}' | sort | uniq | \
while read file; do
mkdir -p "$test_dir/$(dirname "$file")"
cp "$file" "$test_dir/$file"
done
# Test merges
for file in "$test_dir"/*; do
if [ "$test_mode" = "true" ]; then
echo "Would test: $(basename "$file")"
continue
fi
# Create test versions
git show :1:"$file" > "$file.base" 2>/dev/null || echo "" > "$file.base"
git show :2:"$file" > "$file.ours" 2>/dev/null
git show :3:"$file" > "$file.theirs" 2>/dev/null
if "$merge_program" "" "" "" "$file" "" "" "" 2>/dev/null; then
echo "✓ $(basename "$file") would merge cleanly"
else
echo "✗ $(basename "$file") would have conflicts"
failures=$((failures + 1))
fi
rm -f "$file.base" "$file.ours" "$file.theirs"
done
rm -rf "$test_dir"
if [ "$failures" -eq 0 ]; then
echo "All files can be merged automatically"
return 0
else
echo "$failures file(s) would have merge conflicts"
return 1
fi
}
validate_index_merge
Terminal window
# Automated resolution based on rules
auto_resolve_conflicts() {
local resolution_script=$(mktemp)
cat > "$resolution_script" << 'EOF'
#!/bin/bash
base_sha=$1
our_sha=$2
their_sha=$3
filename=$4
base_mode=$5
our_mode=$6
their_mode=$7
# Rule-based resolution
case "$filename" in
# Always take our version for generated files
*.lock|*.min.js)
git cat-file -p "$our_sha" > "$filename"
echo "Resolved $filename: kept our version"
;;
# Always take their version for third-party
node_modules/*|vendor/*)
git cat-file -p "$their_sha" > "$filename"
echo "Resolved $filename: kept their version"
;;
# Use git merge-file for code files
*.c|*.cpp|*.py|*.js)
git merge-file "$filename" \
<(git cat-file -p "$base_sha" 2>/dev/null || echo "") \
<(git cat-file -p "$our_sha") \
<(git cat-file -p "$their_sha")
echo "Resolved $filename: three-way merge"
;;
# Manual handling for config files
*.json|*.yml|*.yaml)
echo "Manual resolution needed for $filename"
exit 1 # Signal manual intervention needed
;;
*)
# Default: try automatic merge
git merge-one-file "$filename" 2>/dev/null
;;
esac
EOF
chmod +x "$resolution_script"
if git merge-index "$resolution_script" -a; then
echo "All conflicts resolved automatically"
rm "$resolution_script"
return 0
else
echo "Some conflicts need manual resolution"
echo "Leftover script: $resolution_script"
return 1
fi
}
auto_resolve_conflicts
Terminal window
# Check index state
git status
git ls-files --stage | grep -v "^100644 0"
# Reset problematic index entries
git checkout --theirs conflicted-file.txt
git add conflicted-file.txt
# Verify index integrity
git fsck --full
Terminal window
# Test merge program manually
echo "" | git-merge-one-file "" "" "" test.txt "" "" ""
# Debug merge program
GIT_TRACE=1 git merge-index git-merge-one-file conflicted.txt
# Check merge program exit codes
echo "Exit codes: 0=success, 1=conflict"
Terminal window
# Check file permissions
ls -la conflicted-file.txt
git ls-files --stage conflicted-file.txt
# Fix permissions
chmod 644 conflicted-file.txt
# Handle read-only files
chmod u+w conflicted-file.txt
Terminal window
# Check file sizes
git ls-files --stage | awk '{print $4 ": " $3 " (" $1 " bytes)"}'
# Handle memory limits
ulimit -v $((1024*1024*1024)) # 1GB memory limit
# Split large merges
git merge-index git-merge-one-file -a --batch-size 10
#!/bin/bash
# Smart conflict resolution based on file analysis
SMART_RESOLVER=$(mktemp)
cat > "$SMART_RESOLVER" << 'EOF'
#!/bin/bash
base_sha=$1
our_sha=$2
their_sha=$3
filename=$4
# Analyze what changed
our_changes=$(git diff --no-index /dev/null <(git cat-file -p "$base_sha" 2>/dev/null || echo "") <(git cat-file -p "$our_sha") 2>/dev/null | wc -l || echo "0")
their_changes=$(git diff --no-index /dev/null <(git cat-file -p "$base_sha" 2>/dev/null || echo "") <(git cat-file -p "$their_sha") 2>/dev/null | wc -l || echo "0")
echo "File: $filename"
echo "Our changes: $our_changes lines"
echo "Their changes: $their_changes lines"
# Resolution strategy based on change volume
if [ "$our_changes" -gt $((their_changes * 3)) ]; then
echo "Taking our version (much larger changes)"
git cat-file -p "$our_sha" > "$filename"
elif [ "$their_changes" -gt $((our_changes * 3)) ]; then
echo "Taking their version (much larger changes)"
git cat-file -p "$their_sha" > "$filename"
else
# Similar change volume - need merge
git merge-file "$filename" \
<(git cat-file -p "$base_sha" 2>/dev/null || echo "") \
<(git cat-file -p "$our_sha") \
<(git cat-file -p "$their_sha")
echo "Attempted automatic merge"
fi
EOF
chmod +x "$SMART_RESOLVER"
git merge-index "$SMART_RESOLVER" -a
rm "$SMART_RESOLVER"
#!/bin/bash
# Generate merge operation statistics
generate_merge_report() {
local report_file="merge-report-$(date +%Y%m%d-%H%M%S).txt"
echo "=== Git Merge Report $(date) ===" > "$report_file"
echo "" >> "$report_file"
# Index state summary
echo "Index State:" >> "$report_file"
git ls-files --stage | awk '
/^100644 0/ {clean++}
/^100644 [123]/ {conflict++}
/^120000/ {symlink++}
/^160000/ {submodule++}
END {
print "Clean files:", clean
print "Conflicts:", conflict
print "Symlinks:", symlink
print "Submodules:", submodule
}
' >> "$report_file"
echo "" >> "$report_file"
# Per-file conflict details
if git ls-files -u >/dev/null 2>&1; then
echo "Files needing merge:" >> "$report_file"
git ls-files -u | awk '{print $4}' | sort | uniq | \
while read file; do
stages=$(git ls-files --stage "$file" | wc -l)
echo " $file ($stages versions)" >> "$report_file"
done
echo "" >> "$report_file"
fi
# Attempt automatic resolution
echo "Automatic Resolution Attempt:" >> "$report_file"
MERGE_TRACKER=$(mktemp)
cat > "$MERGE_TRACKER" << 'EOF'
#!/bin/bash
filename=$4
echo "Attempting: $filename"
if git merge-one-file "$filename" 2>/dev/null; then
echo "SUCCESS: $filename" >&2
else
echo "FAILED: $filename" >&2
fi
EOF
chmod +x "$MERGE_TRACKER"
success_count=$(git merge-index "$MERGE_TRACKER" -a 2>&1 | grep -c "SUCCESS:")
fail_count=$(git merge-index "$MERGE_TRACKER" -a 2>&1 | grep -c "FAILED:")
echo "Successfully resolved: $success_count" >> "$report_file"
echo "Need manual resolution: $fail_count" >> "$report_file"
rm "$MERGE_TRACKER"
echo "Report saved to: $report_file"
echo "Total conflicts resolved: $((success_count + fail_count))"
}
generate_merge_report
Terminal window
# Corporate merge validation and tracking
corporate_merge_workflow() {
local ticket_id="$1"
local change_description="$2"
echo "Starting corporate merge workflow"
echo "Ticket: $ticket_id"
echo "Description: $change_description"
# Pre-merge validation
conflicted_files=$(git ls-files -u | wc -l)
if [ "$conflicted_files" -gt 0 ]; then
echo "Warning: $conflicted_files files already in conflicted state"
return 1
fi
# Track merge operation
start_time=$(date +%s)
MERGE_AUDITOR=$(mktemp)
cat > "$MERGE_AUDITOR" << EOF
#!/bin/bash
filename=\$4
# Log merge operation
echo "\$(date): Merge operation on \$filename" >> merge-audit.log
echo " Ticket: $ticket_id" >> merge-audit.log
echo " User: \$(git config user.name) <\$(git config user.email)>" >> merge-audit.log
# Perform merge
if git merge-one-file "\$filename" 2>/dev/null; then
echo " Status: AUTOMATIC" >> merge-audit.log
echo "✓ \$filename merged automatically"
else
echo " Status: MANUAL_INTERVENTION_NEEDED" >> merge-audit.log
echo "⚠ \$filename requires manual resolution"
exit 1
fi
EOF
chmod +x "$MERGE_AUDITOR"
# Execute merge with auditing
if git merge-index "$MERGE_AUDITOR" -a; then
end_time=$(date +%s)
duration=$((end_time - start_time))
echo "Merge completed successfully in ${duration}s"
echo "Quality gate: PASSED"
# Create merge approval record
git notes add -m "Corporate approval: $ticket_id" HEAD
git notes append -m "Change: $change_description" HEAD
git notes append -m "Duration: ${duration}s, Auto-resolved: 100%" HEAD
else
echo "Merge requires manual intervention"
echo "Quality gate: REVIEW_NEEDED"
return 1
fi
rm "$MERGE_AUDITOR"
echo "Audit log: merge-audit.log"
}
# Usage
corporate_merge_workflow "PROJ-1234" "Feature implementation with security fixes"

What’s the relationship between merge-index and git merge?

Section titled “What’s the relationship between merge-index and git merge?”

git merge uses merge-index internally to process conflicted files. merge-index is the plumbing command, git merge is the porcelain interface.

How do I create custom merge programs for merge-index?

Section titled “How do I create custom merge programs for merge-index?”

Write scripts that accept 7 arguments: base-sha, our-sha, their-sha, filename, base-mode, our-mode, their-mode. Return 0 for success, non-zero for conflict.

What does the -o option do in merge-index?

Section titled “What does the -o option do in merge-index?”

Skips checking if files actually need merging. Useful for testing merge programs on all files, regardless of conflict state.

Yes, but merge programs must handle binary content appropriately. Standard merge programs are text-oriented.

Use GIT_TRACE=1 to see detailed execution. Test merge programs independently before using with merge-index.

What’s the difference between git merge-index and git merge-one-file?

Section titled “What’s the difference between git merge-index and git merge-one-file?”

merge-index orchestrates merges across multiple files using your specified program; merge-one-file is a specific program that merge-index can use.

Can merge-index process files in parallel?

Section titled “Can merge-index process files in parallel?”

Use —parallel option or external parallelization. File dependencies may limit parallelism effectiveness.

Use timeout command: timeout 300 git merge-index merge-program -a. Individual merge operations may need per-file timeouts.

What happens if a merge program crashes during merge-index?

Section titled “What happens if a merge program crashes during merge-index?”

Remaining files continue processing. Use -q to suppress error output. Failed merges remain unresolved in index.

Can merge-index work in bare repositories?

Section titled “Can merge-index work in bare repositories?”

Yes, operates on index regardless of working directory. Useful for server-side merge validation.

Applications of the git merge-index command

Section titled “Applications of the git merge-index command”
  1. Automated Merge Processing: Run custom merge logic during Git merge operations
  2. Conflict Resolution Automation: Implement intelligent conflict resolution based on file types and content
  3. Merge Validation: Test merge viability before committing changes
  4. Custom Merge Drivers: Support specialized merge strategies for different file types
  5. Enterprise Merge Auditing: Track and audit merge operations in corporate workflows
  6. Bulk Merge Operations: Process large numbers of merge conflicts systematically