Guía9 min

git rerere para coding agents: el mismo conflicto no se resuelve dos veces

Resumen

git rerere graba cómo resolviste un conflicto de merge y reaplica esa resolución cuando el mismo conflicto reaparece en rebase, merge o cherry-pick. Con rerere.enabled y autoupdate, el agente deja de re-derivar resoluciones idénticas en ramas largas y PRs apilados. Cero forget a ciegas, cero confiar en resoluciones rancias de 60 días. Git 2.50.1.

GitHub
Resolución de conflictos reutilizada: rerere graba la resolución manual una vez y la reaplica en cada rebase y merge posterior

Qué resuelve

Esta pieza se queda en la decisión práctica: qué instalar, qué riesgo agrega y cómo aplicarlo sin romper operación.

git rerere (REuse REcorded REsolution) graba el conflicto y la resolución manual la primera vez que resuelves un merge, y reaplica esa resolución automáticamente cada vez que el mismo conflicto reaparece. El man (git-rerere(1); git-scm.com/docs/git-rerere; man local Git 2.50.1.428.g0e8243, binario Git 2.50.1 / Apple Git-155): assists the developer by recording conflicted automerge results and corresponding hand resolve results on the initial manual merge, and applying previously recorded hand resolutions to their corresponding automerge results.

El caso canónico es el de las ramas tópicas largas: haces merge de main en tu rama para probar, resuelves 5 conflictos, y una semana después repites el merge y aparecen los mismos 5 conflictos. Sin rerere, los resuelves otra vez a mano (o el agente los re-deriva con un LLM, gastando tokens y arriesgando una resolución distinta). Con rerere, Git reconoce la huella del conflicto y aplica tu resolución anterior.

No es merge. merge combina ramas y produce el conflicto; rerere solo recuerda cómo lo resolviste la vez anterior. No es rebase: rebase reescribe la serie y re-expone los mismos conflictos una y otra vez —rerere es lo que hace ese ciclo tolerable. No es cherry-pick: cherry-pick puede tropezar con el mismo conflicto al portar commits entre ramas, y rerere lo resuelve igual. Y no es gc: git rerere gc poda resoluciones viejas, pero no empaqueta ni recorta objetos.

Contrato: activa rerere.enabled una vez, resuelve bien la primera vez, y deja que Git repita tu resolución. Verifica cada resolución reutilizada antes de commitear. Cero forget a ciegas. Cero asumir que una resolución de hace 60 días sigue siendo correcta.

Activar (una vez por repo o global)

Rerere viene apagado por defecto. Sin el flag, git rerere no graba nada y los subcomandos no tienen estado que mostrar:

# Por repo (recomendado para agentes: vive en .git/config del worktree)
git config rerere.enabled true

# O global para todos tus repos y worktrees
git config --global rerere.enabled true

# Recomendado: que el stage de archivos auto-resueltos sea automático
git config rerere.autoupdate true

rerere.autoupdate hace que los archivos cuya resolución fue reutilizada queden staged (git add implícito) sin intervención. Sin él, el agente debe revisar y stagear a mano —que, siendo honestos, es lo que quieres en resoluciones delicadas. Regla operativa: autoupdate en true para ramas de experimento del agente, en false (default) cuando el merge toca código crítico y quieres ojo humano en cada archivo reutilizado.

Verifica que quedó activo:

git config --get rerere.enabled   # debe imprimir: true

Ciclo de grabación y reutilización: el primer merge conflictuado se resuelve a mano y rerere reaplica esa resolución en los merges siguientes

Cómo graba: preimage, postimage y rr-cache

Cuando un merge conflicta con rerere activo, Git guarda en .git/rr-cache/ (un directorio por huella de conflicto):

  • preimage: el estado conflictuado (el archivo con marcadores <<<<<<<).
  • postimage: el archivo tal como quedó tras tu resolución + git add.
  • thisimage: el estado actual durante una resolución en curso.

La próxima vez que aparezca un conflicto con la misma huella (mismo contenido en conflicto, aunque los SHAs de los commits sean distintos), rerere copia el postimage sobre el worktree y reporta Resolved ... using previous resolution. La huella es de contenido, no de commits: funciona aunque hayas hecho rebase, amend o cherry-pick entre medias.

Tres consecuencias prácticas para agentes:

  1. La primera resolución debe ser buena. Rerere repite exactamente lo que grabó, incluyendo tus errores. Si la primera resolución metió un bug, cada reaplicación lo propaga en silencio.
  2. Es local al repo. rr-cache vive en .git/ y no se pushea, no se clona, no viaja en bundles. Cada clon, cada worktree y cada runner de CI empieza con la memoria vacía. Si tu pipeline de agentes depende de rerere, el rr-cache debe persistir en el volumen del runner o re-entrenarse.
  3. Cero volcar preimage/postimage al contexto del LLM. Son archivos enteros con marcadores; el agente solo necesita saber qué paths se auto-resolvieron (git rerere status) y el diff de la resolución (git rerere diff).

Subcomandos: la mesa de trabajo

