Herramienta simple para 'aceptar la suya' o 'aceptar la mía' en un archivo completo usando git

399

No quiero una herramienta de combinación visual, y tampoco quiero tener que vi el archivo en conflicto y elegir manualmente entre HEAD (el mío) y el cambio importado (el suyo). La mayoría de las veces quiero todos sus cambios o todos los míos. Por lo general, esto se debe a que mi cambio lo hizo en sentido ascendente y está volviendo a mí mediante un tirón, pero puede modificarse ligeramente en varios lugares.

¿Existe una herramienta de línea de comando que elimine los marcadores de conflicto y elija todo de una manera u otra según mi elección? O un conjunto de comandos git que puedo usar como alias para hacer cada uno.

# accept mine
alias am="some_sequence;of;commands"
alias at="some_other_sequence;of;commands"

Hacer esto es bastante molesto. Para 'aceptar el mío' he intentado:

randy@sabotage ~/linus $ git merge test-branch
Auto-merging Makefile
CONFLICT (content): Merge conflict in Makefile
Automatic merge failed; fix conflicts and then commit the result.

randy@sabotage ~/linus $ git checkout Makefile 
error: path 'Makefile' is unmerged

andy@sabotage ~/linus $ git reset --hard HEAD Makefile 
fatal: Cannot do hard reset with paths.

¿Cómo se supone que debo deshacerme de estos marcadores de cambio?

Puedo hacer:

git reset HEAD Makefile; rm Makefile; git checkout Makefile

Pero esto parece bastante redondo, debe haber una mejor manera. Y en este punto, no estoy seguro de si git incluso piensa que ocurrió la fusión, por lo que no creo que esto necesariamente funcione.

Yendo hacia el otro lado, hacer 'aceptar el suyo' es igualmente desordenado. La única forma en que puedo resolverlo es hacer:

git show test-branch:Makefile > Makefile; git add Makefile;

Esto también me da un mensaje de confirmación en mal estado, que tiene Conflictos: Makefile en él dos veces.

¿Alguien puede señalar cómo hacer las dos acciones anteriores de una manera más simple? Gracias

nosatalian
fuente
44
Tengo que dártelo como usuario de línea de comando git de más de tres años. Esto me parece ridículamente difícil de hacer desde la memoria. Realmente debería estar integrado por defecto.
Mauvis Ledford

Respuestas:

602

La solución es muy simple. git checkout <filename>intenta extraer el archivo del índice y, por lo tanto, falla al fusionar.

Lo que debe hacer es (es decir, pagar una confirmación ):

Para verificar su propia versión , puede usar uno de:

git checkout HEAD -- <filename>

o

git checkout --ours -- <filename>

o

git show :2:<filename> > <filename> # (stage 2 is ours)

Para pagar la otra versión , puede usar uno de:

git checkout test-branch -- <filename>

o

git checkout --theirs -- <filename>

o

git show :3:<filename> > <filename> # (stage 3 is theirs)

También deberá ejecutar 'agregar' para marcarlo como resuelto:

git add <filename>
Jakub Narębski
fuente
31
Me pareció un poco extraño eso --oursy --theirssignifica exactamente lo contrario de lo que intuitivamente pensé al probar este comando ...
Joshua Muheim
66
Tenga cuidado al usarlo git show: esto omite la normalización de nueva línea.
2012
2
Esto es bueno para algunos archivos, pero cuando tiene muchos archivos en conflicto (porque se modificó la fecha en un comentario), ¿cómo lo hace?
JhovaniC
44
@Santhos: --Git lo utiliza para separar las revisiones (nombres de rama, etc.) de los nombres de ruta (nombres de archivo, directorios). Es importante si Git no puede decidir si un nombre es el nombre de la sucursal o el nombre del archivo. Esto sigue la convención POSIX (o GNU) de usar doble guión para separar las opciones de los argumentos (nombres de archivo).
Jakub Narębski
3
@Sammaron @Joshua Muheim; el theirs/ ourspuede aparecer intercambiado si está resolviendo conflictos en el contexto de una operación de rebase. Debido a que rebase funciona comprobando la rama de destino y luego los compromisos de selección de cereza de "su" rama en el objetivo, el cambio entrante ("el suyo") es de "su" rama, y ​​la rama actual es la rama de destino ("la nuestra" )
RJFalconer
93

Prueba esto:

Para aceptar sus cambios: git merge --strategy-option theirs

Para aceptar el tuyo: git merge --strategy-option ours

Siva Mandadi
fuente
55
Tenga en cuenta que esto mantendrá sus cambios para TODOS los archivos en conflicto, por lo que podría ser peligroso si se produce un conflicto inesperado.
John
3
Y puede usar esto para otros comandos merge-y como cherry-pick y rebase.
idbrii
50

Según la respuesta de Jakub, puede configurar los siguientes alias git para su comodidad:

accept-ours = "!f() { git checkout --ours -- \"${@:-.}\"; git add -u \"${@:-.}\"; }; f"
accept-theirs = "!f() { git checkout --theirs -- \"${@:-.}\"; git add -u \"${@:-.}\"; }; f"

Opcionalmente, toman una o varias rutas de archivos para resolver y predeterminadas para resolver todo en el directorio actual si no se proporciona ninguno.

Agréguelos a la [alias]sección de su ~/.gitconfigo ejecute

