¿Cómo puedo previsualizar una fusión en git?

397

Tengo una rama git (la línea principal, por ejemplo) y quiero fusionarme en otra rama de desarrollo. O yo?

Para decidir si realmente quiero fusionar esta rama, me gustaría ver algún tipo de vista previa de lo que hará la fusión. Preferiblemente con la capacidad de ver la lista de confirmaciones que se están aplicando.

Hasta ahora, lo mejor que se me ocurre es merge --no-ff --no-commit, y luego diff HEAD.

Glenjamin
fuente
17
Solo git mergey git reset --keep HEAD@{1}si no me gusta el resultado.
Jan Hudec
3
Tenga en cuenta que ver la lista de confirmaciones con sus diferencias no necesariamente cuenta toda la historia: si la fusión no es trivial, y especialmente si hay conflictos, el resultado real de la fusión podría ser un poco interesante.
Cascabel
2
Tu método original hace exactamente eso. El punto de mi comentario es que, si bien observar diferencias individuales está muy bien, si tiene una fusión compleja, puede terminar con resultados sorprendentes incluso si todas las confirmaciones fusionadas son buenas independientemente.
Cascabel
2
@ Jan: Por alguna razón, git reset --keep HEAD@{1}devolvió la fatal: Cannot do a keep reset in the middle of a merge.Ayuda?
moey
3
¿Por qué no hay una --previewopción en git-merge?
Gaui

Respuestas:

285

Descubrí que la solución que mejor funciona para mí es simplemente realizar la fusión y abortarla si hay conflictos . Esta sintaxis particular me parece limpia y simple. Esta es la estrategia 2 a continuación.

Sin embargo, si desea asegurarse de no estropear su rama actual, o simplemente no está listo para fusionarse, independientemente de la existencia de conflictos, simplemente cree una nueva sub-rama y fusione eso:

Estrategia 1: La forma segura: fusionar una rama temporal:

git checkout mybranch
git checkout -b mynew-temporary-branch
git merge some-other-branch

De esa manera, simplemente puede tirar la rama temporal si solo quiere ver cuáles son los conflictos. No necesita molestarse en "abortar" la fusión, y puede volver a su trabajo: simplemente vuelva a pagar 'mybranch' y no tendrá ningún código fusionado o conflictos de fusión en su sucursal.

Esto es básicamente una carrera en seco.

Estrategia 2: cuando definitivamente quieres unirte, pero solo si no hay conflictos

git checkout mybranch
git merge some-other-branch

Si git informa conflictos (y SOLO SI HAY conflictos), puede hacer lo siguiente:

git merge --abort

Si la fusión es exitosa, no puede cancelarla (solo restablecer).

Si no está listo para fusionarse, use la forma más segura anterior.

[EDITAR: 2016-noviembre - Cambié la estrategia 1 por 2, porque parece ser que la mayoría de la gente está buscando "la forma segura". La Estrategia 2 ahora es más una nota de que simplemente puede abortar la fusión si la fusión tiene conflictos con los que no está listo para lidiar. ¡Tenga en cuenta si lee los comentarios!]

