Inspecting the rerere cache Jump to heading

Most people use rerere as a black box: enable it, and conflicts sometimes resolve themselves. That works until something goes wrong β€” a resolution is reused where it should not be, a resolution you expected is missing, the cache seems to have forgotten everything. Then you need to look inside. The cache is plain files in a directory, one subdirectory per conflict, each holding the conflicted text and the resolved text. Understanding that layout makes rerere predictable: you can see what it knows, find the entry behind a particular reuse, and edit or remove it by hand. This page walks through the structure and the commands that read it, within rerere conflict automation.

When to use this approach Jump to heading

  • rerere reused a resolution and you want to see exactly what it recorded.
  • An expected resolution was not reused and you want to know why.
  • You want to remove an entry without being in the middle of the conflict.
  • You are preparing to share the cache, as in sharing rerere resolutions across a team, and want to know what you would be sharing.

Step 1 β€” Find the cache directory Jump to heading

The cache lives in the common Git directory, shared by all worktrees of a repository.

cache="$(git rev-parse --git-common-dir)/rr-cache"
ls "$cache" | head
ls "$cache" | wc -l

Each subdirectory name is a hash β€” the conflict ID. It is computed from the conflicting hunks’ content, normalised so that which side is β€œours” and which is β€œtheirs” does not matter. The same conflict in a merge and in the reverse merge produces the same ID.

Step 2 β€” Read an entry’s files Jump to heading

An entry contains up to three kinds of files.

Files inside one rr-cache entryThe preimage holds the conflicted text with markers as rerere first saw it. The postimage holds the same region after you resolved it. A thisimage file can appear temporarily during an operation. Multiple numbered variants appear when one conflict ID has several recorded resolutions.preimageconflict with markerspostimageyour resolutionpreimage.Nvariants of thesame conflict IDan entry with a preimage but no postimage is a conflict rerere saw but never saw resolved
d="$cache/$(ls -t "$cache" | head -1)"
ls -l "$d"
sed -n '1,30p' "$d/preimage"
diff -u "$d/preimage" "$d/postimage"

The diff between preimage and postimage is the resolution as rerere will apply it: it removes the markers and keeps whatever you kept.

Step 3 β€” Find the entry behind a specific conflict Jump to heading

During a conflicted operation, git rerere status lists files rerere is tracking and git rerere remaining lists those still needing resolution. To find the entry for a file, match its conflict text against preimages.

git rerere status
git rerere remaining
# Which entry has a preimage containing this file's conflicting line?
grep -l 'max_connections' "$cache"/*/preimage* | sed 's#/preimage.*##' | sort -u

Outside an operation, search postimages for text you remember from the resolution.

grep -l 'skip_discount=invoice.is_credit_note' "$cache"/*/postimage* | xargs -n1 dirname | sort -u
How rerere uses an entry during a mergeWhen a merge produces a conflict, rerere normalises the conflicting hunks, hashes them to a conflict ID, and looks for that directory. If it has a postimage, rerere applies the preimage-to-postimage change to the working file. After you resolve a new conflict, it saves the postimage.Conflictmarkers in fileNormalise + hashconflict IDrr-cache/<ID>preimage found?Applypre β†’ post diffa changed line inside the conflict changes the ID β€” that is why near-identical conflicts sometimes miss

Step 4 β€” Understand why a resolution was not reused Jump to heading

When rerere does not reuse a resolution you expected, one of three things happened: the conflict text differs slightly (a new line inside the hunk changes the ID), the entry expired, or rerere was not enabled when you resolved it the first time.

# Was rerere enabled at all in this repository?
git config --get rerere.enabled
# Are there entries older than the default resolved-expiry of 60 days?
find "$cache" -name postimage -mtime +60 | wc -l
# Unresolved entries (preimage, no postimage) β€” conflicts never seen resolved
for e in "$cache"/*/; do [ -e "$e/postimage" ] || echo "unresolved: $(basename "$e")"; done

Expiry is controlled by gc.rerereResolved (sixty days by default) and gc.rerereUnresolved (fifteen days), applied when git gc or git rerere gc runs.

Why rerere did not reuse a resolutionIf rerere was disabled when the conflict was first resolved, nothing was recorded. If the conflict's content changed slightly, it hashes to a new ID. If the entry was older than the expiry setting when gc ran, it was pruned.Why was nothing reused?rerere was offNever recordedenable it nowconflict text changedNew conflict IDresolve once moreentry too oldPruned by gcraise gc.rerereResolvedthe second reason is normal β€” rerere is exact by design

Step 5 β€” Edit or remove entries by hand Jump to heading

Because entries are plain files, you can correct a postimage directly or remove an entry entirely. This is useful when the conflict is not in front of you, so git rerere forget cannot be used.

# Correct a recorded resolution in place
$EDITOR "$cache/<conflict-id>/postimage"

# Remove an entry entirely
rm -r "$cache/<conflict-id>"

# Prune expired entries now rather than waiting for gc
git rerere gc

⚠️ SAFETY WARNING: Editing a postimage changes what rerere will silently apply in future merges and rebases. Make the edit carefully and keep a copy of the original (cp postimage postimage.bak). Removing an entry cannot be undone without a backup of the cache directory.

Step 6 β€” Audit the cache before sharing it Jump to heading

Before copying a cache to teammates or CI, check that it contains only resolutions worth spreading. List each resolved entry with the first changed line of its resolution, which is usually enough to recognise what it is for.

for e in "$cache"/*/; do
  [ -f "$e/postimage" ] || continue
  first=$(diff "$e/preimage" "$e/postimage" | grep '^>' | head -1 | cut -c3-80)
  printf '%s  %s\n' "$(basename "$e" | cut -c1-10)" "$first"
done | sort -k2 | less

Remove anything you do not recognise or would not stand behind, and anything resolved for a branch that no longer exists. A small, trusted cache spreads good resolutions; a large, unexamined one spreads old mistakes.

Validation checklist Jump to heading

Frequently Asked Questions Jump to heading

Is the cache shared between clones? Jump to heading

No. Each clone has its own rr-cache. Worktrees of the same clone share it. Sharing between clones requires copying the directory or training from merge commits.

Can the same conflict have more than one recorded resolution? Jump to heading

Yes. If you resolve the same conflict ID differently in different contexts, rerere stores numbered variants and tries to pick the one whose preimage matches the current conflict exactly.

Does rerere store whole files? Jump to heading

No, only the conflicted regions with enough context to identify them. That is why the cache stays small even after years of use.