Merge Algorithms, Conflict Engineering, rerere, and Custom Merge Drivers: Guided Hands-On Workflow and Core Operations
Create disposable text, rename/delete, file/directory, semantic, and repeated-conflict scenarios; inspect merge bases/index stages; abort/retry safely; and prove rerere reuse.
Learning objectives
- Preview merge outcomes with merge-tree without changing the live index/worktree.
- Inspect stage 1/2/3 entries and exact base/ours/theirs contents.
- Abort and retry a clean lab merge, then validate the chosen resolution.
- Diagnose structural conflicts from Git state rather than marker scanning.
- Record and reuse a rerere resolution while automatic staging remains disabled.
1. Create a disposable multi-repository merge laboratory
mkdir git-merge-engineering-lab
cd git-merge-engineering-lab
git --version
Each scenario below uses its own repository. This prevents one conflict, rerere cache, or merge-in-progress state from contaminating another.
2. Build a deterministic text conflict
git init -b trunk text-conflict
cd text-conflict
git config user.name "Merge Lab"
git config user.email "merge-lab@example.invalid"
printf "name=api\ntimeout_ms=100\nretries=2\n" > service.conf
git add service.conf
git commit -m "base"
BASE=$(git rev-parse HEAD)
git switch -c topic
printf "name=api\ntimeout_ms=900\nretries=2\n" > service.conf
git commit -am "topic: raise timeout"
TOPIC=$(git rev-parse HEAD)
git switch trunk
printf "name=api\ntimeout_ms=250\nretries=2\n" > service.conf
git commit -am "trunk: tune timeout"
TRUNK=$(git rev-parse HEAD)
3. Inspect base and preview the merge without live-state mutation
git status --short --branch
git log --graph --decorate --oneline --all
git merge-base trunk topic
git merge-tree --write-tree trunk topic
echo "preview exit=$?"
git status --short --branch
The preview should return conflict exit status 1 while leaving branch, index, and working tree unchanged.
4. Start the real merge and inspect stage 1/2/3
git merge --no-ff topic
echo "merge exit=$?"
git status --short
git ls-files -u -- service.conf
git rev-parse HEAD
git rev-parse MERGE_HEAD
git show :1:service.conf
git show :2:service.conf
git show :3:service.conf
Expected values are 100 in stage 1, 250 in stage 2, and 900 in stage 3. HEAD remains the trunk tip while the merge is unresolved.
5. Inspect ours/theirs in the working tree, then recreate diff3 markers
git restore --ours -- service.conf
cat service.conf
git restore --theirs -- service.conf
cat service.conf
git restore --merge --conflict=diff3 -- service.conf
cat service.conf
restore --ours uses stage 2 and
--theirs uses stage 3 in this normal merge. Rebase uses
different role semantics, so do not generalize the labels blindly
across operations.
6. Abort once on purpose
git merge --abort
git status --short --branch
git rev-parse HEAD
git rev-parse --verify MERGE_HEAD 2>/dev/null || echo "no merge in progress"
The lab started clean, so abort restores the pre-merge state. Production merges should likewise start from controlled state because abort cannot always reconstruct unrelated uncommitted work.
7. Retry, resolve, stage, test, and commit
git merge --no-ff topic || true
printf "name=api\ntimeout_ms=300\nretries=2\n" > service.conf
git add service.conf
git ls-files -u -- service.conf
test "$(sed -n 's/^timeout_ms=//p' service.conf)" -le 500
git diff --cached -- service.conf
git commit -m "merge topic: reconcile timeout"
git show -s --format='%H %P %s' HEAD
An empty unmerged-stage listing proves index resolution; the policy test proves the chosen value satisfies the lab's operational constraint.
8. Engineer a rename/delete conflict
cd ..
git init -b trunk rename-delete
cd rename-delete
git config user.name "Merge Lab"
git config user.email "merge-lab@example.invalid"
printf "legacy=true\nowner=platform\n" > legacy.conf
git add legacy.conf
git commit -m "base"
git switch -c rename-side
git mv legacy.conf current.conf
printf "migrated=true\n" >> current.conf
git add current.conf
git commit -m "rename and migrate"
git switch trunk
git rm legacy.conf
git commit -m "retire legacy configuration"
git merge-tree --write-tree trunk rename-side
echo "preview exit=$?"
git merge --no-ff rename-side
echo "merge exit=$?"
git status --short
git ls-files -u
The decision is “migration survives” versus “endpoint retired,” not merely “ours versus theirs.” Preserve status/index messages before choosing. Then abort this diagnostic scenario:
git merge --abort
cd ..
9. Engineer a file/directory conflict
git init -b trunk df-conflict
cd df-conflict
git config user.name "Merge Lab"
git config user.email "merge-lab@example.invalid"
printf "base\n" > root.txt
git add root.txt
git commit -m "base"
git switch -c file-side
printf "single-file-docs\n" > docs
git add docs
git commit -m "add docs as file"
git switch trunk
mkdir docs
printf "directory-docs\n" > docs/readme.md
git add docs/readme.md
git commit -m "add docs directory"
git merge-tree --write-tree trunk file-side
echo "preview exit=$?"
git merge --no-ff file-side
echo "merge exit=$?"
git status --short
git ls-files -u
git merge --abort
cd ..
A path cannot be both a file and a directory. Read Git's informational messages and index state because this structural conflict is not reducible to one ordinary marker block.
10. Create a mechanically clean but semantically invalid merge
git init -b trunk semantic-conflict
cd semantic-conflict
git config user.name "Merge Lab"
git config user.email "merge-lab@example.invalid"
cat > pool.conf <<'EOF'
min_workers=2
# allocator tuning
queue_depth=100
batch_size=10
backoff_ms=50
health_window=30
# upper capacity
max_workers=8
EOF
cat > check_pool.sh <<'EOF'
#!/bin/sh
min=$(sed -n 's/^min_workers=//p' pool.conf)
max=$(sed -n 's/^max_workers=//p' pool.conf)
test "$min" -le "$max"
EOF
git add .
git commit -m "base: valid worker pool"
git switch -c high-min
sed -i 's/min_workers=2/min_workers=10/' pool.conf
git commit -am "feature: require ten workers"
git switch trunk
sed -i 's/max_workers=8/max_workers=6/' pool.conf
git commit -am "operations: cap workers at six"
git merge --no-ff high-min -m "merge high-min"
echo "Git merge exit=$?"
sh check_pool.sh
echo "domain test exit=$?"
Because the edited lines are well-separated, Git should merge them cleanly. The test fails because 10 > 6. This is a semantic conflict.
sed -i 's/max_workers=6/max_workers=12/' pool.conf
sh check_pool.sh
git add pool.conf
git commit -m "fix integration: restore worker invariant"
cd ..
11. Record one resolution with rerere
git init -b trunk rerere-lab
cd rerere-lab
git config user.name "Merge Lab"
git config user.email "merge-lab@example.invalid"
git config rerere.enabled true
git config rerere.autoUpdate false
printf "color=blue\n" > theme.conf
git add theme.conf
git commit -m "base"
BASE=$(git rev-parse HEAD)
git switch -c left-one
printf "color=green\n" > theme.conf
git commit -am "left green"
git switch -c right-one "$BASE"
printf "color=orange\n" > theme.conf
git commit -am "right orange"
git switch left-one
git merge --no-ff right-one || true
git rerere status
git rerere diff
printf "color=teal\n" > theme.conf
git add theme.conf
git rerere
git commit -m "merge: choose teal"
12. Recreate the same conflict and validate the remembered result
git switch -c left-two "$BASE"
printf "color=green\n" > theme.conf
git commit -am "left2 green"
git switch -c right-two "$BASE"
printf "color=orange\n" > theme.conf
git commit -am "right2 orange"
git switch left-two
git merge --no-ff right-two || true
cat theme.conf
git ls-files -u -- theme.conf
grep -qx 'color=teal' theme.conf
git add theme.conf
git ls-files -u -- theme.conf
git commit -m "merge: validated rerere reuse"
Rerere should place color=teal in the working tree.
With autoupdate disabled, the unmerged index remains until explicit
staging.
13. Challenge — select evidence from the state model
- Which command previews modern merge machinery without altering the live worktree/index?
- Which commands reveal the exact base/ours/theirs versions of a conflicted file?
- What proves a file is no longer unmerged at the index layer?
- What kind of conflict is a clean merge whose test fails?
- What must happen before accepting a rerere-reused resolution?
14. Cleanup
cd ../..
pwd
rm -rf git-merge-engineering-lab
PowerShell equivalent:
Set-Location ../..; Remove-Item -Recurse -Force
git-merge-engineering-lab.
15. Knowledge check
Question 1. Why did HEAD not move during the unresolved text merge?
Question 2. Why can rename/delete be missed by marker-only checks?
Question 3. What proves the semantic scenario was not a textual conflict?
Question 4. Why keep rerere.autoUpdate false in this lab?
Question 5. What does
git ls-files -u show?
16. Summary
You previewed conflict computation, inspected index stages, aborted/retried, diagnosed structural conflicts, demonstrated a clean semantic failure, and proved rerere can reuse editing work without eliminating validation.
Authoritative references
git-merge
git-merge-base
git-merge-tree
git-restore
git-rerere
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.