SubcomandoQué haceCuándo lo usa el agente
git rerere (sin args)Corre la reutilización sobre el estado actual; también lo invocan merge/rebase/am automáticamenteRara vez manual; el flujo lo dispara solo
statusLista paths en conflicto cuya resolución se va a grabarTras cada merge conflictuado, para saber el scope
remainingLista conflictos NO auto-resueltos (incluye submódulos, que rerere no puede grabar)Para separar "revisar" de "ya resuelto"
diffMuestra el diff del estado actual de la resoluciónRevisar qué cambió antes de git add
forget <pathspec>Borra la resolución grabada para esos paths en el conflicto actualCuando la resolución grabada es mala y quieres re-entrenar
clearResetea el metadata si vas a abortar la resolución (git am --skip/--abort y git rebase --skip/--abort lo invocan solos)Aborto manual de un merge a medias
gcPoda grabaciones viejas: no-resueltas de +15 días, resueltas de +60 días (vía gc.rerereUnresolved / gc.rerereResolved)Higiene mensual; nunca en el loop caliente

El par que más importa en el loop del agente es status + remaining: después de un merge o rebase conflictuado, status te dice qué se va a grabar y remaining qué exige cerebro. Todo lo que salga de remaining y no esté en status es trabajo manual real.

Receta para coding agents: rebase repetido sin re-trabajo

El escenario donde rerere más tokens ahorra: el agente mantiene una rama de feature contra un main que se mueve rápido, con rebase diario.

# 0. Setup una vez por worktree
git config rerere.enabled true
git config rerere.autoupdate true

# 1. Primer rebase: conflictos reales, resolución manual (humano o agente supervisado)
git rebase origin/main
# ... resuelve, git add, git rebase --continue

# 2. Segundo rebase (main avanzó): los mismos hunks conflictan,
#    rerere los resuelve solo
git rebase origin/main
# Resolved 'src/lib/auth.ts' using previous resolution.
git rerere status    # confirma qué se auto-resolvió
git diff --cached    # revisa la resolución reutilizada ANTES de continuar
git rebase --continue

Notas de disciplina que separan un buen uso de un desastre silencioso:

  • Revisa siempre git diff --cached tras una auto-resolución. El contexto alrededor del hunk pudo cambiar en main y la resolución grabada —correcta hace dos semanas— puede ya no compilar o, peor, compilar con semántica distinta.
  • Si la resolución grabada es mala, forget y re-entrena. git rerere forget <path> borra la grabación del conflicto actual; resuelves bien, commiteas, y la próxima vez se reaplica la buena. Nunca edites .git/rr-cache/ a mano.
  • clear al abortar. Si el agente aborta un merge (git merge --abort), el metadata de rerere se limpia solo. Si aborta "a mano" (checkout + reset), corre git rerere clear para no dejar una grabación a medias que contamine la próxima.

Mapa de subcomandos: status y remaining separan lo auto-resuelto de lo manual, forget re-entrena y gc poda lo viejo

Límites: lo que rerere no puede (y lo que caduca)

  • Submódulos. Un conflicto en un gitlink no tiene postimage grabable; siempre sale en remaining y siempre es manual. Si tu repo usa submódulos, el agente debe saber que esa parte nunca se auto-resuelve.
  • Archivos binarios y renames complejos. La huella es de contenido textual del hunk; binarios y detecciones de rename frágiles no reutilizan bien.
  • Resoluciones rancias. git rerere gc poda lo no-resuelto de más de 15 días y lo resuelto de más de 60 (configurables vía gc.rerereUnresolved y gc.rerereResolved). Una resolución podada simplemente deja de aplicarse —fallo seguro hacia "conflicto manual", nunca hacia "resolución inventada". No desactives el gc para "conservar memoria": una resolución de hace 6 meses sobre código que ya cambió es el bug silencioso perfecto.
  • No cruza repos. Cada clon/worktree/runner tiene su propia memoria. Documenta en el README del repo (git config rerere.enabled true en el setup del agente) en vez de asumir que la máquina ya lo trae.

Checklist del agente

  1. git config --get rerere.enabledtrue antes de cualquier serie de rebases/merges repetidos.
  2. Primera resolución manual y revisada por humano en código crítico; esa es la que se repite.
  3. Tras cada auto-resolución: git rerere status + git diff --cached antes de --continue o commit.
  4. git rerere remaining para aislar lo que exige resolución manual real (submódulos incluidos).
  5. forget + re-entrenar ante una grabación mala; clear si abortas a mano.
  6. Nunca editar .git/rr-cache/ directamente; nunca asumir memoria compartida entre clones o CI.

FAQ

¿Rerere decide solo, sin que yo revise? No. Reaplica tu resolución anterior sobre el conflicto actual y te deja el resultado en el worktree (staged si autoupdate). El commit o el --continue siguen siendo tu decisión, y el diff merece un vistazo siempre.

¿Funciona con rebase, cherry-pick y am, o solo con merge? Con todos: merge, rebase, cherry-pick y am invocan a rerere automáticamente cuando está activo. La huella es del contenido en conflicto, no del comando que lo produjo.

¿Por qué mi runner de CI nunca auto-resuelve nada? Porque rr-cache vive en .git/ y cada clon/CI efímero empieza vacío. Persiste el directorio en caché del runner o acepta que CI siempre resuelve a mano.

¿Cada cuánto limpio las grabaciones? git rerere gc con los defaults (15/60 días) basta como higiene mensual. No lo corras en el loop caliente y no desactives el gc para acumular "memoria infinita".


Siguiente paso: si tu agente aún se instala a mano en cada máquina, el flujo guiado de /curso/instalar-agente deja el entorno listo; y si los merges duelen por otra razón (estrategia, no memoria), revisa merge y el estado limpio con status antes de cada serie.