Kasapo
fuente
2
+1 para la estrategia 2. Rama temporal que muestra exactamente lo que entrará al fusionarse. La estrategia 1 es lo que estoy tratando de evitar para este caso particular.
Gordolio
2
Sugiero cambiar las estrategias para que la más segura sea la primera (mi entrenamiento psíquico está llegando; la mayoría de las personas asumirían que la mejor opción sería la primera, a pesar del uso claro de la palabra "más segura") pero, aparte de eso, excelente trabajo.
paxdiablo
66
Puede hacer un git merge --no-ff --no-commitsi no desea confirmar los cambios directamente. Esto elimina la "necesidad" de una rama diferente, lo que hace que sea un poco más fácil revisar los cambios, en mi opinión.
Gerard van Helden
y si decide que no desea fusionarse en absoluto y, de hecho, prefiere escribir un poco más de código y luego comprometerse con su sucursal para que pueda implementar SOLO su sucursal en un servidor de prueba, ¿no tendría que revertir el git merge --no-ff --no-commit? Supongo que aún puedes hacer algo git merge --abortdespués si no te gusta lo que ves. ¿Incluso si la fusión no produce conflictos?
Kasapo
A la mayoría de las personas les gusta copiar y pegar y no pensar demasiado. Por lo tanto, para la Estrategia 1, quizás también agregue cómo cancelar la fusión en la rama local temporal git reset --hard HEADy luego verifique una rama diferente git checkout <different branch name>y elimine la rama temporal git delete -b <name of temporary branch>.
user3613932
402
  • git log ..otherbranch
    • lista de cambios que se fusionarán en la rama actual.
  • git diff ...otherbranch
    • difieren del ancestro común (base de fusión) a la cabeza de lo que se fusionará. Tenga en cuenta los tres puntos , que tienen un significado especial en comparación con dos puntos (ver más abajo).
  • gitk ...otherbranch
    • Representación gráfica de las ramas desde que se fusionaron la última vez.

La cadena vacía implica HEAD, así que es por eso solo en ..otherbranchlugar de HEAD..otherbranch.

Los puntos dos frente a tres tienen un significado ligeramente diferente para diff que para los comandos que enumeran revisiones (log, gitk, etc.). Para log y otros dos puntos ( a..b) significa todo lo que está dentro bpero no ay tres puntos ( a...b) significa todo lo que está en solo uno de ao b. Pero diff obras con dos revisiones y existe el caso más simple representados por dos puntos ( a..b) es simple diferencia de aa by tres puntos ( a...b) diferencia media entre ancestro común y b( git diff $(git merge-base a b)..b).

Jan Hudec
fuente
77
El tercer punto era la parte que me faltaba, ¡gracias! El enfoque de registro también funciona bien, log -p --reverse ..otherbranch parece una buena manera de ver lo que se fusionaría.
Glenjamin
1
Me faltaba git checkout masterantes de intentar esto. Me preguntaba por qué decía que todos mis cambios iban a
sobreescribirse
55
git show ..otherbranchmostrará una lista de cambios y diferencias que se fusionarán en la rama actual.
Gaui
44
Esto no es completamente exacto, especialmente para las selecciones de cereza.
void.pointer
2
@ void.pointer, git no trata las selecciones de cereza especialmente en la fusión normal, por lo que tampoco lo hace aquí. Podría escribirse una estrategia de fusión que lo haría, pero que yo sepa, nunca fue así.
Jan Hudec
19

Si eres como yo, estás buscando un equivalente a svn update -n. Lo siguiente parece hacer el truco. Tenga en cuenta que asegúrese de hacer una git fetchprimera para que su repositorio local tenga las actualizaciones apropiadas para comparar.

$ git fetch origin
$ git diff --name-status origin/master
D       TableAudit/Step0_DeleteOldFiles.sh
D       TableAudit/Step1_PopulateRawTableList.sh
A       manbuild/staff_companies.sql
M       update-all-slave-dbs.sh

o si quieres un diferencial de tu cabeza al control remoto:

$ git fetch origin
$ git diff origin/master

En mi opinión, esta solución es mucho más fácil y menos propensa a errores (y, por lo tanto, mucho menos riesgosa) que la solución principal que propone "fusionar y luego abortar".

djschny
fuente
1
debería ser $ git checkout target-branchy luego $ git diff --name-status ...branch-to-be-merged(tres puntos son esenciales, así que
escríbalos
Falta una cosa: no muestra qué archivos tendrían un conflicto: tienen el mismo marcador "M" que los archivos que se han modificado en la rama pero que se pueden combinar sin ningún conflicto.
peterflynn
Tal vez las cosas fueron diferentes en 2012, pero en estos días abortar una fusión parece bastante confiable, por lo que no creo que sea justo caracterizar esto como "mucho más fácil y menos propenso a errores" en este momento.
David Z
8

La mayoría de las respuestas aquí requieren un directorio de trabajo limpio y múltiples pasos interactivos (malos para las secuencias de comandos), o no funcionan para todos los casos, por ejemplo, fusiones anteriores que ya traen algunos de los cambios pendientes en su rama de destino, o selecciones de cereza haciendo el mismo.

Para ver realmente qué cambiaría en la masterrama si se fusionara developcon ella, en este momento:

git merge-tree $(git merge-base master develop) master develop

Como es un comando de plomería, no adivina lo que quieres decir, tienes que ser explícito. Tampoco colorea la salida ni usa su buscapersonas, por lo que el comando completo sería:

git merge-tree $(git merge-base master develop) master develop | colordiff | less -R

- https://git.seveas.net/previewing-a-merge-result.html

(gracias a David Normington por el enlace)

PD:

Si obtiene conflictos de fusión, aparecerán con los marcadores de conflicto habituales en la salida, por ejemplo:

$ git merge-tree $(git merge-base a b ) a b 
added in both
  our    100644 78981922613b2afb6025042ff6bd878ac1994e85 a
  their  100644 61780798228d17af2d34fce4cfbdf35556832472 a
@@ -1 +1,5 @@
+<<<<<<< .our
 a
+=======
+b
+>>>>>>> .their

El usuario @dreftymac hace un buen punto: esto lo hace inadecuado para las secuencias de comandos, porque no se puede captar fácilmente del código de estado. Los marcadores de conflicto pueden ser bastante diferentes dependiendo de las circunstancias (eliminado frente a modificado, etc.), lo que hace que sea difícil de asimilar también. Tener cuidado.

hraban
fuente
1
@hraban Esta parece la respuesta correcta, sin embargo, aparentemente todavía falta un elemento. Este enfoque requiere que el usuario "observe" la salida para ver si los marcadores de conflicto están presentes. ¿Tiene un enfoque que simplemente devuelve truesi hay conflictos y falsesi no hay conflictos (por ejemplo, un valor booleano que no requiere la prueba de "globo ocular" o cualquier intervención humana).
dreftymac el
1
@dreftymac no puede encontrar nada al respecto. podrías usar algo como git merge-tree ... | grep -q '^[a-z].*in both$' && echo conflict || echo safe to mergepero es fastidioso; Probablemente estoy olvidando un caso. ¿tal vez quieras comprobar los marcadores de conflicto? por ejemplo, esto probablemente no capta los conflictos de estilo "ellos borrados, los nuestros cambiados". (Acabo de comprobar y eso ni siquiera muestra marcadores de conflicto, por lo que necesita una expresión regular meticulosa para estar seguro aquí)
hraban
1
Úselo less -Rpara manejar la salida en color sin modificar su configuración.
Giacomo Alzetta
Gracias @GiacomoAlzetta, lo he actualizado.
hraban
6

Si ya obtuvo los cambios, mi favorito es:

git log ...@{u}

Eso necesita git 1.7.x, creo. La @{u}notación es una "taquigrafía" para la rama aguas arriba, por lo que es un poco más versátil que git log ...origin/master.

Nota: Si usa zsh y el elemento extendido de glog, es probable que tenga que hacer algo como:

git log ...@\{u\}
Pablo Olmos de Aguilera C.
fuente
6

Además de las respuestas existentes, se podría crear un alias para mostrar la diferencia y / o el registro antes de una fusión. Muchas respuestas omiten fetchque se haga primero antes de "previsualizar" la fusión; Este es un alias que combina estos dos pasos en uno (emulando algo similar a Mercurial hg incoming/ outgoing)

Entonces, basándose en " git log ..otherbranch", puede agregar lo siguiente a ~/.gitconfig:

...
[alias]
    # fetch and show what would be merged (use option "-p" to see patch)
    incoming = "!git remote update -p; git log ..@{u}"

Para la simetría, el siguiente alias se puede usar para mostrar lo que está comprometido y lo que se empujaría, antes de empujar:

    # what would be pushed (currently committed)
    outgoing = log @{u}..

Y luego puede ejecutar " git incoming" para mostrar muchos cambios, o " git incoming -p" para mostrar el parche (es decir, el "diff"), " git incoming --pretty=oneline", para un breve resumen, etc. Luego puede (opcionalmente) ejecutar " git pull" para En realidad fusionarse. (Sin embargo, dado que ya ha obtenido, la fusión se podría hacer directamente).

Del mismo modo, " git outgoing" muestra lo que se presionaría si tuviera que correr " git push".

Miguel
fuente
3

git log currentbranch..otherbranchle dará la lista de confirmaciones que irán a la rama actual si hace una fusión. Los argumentos habituales para iniciar sesión que dan detalles sobre los commits le darán más información.

git diff currentbranch otherbranchte dará la diferencia entre los dos commits que se convertirán en uno. Esta será una diferencia que le dará todo lo que se fusionará.

¿Ayudarían estos?

Noufal Ibrahim
fuente
2
En realidad, mal. git log otherbranch..currentbranchda una lista de confirmaciones en currentbranch . Le git diff otherbranch currentbranchda la diferencia de la versión a fusionar a la punta actual, que es tan inútil como puede ser, porque lo que desea es la diferencia de la base de fusión a la cabeza de fusión.
Jan Hudec
Gracias. He cambiado los nombres de los arboles.
Noufal Ibrahim
3

Solicitud de extracción: he utilizado la mayoría de las ideas ya enviadas, pero una que también utilizo a menudo es (especialmente si es de otro desarrollador) hacer una solicitud de extracción que proporciona una forma práctica de revisar todos los cambios en una fusión antes de que tome sitio. Sé que es GitHub, no git, pero seguro que es útil.

G-Man
fuente
2

A menos que realmente realice la fusión de una manera descartable (vea la respuesta de Kasapo), no parece haber una forma confiable de ver esto.

Dicho esto, aquí hay un método que se acerca marginalmente:

git log TARGET_BRANCH...SOURCE_BRANCH --cherry

Esto da una indicación justa de qué commits lo harán en la fusión. Para ver las diferencias, agregue -p. Para ver los nombres de archivo, añadir cualquiera de --raw, --stat, --name-only, --name-status.

El problema con el git diff TARGET_BRANCH...SOURCE_BRANCHenfoque (consulte la respuesta de Jan Hudec) es que verá diferencias para los cambios que ya se encuentran en su rama de destino si su rama de origen contiene fusiones cruzadas.

antak
fuente
1

Tal vez esto pueda ayudarle ? git-diff-tree : compara el contenido y el modo de los blobs encontrados a través de dos objetos de árbol

Chugaister
fuente
1

No quiero usar el comando git merge como precursor para revisar archivos en conflicto. No quiero hacer una fusión, quiero identificar posibles problemas antes de fusionar, problemas que la fusión automática podría ocultarme. La solución que he estado buscando es cómo hacer que git escupe una lista de archivos que se han cambiado en ambas ramas que se fusionarán en el futuro, en relación con algún antepasado común. Una vez que tenga esa lista, puedo usar otras herramientas de comparación de archivos para explorar más las cosas. He buscado varias veces, y todavía no he encontrado lo que quiero en un comando git nativo.

Aquí está mi solución, en caso de que ayude a alguien más:

En este escenario, tengo una rama llamada QA que tiene muchos cambios desde la última versión de producción. Nuestro último lanzamiento de producción está etiquetado con "15.20.1". Tengo otra rama de desarrollo llamada new_stuff que quiero fusionar en la rama QA. Tanto QA como new_stuff apuntan a confirmaciones que "siguen" (según lo informado por gitk) la etiqueta 15.20.1.

git checkout QA
git pull
git diff 15.20.1 --name-only > QA_files
git checkout new_stuff
git pull
git diff 15.20.1 --name-only > new_stuff_files
comm -12 QA_files new_stuff_files

Aquí hay algunas discusiones que explican por qué estoy interesado en apuntar a estos archivos específicos:

¿Cómo puedo confiar en Git Merge?

/software/199780/how-far-do-you-trust-automerge

Laura
fuente