Skip to content

Build1 publisher3 min readPublished

Enabling rerere replays a recorded conflict resolution into a file Git still marks unmerged

git rerere records the conflict and the resolution you commit, then writes that resolution back on the next byte-identical clash, leaving the git add as the point where you confirm the old decision still holds.

The Engineer · Build desk

Illustration accompanying Enabling rerere replays a recorded conflict resolution into a file Git still marks unmerged

What happened

  • Setting rerere.enabled to true makes Git record every conflict it shows you as a preimage with branch labels stripped, and the resolution you commit as the postimage, both under .git/rr-cache.
  • On a later rebase of the same clash Git writes the recorded resolution into the file and says so, but leaves the path unmerged at UU rather than staging it for you.
  • Turning on rerere.autoupdate stages the replayed file too, and the rebase still stops, because rerere does not continue an operation on its own.
  • Changing either side of the conflict ends the replay: the merge conflicts plainly and Git records a new preimage, since the match is byte-for-byte on the conflicted lines.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • decision Autoupdate moves the last human checkpoint onto a byte match, and the manual git add is the cheapest place to keep that check until you have watched a replay land in a repository you know.
  • exposure A resolution that was correct when recorded gets written back into a file whose tests and purpose may have moved, because the cache never looks past the conflicting lines.
  • cost The recording cost is paid per clone: the rr-cache does not travel with a push, so a teammate or a fresh checkout starts empty and re-resolves by hand.
  • constraint You cannot plan a long rebase around rerere, since one edit touching either side of the hunk puts you back to resolving it yourself.

The stop is the point of the whole feature. On the rebase, Git prints `Resolved 'config.yml' using previous resolution.` and then leaves the path at `UU` in `git status --short` [5]. Old answer in the working tree, unmerged in the index. By default rerere writes the resolution and waits for you to inspect it [7]. The line to inspect is in the combined diff: `++timeout: 5000`, sitting under the two sides it displaced, `- timeout: 10000` and `-timeout: 500` [6].

The saving on a single commit is small; repeated hunks are the reason to turn this on. The walkthrough's example is a ten-commit branch where three commits hit the same hunk, which gives three replays, each a `git diff` and a `git add` instead of a fresh decision [8]. The `timeout` value is decided once and checked three times, so three of the four encounters with that hunk are inspections [9].

`rerere.autoupdate true` adds one more line to the transcript, `Staged 'config.yml' using previous resolution.`, and `--continue` then proceeds without a manual `add` [10]. The rebase still halts, because rerere never continues an operation on its own [11]. I would not want that configurable. What autoupdate costs you is the `git add` that used to be the moment you read the diff, and the author of the dev.to walkthrough says to leave it off until you have watched a replay in a repository you know [12].

Reversing a recorded decision takes two commands. `git rerere forget config.yml` drops the recorded resolution, and `git checkout --conflict=merge config.yml` puts the markers back, reporting `Recreated 1 merge conflict` with `ours` at `timeout: 10000` and `theirs` at `timeout: 500` [13]. Note `forget`, not `clear`; the author lists confusing the two as one of the common mistakes [21].

Matching is byte-for-byte on the conflict rerere saw [14]. Add a commit on `main` that also touches `retries`, merge again, and there is no replay at all: a plain `CONFLICT` and a new `Recorded preimage` [15]. The failure that costs you runs the other way. The conflicted lines can be identical while the right answer has moved, since the cache matches those lines and not the surrounding file or the tests [16]. The walkthrough pairs the `++` read with `grep -nE '^(<<<<<<<|=======|>>>>>>>)'` before every `--continue` [17].

Two adoption details sit in the setup. The config line is written without `--global`, deliberately, so the first repository you try it in is a repository you chose [18]. And the cache is per-clone [19]. The transcripts above come from a disposable repository on git 2.43.0 [2]. The author's own precondition is the one I would keep: rerere pays off once resolving a conflict by hand is already routine [20].

What to watch

  • The transcripts come from git 2.43.0; a later release changing the replay messages or the default would change the inspection habit built on them.
  • Any tooling that syncs .git/rr-cache between clones turns one person's trust decision into the whole team's.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories