Guía9 min

git merge-file para coding agents: merge a tres vías de un archivo, no de la historia

Resumen

git merge-file combina cambios de current y other sobre base y reescribe current. Exit 0 limpio, N conflictos, negativo en error. Cero autónomo sobre el worktree. Distinto de merge, merge-tree, mergetool y apply. Git 2.50.1.

GitHub
Tres archivos CURRENT BASE OTHER frente a un merge de un solo path; el agente no pisa current

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-file fusiona tres archivos de texto. El man (git-merge-file(1); git-scm.com/docs/git-merge-file HTTP 200, last-modified 2026-08-31; pie del man local Git 2.50.1.428.g0e8243, 2025-07-22; binario Git 2.50.1 / Apple Git-155): Run a three-way file merge. SYNOPSIS: git merge-file [-L <current-name> [-L <base-name> [-L <other-name>]]] [--ours|--theirs|--union] [-p|--stdout] [-q|--quiet] [--marker-size=<n>] [--[no-]diff3] [--object-id] <current> <base> <other>.

DESCRIPTION: Given three files <current>, <base> and <other>, git merge-file incorporates all changes that lead from <base> to <other> into <current>. The result ordinarily goes into <current>. Es un clon mínimo de RCS merge. No toca índice, HEAD ni historial. Solo el path de current, salvo -p.

Contrato para un coding agent: no lo lances autónomo sobre el worktree. Pisa current. Un conflicto deja markers <<<<<<< / ======= / >>>>>>> dentro del archivo vivo. --ours / --theirs / --union resuelven en silencio y salen 0: parece limpio y perdiste un lado. Si el humano pide ver el choque: -p primero. Cero dump del conflicto al contexto.

No es merge: merge une historias y, si choca, para con UU. merge-file une un archivo y no crea commit. No es merge-tree: merge-tree simula dos trees y no escribe disco. No es mergetool: mergetool abre GUI/vimdiff sobre un merge ya parado. No es apply: apply pega un diff; merge-file no consume parche.

Qué hace (y qué no)

Tres paths obligatorios. Dos args: usage: y exit 129. Verificado 2026-09-06: git merge-file current.txt base.txt → 129.

Sin conflicto, current recibe el merge y exit 0. Verificado: base = line1/line2/line3, current igual, other cambia la tercera línea a line3-other → current queda line1/line2/line3-other, exit 0.

Con el mismo segmento cambiado en current (CURRENT) y other (OTHER): warning, markers y exit 1. El man: The exit value of this program is negative on error, and the number of conflicts otherwise (truncated to 127 if there are more than that many conflicts). If the merge was clean, the exit value is 0. Un 1 no es “falló el comando”: es “hay un conflicto”. No lo trates como 0/1 booleano de merge --quiet.

Archivo inexistente: error: Could not stat missing.txt: No such file or directory, exit 255 (negativo). Fuera de repo, --object-idfatal: not a git repository, exit 128.

-p manda el resultado a stdout y no pisa current. Verificado: con conflicto, -p exit 1, current intacto. Úsalo si el humano pide “enséñame el merge”. No redirijas ese stdout al LLM.

Flags que un agente no toca

FlagManAgente
-p / --stdoutResultado a stdout; no pisa currentSolo si el humano pide ver. Cero dump
-q / --quietNo avisa conflictos; el exit sigue siendo NCero. Escondes la señal
-L <label>Hasta 3 veces: labels en markersCero autónomo
--oursConflicto → lado current; exit 0Cero. Borra other
--theirsConflicto → lado other; exit 0Cero. Borra current
--unionConflicto → ambos lados; exit 0Cero. Concatena
--diff3 / --zdiff3Markers con ||||||| baseCero dump
--object-idArgs son blobs; sin -p escribe blob y imprime SHACero autónomo
--diff-algorithm=patience/minimal/histogram/myersCero. Default myers
--marker-size=<n>Largo de <<<<<<<Cero

Verificado --ours: current queda CURRENT, exit 0. --theirs: OTHER, exit 0. --union: CURRENT y OTHER seguidos, exit 0. Un agente que “arregla” con --ours esconde el conflicto. Eso no es rerere.

--diff3 (exit 1) deja:

<<<<<<< current
CURRENT
||||||| base
line2
=======
OTHER
>>>>>>> other

Más texto, no más seguro. No lo vuelques.

--object-id exige repo. Con -p, el merge va a stdout. Sin -p, Git escribe un blob y imprime el object ID. Un agente no crea blobs “para ver”. No es hash-object ni write-tree.

Flujo seguro

  1. status porcelain. Si hay UU, el merge de historias ya paró. Ahí no entra merge-file: aborta o pide humano. merge-file no desmarca UU.
  2. Si el humano tiene tres copias sueltas (export, backup, parche a mano) y pide unir un path: copia a /tmp, corre git merge-file -p current base other. Mira el exit. 0 = limpio. ≥1 = conflictos. Negativo = error.
  3. Cero --ours/--theirs/--union para “que pase CI”.
  4. Cero merge-file sobre el path del worktree sin -p: reescribe el archivo aunque el merge sea sucio.
  5. Resolución de un merge real: humano con mergetool o el editor del PR. GitHub Docs (/en/pull-requests/reference/merge-conflicts, HTTP 200; la URL About merge conflicts 301) : un PR con conflicto no se mergea hasta que alguien elija contenido. merge-file no es ese botón.

Tres copias en /tmp; stdout del merge, current intacto

Checklist:

  • Tres paths o tres object IDs. Nunca dos args.
  • Autónomo: no. Si humano pide: -p primero.
  • Exit 0/N/negativo. N no es “falló git”.
  • Cero --ours/--theirs/--union autónomo.
  • Cero --object-id que escriba blob.
  • Markers en disco ≠ archivo resuelto. No git add.

Qué no es (tabla)

VerboUnidadEscribeConflicto
mergehistorialíndice + WTpara con UU
merge-treedos treesnada (seco)exit 1, no disco
mergetoolpaths UUGUI sobre MERGEDinteractivo
merge-fileun archivocurrent (salvo -p)markers + exit N
applyun diffpaths del parche--reject o aborta

merge-file no lee el índice. Puedes correrlo fuera de un repo (salvo --object-id). Por eso es peligroso: no hay merge --abort que deshaga el overwrite.

El agente no pisa current; el conflicto queda fuera del worktree

FAQ

¿Lo uso para resolver UU? No. UU es un merge de historial. merge-file no actualiza el índice. Tras pisar el archivo seguirías en merge a medias.

¿--ours es lo mismo que git merge -X ours? No. -X ours es estrategia de merge a nivel historial. --ours de merge-file elige líneas de un archivo y sale 0.

¿Puedo parsear los markers con el LLM? No. El man dice que el usuario edite el resultado y borre una alternativa. Meter el conflicto al prompt es filtrar código a medias y, a menudo, secretos.

¿-q sirve en CI? El exit sigue siendo el número de conflictos. -q solo tapa el warning. Prefiere -p y el código de salida, sin el cuerpo.

¿Es plumbing de merge-tree? No. merge-tree opera trees. merge-file opera archivos (o blobs con --object-id). Distinta unidad, distinto daño.

Si estás armando el agente desde cero, el curso de instalar un agente cubre el loop de tools. El hub de comparativas y decisiones agrupa el resto de verbos Git. Esta guía es la regla cuando aparecen tres copias de un path: no fusiones el worktree a ciegas.