Ninth rebase of a three-week-old branch onto main. Same file. Same forty lines of conflict markers. Same resolution I had already typed eight times, character for character.
Git watched me do it every single time and never once offered to help.
Except it can. git rerere has shipped inside git for roughly two decades, it is off by default, and almost nobody turns it on. It stands for reuse recorded resolution: git memorizes how you resolved a conflicted hunk and silently replays that resolution the next time the exact same hunk shows up.
That is the good news. The rest of this post is the part that actually matters: how git rerere decides two conflicts are "the same," why it misses conflicts you swear are identical, and the case where it confidently replays a resolution that is now wrong.
TL;DR
-
git config --global rerere.enabled trueis the whole setup. Git then records every conflict resolution you commit into.git/rr-cache/. - Next time the identical conflict appears, git resolves the file in your working tree and prints
Resolved '<file>' using previous resolution. - rerere keys on a normalized preimage of the conflicted hunk, not on the filename or the commit. Change the surrounding context lines and it becomes a different conflict with a different ID, and rerere goes quiet.
- It does not make rebase hands-free. Git still stops, the path still shows as unmerged, you still run
git addandgit rebase --continue. Addrerere.autoUpdate trueto skip thegit add. - The footgun: rerere will happily replay a stale resolution.
git rerere forget <path>while the conflict is on screen is the escape hatch.
What does git rerere actually do?
git rerere records the before-and-after of a conflict you resolved by hand, then reapplies that resolution automatically when the same conflict reappears.
Mechanically, git keeps a cache directory at .git/rr-cache/. When a merge, rebase or cherry-pick hits a conflict, git computes an ID for each conflicted hunk and writes the conflicted text to .git/rr-cache/<id>/preimage. When you finish resolving and commit, git writes your resolved text to .git/rr-cache/<id>/postimage. The mapping from conflict ID to current state also lives in .git/MERGE_RR.
Next time a conflict hashes to an ID that already has a postimage, git drops your recorded resolution straight into the working tree.
This is exactly the shape of the long-lived-branch problem. You rebase, you hit the conflict, you resolve, you rebase again a week later, the same two hunks fight again. First resolution is real work. Every replay after that is free.
How do I turn on git rerere?
One line, globally:
git config --global rerere.enabled true
Two optional settings worth knowing:
# stage rerere-resolved files automatically instead of leaving them unmerged
git config --global rerere.autoUpdate true
There is also a quiet side door: if a .git/rr-cache directory exists, git treats rerere as enabled even without the config flag. That surprises people who inherited a repo from a teammate's tarball and wondered why conflicts were pre-solving themselves.
Here is a 60-second reproduction you can paste into a scratch directory and watch work:
mkdir rerere-demo && cd rerere-demo
git init -q
git config rerere.enabled true
printf 'version = 1\n' > config.txt
git add . && git commit -qm init
git branch -M main
git switch -qc feature
printf 'version = 2-feature\n' > config.txt
git commit -qam feature
git switch -q main
printf 'version = 2-main\n' > config.txt
git commit -qam main
git merge feature # CONFLICT, as designed
printf 'version = 2-merged\n' > config.txt
git add config.txt
git commit -qm merged # <- rerere records the resolution here
git reset --hard HEAD~1 # pretend that merge never happened
git merge feature
That last git merge prints:
Resolved 'config.txt' using previous resolution.
and config.txt already contains version = 2-merged. You never retyped it.
Why does git rerere miss conflicts that look identical?
Because rerere keys on the content of the conflicted hunk plus its surrounding context, not on the file path or the branches involved. Edit a line near the conflict and you have produced a different preimage, a different ID, and rerere has no recorded resolution for it.
This is the single biggest source of "rerere is broken" complaints, and it is not a bug. The cache is keyed by what the conflict looks like. Shift the context and you get a cache miss.
Two useful consequences:
It survives merge direction. rerere normalizes the two sides of the conflict, so a resolution you recorded merging feature into main is still recognized when the same hunk shows up merging main into feature. This is why the classic "test-merge your topic branch repeatedly, throw the merge away, merge for real at the end" workflow works so well with rerere on.
It does not survive reformatting. Run Prettier, Black, or gofmt over the neighborhood and every recorded resolution in that region turns into a miss.
Three commands tell you what rerere is thinking, and you can run them mid-conflict:
git rerere status # which paths rerere has a preimage for
git rerere remaining # which paths are still yours to solve
git rerere diff # what changed between the conflict and the current state
git rerere remaining is the one I actually use. During a big rebase it tells me, in one line, which files are still real work and which ones the cache already handled.
Does git rerere make rebase automatic?
No, and this catches everyone. rerere resolves the content, it does not resolve the operation.
With plain rerere.enabled true, a rebase that hits a recorded conflict will:
- stop, exactly like a normal conflict,
- write the recorded resolution into the working tree,
- leave the path listed under Unmerged paths in
git status.
So the file on disk is already correct while git still calls it conflicted. You run git add <file> and git rebase --continue and move on.
Turning on rerere.autoUpdate stages those paths for you, which collapses the ritual to just git rebase --continue. The same behavior is available per-invocation with git merge --rerere-autoupdate and git rebase --rerere-autoupdate.
I keep autoUpdate on globally, with one habit attached: I read the Resolved '<file>' using previous resolution. lines in the scrollback before continuing. Which brings us to the dangerous part.
When does git rerere apply the wrong resolution?
When the conflicted hunk still hashes to the same ID but the correct answer has changed. rerere replays what you did last time. It has no idea whether that is still right.
The realistic version: months ago you resolved a conflict in a config block by keeping your branch's value. Since then the team decided main's value is now authoritative. The hunk is textually unchanged, so rerere matches it, replays "keep mine," and with autoUpdate on it stages the file. If you continue without reading the output, you have just silently reverted a decision.
The fix is git rerere forget:
# while the conflict is on screen
git rerere forget path/to/file
That drops the recorded resolution for the current conflict in that path and hands you the conflict markers back. If the file was already auto-resolved and staged, restore the markers first:
git checkout --conflict=merge path/to/file
git rerere forget path/to/file
Two more housekeeping facts. git gc prunes the cache on a timer, controlled by gc.rerereResolved (default 60 days for resolutions that were used) and gc.rerereUnresolved (default 15 days for conflicts you never finished). And .git/rr-cache is strictly local: it is not cloned, not pushed, not fetched. If you want a shared team cache you have to copy or symlink the directory yourself, which I would only do with a team that agrees on what "correct resolution" means.
Should you turn git rerere on?
Yes, with autoUpdate and one habit. The cost is a single config line and a directory inside .git that a gc run keeps trimmed. The benefit shows up the moment you maintain a branch that outlives a sprint, keep a long-running fork in sync, or do the test-merge-then-discard dance.
The habit: when you see Resolved '<file>' using previous resolution., glance at the diff before you continue. rerere is a cache, and like every cache the failure mode is not "no answer," it is "a confident stale answer."
So what is git rerere? git rerere is a built-in git feature that records how you resolved a merge conflict and automatically replays that resolution the next time the identical conflicted hunk appears in a merge, rebase or cherry-pick. Enable it with git config --global rerere.enabled true; add rerere.autoUpdate true so resolved paths are staged for you. It keys on the normalized content of the conflicted hunk, so changing surrounding lines makes it miss, and it will replay an out-of-date resolution without warning, which git rerere forget <path> undoes while the conflict is still open.
Written by the developer behind Preterview, an interview prep platform.
Top comments (0)