Running rerere in CI integration builds Jump to heading
Some projects assemble an integration branch by merging many topic branches together: Gitβs own seen branch is built this way, and so are release candidates in teams that collect features before shipping. Doing it by hand each night is tedious, and most of the conflicts are the same ones as the night before. A CI job can do the assembly, using a stored rerere cache to resolve every conflict a person has already resolved once. The job only needs a human when a genuinely new conflict appears, and it can say exactly which topic pair caused it. This page builds that job, within rerere conflict automation.
When to use this approach Jump to heading
- You build an integration or release-candidate branch by merging a set of topic branches.
- Topic branches are long-lived enough that their conflicts with each other recur nightly.
- People already resolve those conflicts and would rather resolve each one once.
- You understand rerereβs model; if not, start with automating repeated conflict resolution with rerere.
Step 1 β Store the rr-cache where CI can read and write it Jump to heading
The cache must persist between runs and be editable by people who resolve new conflicts. A dedicated branch holding the cache directory is simple and reviewable.
# One-time setup: an orphan branch holding the rerere cache
git switch --orphan rr-cache-store
git rm -rf . >/dev/null 2>&1 || true
mkdir rr-cache && touch rr-cache/.keep
git add rr-cache && git commit -m "Initialise shared rerere cache"
git push origin rr-cache-store
git switch main Step 2 β List the topics to integrate Jump to heading
The set and order of topic branches is configuration. Keep it in a file on main so changes to the integration plan are reviewed.
# integration/topics.txt β merged in this order
topic/new-billing-api
topic/search-ranking
topic/export-scheduling
topic/ui-redesign Step 3 β Assemble the branch with rerere enabled Jump to heading
The job restores the cache into .git/rr-cache, merges each topic, and uses rerere.autoUpdate so reused resolutions are staged without a human. Any file still conflicted after rerere runs means a new conflict.
#!/bin/sh
# ci/assemble-integration.sh
set -eu
git config rerere.enabled true
git config rerere.autoUpdate true
git config user.name integration-bot; git config user.email [email protected]
git fetch origin rr-cache-store
git archive origin/rr-cache-store rr-cache | tar -x -C "$(git rev-parse --git-dir)"
git switch -C integration origin/main
while read -r topic; do
case "$topic" in ''|\#*) continue ;; esac
if ! git merge --no-ff --no-edit "origin/$topic"; then
left=$(git diff --name-only --diff-filter=U)
if [ -n "$left" ]; then
echo "NEW CONFLICT merging $topic:"; echo "$left"
exit 1
fi
git commit --no-edit # rerere resolved everything
fi
done < integration/topics.txt
git push --force origin integration The integration branch is rebuilt from scratch every night, so force-pushing it is expected; nobody should base work on it.
β οΈ SAFETY WARNING: With
rerere.autoUpdate, reused resolutions are committed without anyone reviewing them. That is acceptable for a throwaway integration branch that only feeds testing, and not acceptable for a branch that is deployed or released. If the integration branch ships, drop autoupdate and require a person to confirm each reused resolution.
Step 4 β Turn a new conflict into a recorded resolution Jump to heading
When the job reports a new conflict, a person reproduces the merge locally with the same cache, resolves it once, and pushes the updated cache. The next nightly run reuses it.
git fetch origin rr-cache-store integration
git archive origin/rr-cache-store rr-cache | tar -x -C "$(git rev-parse --git-dir)"
git config rerere.enabled true
git switch -C integration-fix origin/main
# replay the topics up to the one that failed (the job log names it), then:
git merge origin/topic/export-scheduling
# resolve, add, commit β rerere records the resolution
git switch rr-cache-store
cp -r "$(git rev-parse --git-dir)/rr-cache/." rr-cache/
git add rr-cache && git commit -m "Record resolution: export-scheduling vs search-ranking"
git push origin rr-cache-store Step 5 β Keep the cache from growing stale Jump to heading
Topic branches change, and old resolutions stop matching anything. Prune the shared cache periodically by running a fresh assembly with an empty cache copy and keeping only the entries it used.
# Entries touched by the last successful run are the ones worth keeping
find "$(git rev-parse --git-dir)/rr-cache" -mindepth 1 -maxdepth 1 -type d -mtime +90 -print rerere updates timestamps on entries it reuses, so entries not touched in ninety days are almost certainly obsolete. Review the list and remove them from the cache branch.
Validation checklist Jump to heading
Frequently Asked Questions Jump to heading
Why store the cache on a branch instead of a CI cache? Jump to heading
CI caches are opaque and easy to lose, and people cannot push resolutions into them. A branch is versioned, reviewable, and shared between CI and humans with ordinary Git commands.
What if two topics conflict differently depending on merge order? Jump to heading
They can: merging A then B produces different conflict hunks from B then A. Keep the order fixed in the topics file, and change it only deliberately, expecting to resolve some conflicts again.
Should the integration branch run the full test suite? Jump to heading
Yes β that is the reason to build it. Conflicts are the visible problem; the integration buildβs real value is catching topics that merge cleanly but break each other.
Related Jump to heading
- Rerere Conflict Automation β the parent topic.
- Sharing rerere Resolutions Across a Team β other ways to share the cache.
- Catching Semantic Conflicts in CI β what the integration build should test.
- Running a Scheduled Release Train β a release process this job fits into.