git config --global alias.accept-ours '!f() { git checkout --ours -- "${@:-.}"; git add -u "${@:-.}"; }; f'
git config --global alias.accept-theirs '!f() { git checkout --theirs -- "${@:-.}"; git add -u "${@:-.}"; }; f'
kynan
fuente
1
No funciona para mí ... ¿Son para bash o algún otro shell?
user456584
Estos son alias git, agréguelos a la [alias]sección en su ~.gitconfigo use git config --global accept-ours "...". Han editado mi respuesta.
kynan
2
No tienes idea de cuánto tiempo me ahorró este alias. ¡Pulgares hacia arriba!
Adam Parkin
1
@hakre Asegúrate de citar el alias, de lo contrario tu shell intentará interpretarlo. O simplemente edite manualmente su ~/.gitconfig.
kynan
1
Sintaxis de Shell para los valores predeterminados:!f() { git checkout --ours -- "${@:-.}" git add -u "${@:-.}; }; f
hasta el
17

Según la respuesta de Kynan, aquí están los mismos alias, modificados para que puedan manejar espacios y guiones iniciales en los nombres de archivo:

accept-ours = "!f() { [ -z \"$@\" ] && set - '.'; git checkout --ours -- \"$@\"; git add -u -- \"$@\"; }; f"
accept-theirs = "!f() { [ -z \"$@\" ] && set - '.'; git checkout --theirs -- \"$@\"; git add -u -- \"$@\"; }; f"
Dar
fuente
0

La situación ideal para resolver conflictos es cuando sabe de antemano de qué manera quiere resolverlos y puede pasar las opciones de estrategia de fusión recursiva -Xourso -Xtheirsrecursiva. Fuera de esto puedo ver tres escenarios:

  1. Solo desea mantener una única versión del archivo (esto probablemente solo se debe usar en archivos binarios no fusionables, ya que de lo contrario los archivos en conflicto y no en conflicto pueden no estar sincronizados entre sí).
  2. Simplemente quiere decidir todos los conflictos en una dirección particular.
  3. Debe resolver algunos conflictos manualmente y luego resolver todo el resto en una dirección particular.

Para abordar estos tres escenarios, puede agregar las siguientes líneas a su .gitconfigarchivo (o equivalente):

[merge]
  conflictstyle = diff3
[mergetool.getours]
  cmd = git-checkout --ours ${MERGED}
  trustExitCode = true
[mergetool.mergeours]
  cmd = git-merge-file --ours ${LOCAL} ${BASE} ${REMOTE} -p > ${MERGED}
  trustExitCode = true
[mergetool.keepours]
  cmd = sed -I '' -e '/^<<<<<<</d' -e '/^|||||||/,/^>>>>>>>/d' ${MERGED}
  trustExitCode = true
[mergetool.gettheirs]
  cmd = git-checkout --theirs ${MERGED}
  trustExitCode = true
[mergetool.mergetheirs]
  cmd = git-merge-file --theirs ${LOCAL} ${BASE} ${REMOTE} -p > ${MERGED}
  trustExitCode = true
[mergetool.keeptheirs]
  cmd = sed -I '' -e '/^<<<<<<</,/^=======/d' -e '/^>>>>>>>/d' ${MERGED}
  trustExitCode = true

los get(ours|theirs) herramienta solo mantiene la versión respectiva del archivo y descarta todos los cambios de la otra versión (para que no se produzca la fusión).

La merge(ours|theirs)herramienta vuelve a combinar las tres vías desde las versiones local, base y remota del archivo, eligiendo resolver conflictos en la dirección dada. Esto tiene algunas advertencias, específicamente: ignora las opciones de diferencias que se pasaron al comando de fusión (como el algoritmo y el manejo de espacios en blanco); se fusiona limpiamente de los archivos originales (por lo que cualquier cambio manual en el archivo se descarta, lo que podría ser bueno o malo); y tiene la ventaja de que no puede confundirse con los marcadores de diferencias que se supone que deben estar en el archivo.

los keep(ours|theirs) herramienta simplemente edita los marcadores de diferencias y las secciones adjuntas, detectándolos por expresión regular. Esto tiene la ventaja de que conserva las opciones de diferencia del comando de combinación y le permite resolver algunos conflictos a mano y luego resolver automáticamente el resto. Tiene la desventaja de que si hay otros marcadores de conflicto en el archivo, podría confundirse.

Todos estos se utilizan ejecutando git mergetool -t (get|merge|keep)(ours|theirs) [<filename>]donde, si <filename>no se proporciona, procesa todos los archivos en conflicto.

En términos generales, suponiendo que sepa que no hay marcadores diff para confundir la expresión regular, las keep*variantes del comando son las más poderosas. Si deja la mergetool.keepBackupopción sin establecer o verdadera, luego de la fusión puede diferenciar el *.origarchivo con el resultado de la fusión para verificar que tenga sentido. Como ejemplo, ejecuto lo siguiente después de mergetoolsolo para inspeccionar los cambios antes de confirmar:

for f in `find . -name '*.orig'`; do vimdiff $f ${f%.orig}; done

Nota : Si el merge.conflictstyleno es, diff3entonces el /^|||||||/patrón en la sedregla debe ser /^=======/en su lugar.

Parakleta
fuente