Git rerere: Stop fighting the Same Merge Conflicts
How one config flag saved me from resolving the same conflict a dozen times during a rebase
You know that feeling when you finally fix a merge conflict? The markers are gone - done. You commit, and for a second you feel good about that completed PR.
Then you try to push. Rejected - your branch is behind. Okay, you pull. Git starts rebasing. And there it is again: the same conflict, in the same file, on the next commit. You fix it. Next commit, same conflict again. After couple of commits, you start to wonder why you even chose this job. You are fixing the same thing, over and over, like Git just forgot what you did 30s ago.
Like WTF? Surely there's a way around this. Why do I have to fix the same three lines for every commit? So git pull rebases automatically, every single time, whether you wanted it or not.
Sure, you could avoid rebase completely - do a normal merge instead, which only asks you to resolve the conflict once. But in a lot of teams that's not really an option, because a clean, linear history is the whole point of rebasing in the first place. So the real question isn't "how do I avoid rebase" - it's "why am I solving the same conflict multiple times when a git should just remember the first one."
We've all been suffering, that's the price. But no, there is a setting for this. It's been in Git since 2008, and almost nobody actually uses it - its called rerere
The scenario
Let's say you're rebasing your branch on top of develop, and config.py was changed in both places. This is what it can look like:
$ git pull --rebase origin develop
Auto-merging config.py
CONFLICT (content): Merge conflict in config.py
error: could not apply a1b2c3d... fix: update timeout value
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
Fine. You open config.py, fix the conflict markers, run git add config.py, then git rebase --continue. Rebase moves to the next commit.
$ git rebase --continue
Auto-merging config.py
CONFLICT (content): Merge conflict in config.py
error: could not apply e4f5g6h... fix: add retry logic
Same file. Same lines. Same conflict, basically. You fix it again. This repeats for every commit that touched config.py. Nine commits, nine almost-identical fixes.
Meet rerere
rerere stands for "reuse recorded resolution." It really does what the name says. Once you turn it on, Git remembers how you solved a conflict. If the same conflict shows up again later - say on commit four of a nine-commit rebase - Git just applies your old fix by itself.
Turning it on is easy:
git config --global rerere.enabled true
That's all. After this, every resolved conflict gets saved in .git/rr-cache. It's saved by the actual content of the conflict, not by branch name or commit.
Go back to the same rebase from before. First conflict, you fix it like normal, then git add config.py. Git quietly remembers this. Next commit hits the same conflict, and this time you see something new:
$ git rebase --continue
Auto-merging config.py
CONFLICT (content): Merge conflict in config.py
Resolved 'config.py' using previous resolution.
error: could not apply e4f5g6h... fix: add retry logic
See that line - Resolved 'config.py' using previous resolution? Git already applied your fix. You just check the file looks right, run git add config.py, and continue. No re-typing the same lines by hand nine times.
You can also check what rerere is about to do before trusting it, with:
git rerere diff
This shows you the resolution it's about to apply, so you can review it like a normal diff.
Two things to keep in mind, though. Git still stages the fix for you to check - it doesn't commit it blindly. Always look at the diff before you trust it. And it only works when the conflict is exactly the same as before. "Close enough" doesn't count.
But still, after you go through that commit nightmare once, rerere earned its place in my global config. I don't have to fight the same merge conflict over and over again anymore. I can just fix it once, and let Git remember it for me.