Guía9 min

git merge-base para coding agents: ancestro común, no folklore

Resumen

git merge-base elige el mejor ancestro común para un merge a 3 vías. Un agente usa --is-ancestor (0/1); no compara SHA a mano ni --fork-point + rebase autónomo. Git 2.50.1.

GitHub
Dos ramas divergen de un commit 1; merge-base de A y B es 1

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 merge-base no fusiona. El man (git-merge-base(1), Git 2.50.1 / Apple Git-155; git-scm.com/docs/git-merge-base HTTP 200, last-modified 2026-08-31; last updated in 2.43.0, 2.43.1 → 2.55.0 sin cambios) find as good common ancestors as possible for a merge: elige el mejor ancestro común para un merge a 3 vías. Un ancestro es mejor que otro si el segundo es ancestro del primero. Puede haber más de uno. HEAD, index y working tree no se mueven.

Esta guía no sustituye merge (el commit de fusión) ni diff (A...B usa el merge-base). El contrato: nombrar el ancestro, preguntar ancestría con exit 0/1, y parar en 128/129.

GitHub Docs (Branches, HTTP 200 2026-09-04; la URL vieja About comparing branches in pull requests redirige aquí): un PR muestra three-dotgit diff A...B = merge-base de ambas ramas contra el tip del topic. Two-dot (A..B) compara tips y cambia cuando avanza la base aunque el topic no se haya tocado. Un agente que quiere “qué introduce el PR” pide el merge-base; no inventa el SHA del compare de github.com.

Dos tips y un ancestro 1: merge-base A B imprime 1, no fusiona

Lo que el agente sí / no corre

QuieroComandoTrampa
Ancestro de dos tipsgit merge-base origin/main HEADgit log --oneline y adivinar
¿A es ancestro de B?git merge-base --is-ancestor A Btest "$(git merge-base A B)" = "$(git rev-parse A)"
Diff del PRgit diff origin/main...HEAD --stattwo-dot origin/main..HEAD
Todos los bases (criss-cross)git merge-base --all A B--all volcado al LLM “por si acaso”

Prohibido en autónomo:

  • --fork-point + git rebase --onto. El man: mira el reflog de <ref>. Si git gc expiró el tip viejo, falla a propósito (no devuelve el padre aleatorio que daría sin --fork-point). Un rebase autónomo reescribe SHA; ver rebase.
  • Más de dos commits sin --octopus. El man: el primero vs un commit hipotético que fusiona el resto. Verificado 2026-09-04: merge-base C B A (C encima de A; B diverge) imprimió A, no el ancestro de los tres. --octopus C B A sí imprimió el ancestro común de todos.
  • Tratar exit 1 de --is-ancestor como fallo de Git. El man: 0 = sí es ancestro; 1 = no; otro ≠0 (p. ej. 128) = error.
  • El idiom viejo A=$(git rev-parse --verify A); test "$A" = "$(git merge-base A B)". El man lo deja para scripts antiguos; in modern git es --is-ancestor.
  • --independent “para ver qué no está contenido”. El man: subconjunto mínimo de los commits dados que no se alcanzan entre sí. Dump de tips, no un ancestro.
  • Sustituir el SHA local por el compare de GitHub. Three-dot en github.com mira el remoto; tu worktree puede estar en otra base.

Sin args o un solo commit (modo default / --is-ancestor): 129 y usage. Verificado: git merge-base, git merge-base HEAD, git merge-base --is-ancestor HEAD → 129. Nombre inválido: 128, Not a valid object name. Fuera de un repo: 128, not a git repository.

Receta (60 segundos)

Solo en un worktree propio, con fetch si el ticket nombra el remoto:

git merge-base --is-ancestor origin/main HEAD   # 0 = HEAD está encima; 1 = divergió
git merge-base origin/main HEAD                 # SHA del ancestro
git diff origin/main...HEAD --stat              # lo que el topic introduce

--is-ancestor A B no imprime SHA. Verificado: ancestro real → 0 y stdout vacío; tips divergentes → 1 silencioso; NOPE HEAD → 128.

Si el ticket pide “¿puedo fast-forward B con A?”: --is-ancestor A B (A ancestro de B). No compares strings de SHA.

--is-ancestor sale 0 o 1; un nombre inválido sale 128, no un SHA

Más de dos commits y criss-cross

El man (OPERATION MODES): con dos commits, el merge-base es entre esos dos. Con más, el primero vs un merge hipotético del resto. No está necesariamente contenido en cada argumento. Eso no es git show-branch --merge-base.

--octopus: mejor ancestro común de todos. En el diagrama del man (A, B, C con 2 más antiguo que 1): merge-base A B C → 1; merge-base --octopus A B C → 2.

-a / --all: todos los best merge bases. En criss-cross (el man: 1 y 2, ninguno mejor), sin --all no está especificado cuál se imprime. Un agente que necesita ambos pide --all y parsea líneas; no asume unicidad.

--independent: no imprime bases. Lista, de los commits dados, los que no se alcanzan desde otro. Verificado: tras dos merges --no-ff cruzados, --independent M1 M2 SHA1 imprimió solo el tip que contenía al resto.

--fork-point <ref> [<commit>]: no es merge-base origin/main topic. El man: si origin/master se rebobinó (B0→B1→B2→B) y tu topic nació en B0, el merge-base normal incluye B0 (commit ajeno que la otra punta descartó). --fork-point busca B0 en el reflog. Si el reflog ya no lo tiene, falla. Un agente no “arregla” eso con el padre de B0.

Checklist

  • Worktree propio. 128 fuera del repo = para.
  • Dos commits para el ancestro; --octopus si son N-way.
  • Ancestría con --is-ancestor (0/1). Cero comparación de SHA.
  • Diff de PR = A...B (three-dot), no two-dot.
  • Cero --fork-point + rebase autónomo. Cero --all / --independent al prompt.
  • 129 = usage (faltan args). 128 = objeto o repo inválido.

FAQ

¿git merge-base A B C es el ancestro de los tres? No. El man: A vs merge hipotético de B y C. Para los tres: --octopus.

¿--all siempre imprime varios? No. Si hay un único best, una línea. En criss-cross, varios; sin --all cuál de ellos es unspecified.

¿Esto reemplaza log? No. A...B en log es la diferencia simétrica. merge-base nombra un commit (o varios con --all). No camina historia.

El curso instalar un agente cubre el loop local. Hub: comparativas y decisiones. merge-base no es merge: nombra el ancestro; no crea el commit de fusión.

Verificado 2026-09-04 contra git-merge-base(1) (Git 2.50.1 / Apple Git-155; git-scm.com/docs/git-merge-base HTTP 200, last-modified 2026-08-31), GitHub Docs “Branches” (HTTP 200; three-dot = merge-base…topic) y git-merge(1). En esta máquina: dos tips divergentes → SHA del padre común; --is-ancestor 0/1/128; sin octopus tres commits → el hipotético, no el de los tres; 0 args o 1 arg → 129; fuera del repo → 128.