Git Merge –no-ff hace copy de confirmaciones

Tuve un curioso comportamiento de git hoy, y quiero preguntar si esto es normal. Ahora, esta es la situación (las nuevas confirmaciones se agregan al final):

br1 br2 A \ B C D 

Ahora, cuando hago

 git checkout br1 git merge --no-ff br2 

Se vuelve así (al less lo que el log loggit me dice):

 br1 br2 A | \ | B | C | D | / E 

OK … Ahora, algo extraño es; ahora llamo "estado de git"; dice que estoy 4 commits antes de la twig remota. ¿Cómo está sucediendo esto? ¿No debería ser solo un compromiso por delante?

Y lo curioso es que cuando miro desde el Stash (la IU web de Git, básicamente) confirma este estado de "4 commits" y veo los mismos commits (BC y D) bajo "br1" y "br2" ambos … .

Supuse que cuando usé los parameters "–no-ff", los commits en "br2" (BCD) no se copyrán en "br1" y solo se fusionará el commit creado. ¿Me equivoco aquí?

Creo que no entiendes cómo funcionan las sucursales en Git. Las twigs son livianas , lo que significa que no se copy nada cuando se crea una nueva twig o se fusiona una twig con otra.

Una twig es una reference que apunta a una confirmación. Dado que cada compromiso está vinculado a sus compromisos previos, cuando señala un compromiso, efectivamente está señalando el historial de ese compromiso.

En su ejemplo, E es una combinación de fusión que combina dos twigs juntas, creando un solo historial. Esto significa que después de fusionar br2 en br1 , br1 (que ahora es una reference a E ) sabrá sobre la historia de br2 (que todavía es una reference a D ), porque D , C y B son parte de la historia de E

Su br1 local es una reference a E , mientras que su br1 remoto sigue siendo una reference a A Por lo tanto, hay cuatro confirmaciones ( B , C , D y E ) que son nuevas en la historia de br1 .

Actualización: el póster cambió la pregunta después de que compuse esta respuesta. Esta actualización responde a una de las preguntas que el rest de la respuesta no cubre.

OK … Ahora, algo extraño es; ahora llamo "estado de git"; dice que estoy 4 commits antes de la twig remota. ¿Cómo está sucediendo esto? ¿No debería ser solo un compromiso por delante?

Después de fusionar ( --no-ff ) los commits A y D en E , la twig local es 4 commits delante de la twig remota (supongo que la twig remota aún apunta a A ). Esto es correcto.

Vamos a contar las confirmaciones que están en la twig local (ahora apuntando a E ) y no están en la twig remota (apuntando a A ). E es obviamente uno de ellos. E , al ser un compromiso de fusión, tiene dos padres: A (que existe en la twig remota) y D (que no existe en la twig remota). Cuando git empuja E al server remoto, necesita presionar ambos padres (de lo contrario, E no es válido en el server remoto).

D requiere C (su padre) que requiere B que requiere A A ya existe en el server remoto y la cadena termina aquí. B , C , D y E no existen en el server remoto. Todos deben ser empujados para mantener la consistencia de los datos en el server remoto.


El rest de la respuesta trata la pregunta original que no estaba muy clara. El póster hizo references a git log --graph y (probablemente) Atlassian Stash . Ambas herramientas (y todas las otras interfaces git que conozco) presentan la confirmación más reciente primero en el historial de confirmación. Esta respuesta usa la misma convención.

git merge --no-ff no tiene ningún efecto en la situación que describes.

Teniendo br1 como sucursal actual, git merge br2 trae a br1 los cambios introducidos por los commit que son accesibles desde br2 y no son accesibles desde br1 . Dependiendo de la position relativa de br1 respecto a br2 , git merge puede crear una nueva confirmación (que contiene todos los cambios introducidos por las confirmaciones inalcanzables desde br1 ) o simplemente puede mover la twig br1 a una nueva position (y no crea cualquier nueva confirmación).

En su situación, br2 está detrás de br1 con 1 commit y porque A es un commit de fusión, todos los commits accesibles desde br2 también se pueden alcanzar desde br1 .


De todos modos, git merge --no-ff se usa en una situación diferente.

Digamos que tenemos este gráfico:

  B <-- br2 | C | D | E <-- br1 | ... 

La twig br2 se creó a partir de la twig br1 (ambas estaban en la confirmación E en ese momento), luego se verificó br2 y se agregaron tres nuevas confirmaciones ( D , C y B ).

En esta situación, los commands:

 git checkout br1 git merge br2 

no crees una nueva twig. La twig br1 se mueve para confirmar B (donde br2 es) y eso es todo. Ahora todas las confirmaciones que son accesibles desde br2 también son accesibles desde br1 . br2 se fusionó con éxito en br1 .

Así es como se ve el gráfico:

 br1 --> B <-- br2 | C | D | E | ... 

Este tipo de fusión se denomina fast-forward porque la twig br1 se movió "hacia adelante" desde su position anterior a una nueva position (probablemente opuesta a la twig b2 que se movió de E a B una confirmación a la vez). Solo es posible si br2 es la única ruta hija de br1 (comenzando desde E , cada confirmación tiene una confirmación de hijo único y la ruta alcanza B ).

Aquí viene --no-ff en juego. Si desea que esta fusión cree una confirmación de fusión que contenga todos los cambios introducidos por las confirmaciones D , C y B , ejecute:

 git checkout br1 git merge --no-ff br2 

La presencia de la --no-ff prohíbe el reenvío rápido de la twig b1 y fuerza la creación de una nueva confirmación de fusión. El gráfico se verá así:

  A <-- br1 |\ | B <-- br2 | | | C | | | D |/ E | ... 

Técnicamente, los 4 commits son en realidad parte de br1 . Si elimina br2 , las confirmaciones seguirán siendo parte de br1 . La diferencia se produce cuando restablece la confirmación de fusión. Si realiza un restablecimiento completo, volverá al compromiso justo antes de la fusión, es decir, E Esto es especialmente útil cuando desea deshacer. Así que sí, tienes cuatro commits por delante, pero mientras no reinicies br1 , estarás bien.

También -no-ff no es para evitar que git copie las confirmaciones. Es para mantener los commits separados del tree de br1 . De esta forma, mantendrá un historial de sucursales y el context en el que se realizaron las confirmaciones, en lugar de una gran cantidad de confirmaciones relacionadas con diferentes funciones.