¿Cuál es la diferencia entre `git merge` y` git merge --no-ff`?

Respuestas:

1080

El --no-ffindicador evita la git mergeejecución de un "avance rápido" si detecta que su actual HEADes un antepasado de la confirmación que está intentando fusionar. Un avance rápido es cuando, en lugar de construir una confirmación de fusión, git simplemente mueve el puntero de la rama para apuntar a la confirmación entrante. Esto ocurre comúnmente cuando se realiza un git pullsin cambios locales.

Sin embargo, ocasionalmente desea evitar que ocurra este comportamiento, generalmente porque desea mantener una topología de rama específica (por ejemplo, se está fusionando en una rama de tema y quiere asegurarse de que se vea de esa manera cuando lea el historial). Con el fin de hacer eso, se puede pasar la --no-ffbandera y git mergelo harás siempre la construcción de una fusión en lugar de avance rápido.

Del mismo modo, si desea ejecutar git pullo usar git mergepara avanzar explícitamente y desea rescatar si no puede avanzar rápidamente, puede usar la --ff-onlybandera. De esta manera, puede hacer algo como git pull --ff-onlypensar sin pensar, y luego, si se produce un error, puede regresar y decidir si desea fusionar o cambiar la base.

Lily Ballard
fuente
87
Para responder más directamente a la pregunta del OP: no siempre son diferentes, pero si lo son, está claro gitko git log --graphla fusión de avance rápido no creó una confirmación de fusión, mientras que la de avance rápido no lo hizo.
Cascabel
11
Sería bueno ampliar la razón para evitar ff: el autor mencionó que "topología de rama específica" significa que, en caso de que no sea ff, una confirmación de fusión adicional sirve como marcador de la fusión. El pros es un marcador de fusión explícito con los nombres del autor y la fusión. La desventaja es la historia no lineal que se parece a un conjunto de vías de ferrocarril convergentes. Sin embargo, un posible efecto secundario psicológico de la fusión es que los contribuyentes pierden interés debido a un proceso de revisión más largo: blog.spreedly.com/2014/06/24/…
Vlad
66
¿Sería justo decir que --no-ffuna característica para desarrollar o desarrollar para dominar es similar a fusionar una solicitud de extracción?
merlinpatt
99
@merlinpatt Claro. Si combina una solicitud de extracción en GitHub, hace el equivalente de --no-ff.
Lily Ballard
1
Esta es una buena explicación. Hombre, simplemente no hay suficientes buenas explicaciones de git para que la gente entienda por qué la historia de git limpio es realmente muy importante (¡yo incluido!)
dudewad
1037

Respuesta gráfica a esta pregunta.

Aquí hay un sitio con una explicación clara y una ilustración gráfica del uso git merge --no-ff:

diferencia entre git merge --no-ff y git merge

Hasta que vi esto, estaba completamente perdido con git. El uso le --no-ffpermite a alguien que revisa el historial ver claramente la sucursal que desprotegió para trabajar. (ese enlace apunta a la herramienta de visualización de "red" de github) Y aquí hay otra gran referencia con ilustraciones. Esta referencia complementa muy bien la primera con un mayor enfoque en aquellos menos familiarizados con git.


Información básica para novatos como yo

Si eres como yo, y no un Git-guru, mi respuesta aquí describe cómo manejar la eliminación de archivos del seguimiento de git sin eliminarlos del sistema de archivos local, lo que parece estar mal documentado pero a menudo ocurre. Otra situación nueva es obtener el código actual , que aún logra eludirme.


Ejemplo de flujo de trabajo

Actualicé un paquete a mi sitio web y tuve que volver a mis notas para ver mi flujo de trabajo; Pensé que era útil agregar un ejemplo a esta respuesta.

Mi flujo de trabajo de comandos git:

git checkout -b contact-form
(do your work on "contact-form")
git status
git commit -am  "updated form in contact module"
git checkout master
git merge --no-ff contact-form
git branch -d contact-form
git push origin master

Abajo: uso real, incluyendo explicaciones.
Nota: la salida a continuación se corta; git es bastante detallado.

$ git status
# On branch master
# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   ecc/Desktop.php
#       modified:   ecc/Mobile.php
#       deleted:    ecc/ecc-config.php
#       modified:   ecc/readme.txt
#       modified:   ecc/test.php
#       deleted:    passthru-adapter.igs
#       deleted:    shop/mickey/index.php
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       ecc/upgrade.php
#       ecc/webgility-config.php
#       ecc/webgility-config.php.bak
#       ecc/webgility-magento.php

Observe 3 cosas de arriba:
1) En la salida puede ver los cambios de la actualización del paquete ECC, incluida la adición de nuevos archivos.
2) Observe también que hay dos archivos (no en la /ecccarpeta) que eliminé independientemente de este cambio. En lugar de confundir esas eliminaciones de archivos con ecc, haré una cleanuprama diferente más tarde para reflejar la eliminación de esos archivos.
3) ¡No seguí mi flujo de trabajo! Me olvidé de git mientras intentaba que ecc volviera a funcionar.

A continuación: en lugar de hacer el todo incluido git commit -am "updated ecc package"que normalmente haría, solo quería agregar los archivos en la /ecccarpeta. Esos archivos eliminados no eran específicamente parte de mi git add, pero debido a que ya estaban rastreados en git, necesito eliminarlos del commit de esta rama:

$ git checkout -b ecc
$ git add ecc/*
$ git reset HEAD passthru-adapter.igs
$ git reset HEAD shop/mickey/index.php
Unstaged changes after reset:
M       passthru-adapter.igs
M       shop/mickey/index.php

$ git commit -m "Webgility ecc desktop connector files; integrates with Quickbooks"

$ git checkout master
D       passthru-adapter.igs
D       shop/mickey/index.php
Switched to branch 'master'
$ git merge --no-ff ecc
$ git branch -d ecc
Deleted branch ecc (was 98269a2).
$ git push origin master
Counting objects: 22, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (14/14), done.
Writing objects: 100% (14/14), 59.00 KiB, done.
Total 14 (delta 10), reused 0 (delta 0)
To [email protected]:me/mywebsite.git
   8a0d9ec..333eff5  master -> master



Script para automatizar lo anterior

Habiendo usado este proceso más de 10 veces en un día, me dediqué a escribir scripts por lotes para ejecutar los comandos, así que hice un git_update.sh <branch> <"commit message">script casi adecuado para realizar los pasos anteriores. Aquí está la fuente de Gist para ese guión.

En lugar de git commit -amseleccionar archivos de la lista "modificada" producida a través de git statusy luego pegarlos en este script. Esto sucedió porque hice docenas de ediciones pero quería varios nombres de sucursales para ayudar a agrupar los cambios.

Chris K
fuente
11
¿Todavía puede eliminar una rama de forma segura después de fusionarse con la --no-ffopción?
Andy Fleming
17
@DesignerGuy sí, puedes eliminar de forma segura la rama anterior. Piense en las ramas como solo punteros a un compromiso específico.
Zyphrax
2
Este texto de la página enlazada me pareció útil: sin --no-ff "es imposible ver en el historial de Git cuáles de los objetos de confirmación juntos han implementado una función; tendrías que leer manualmente todos los mensajes de registro".
Lorne Laliberte
¡Una imagen siempre es mejor que mil palabras!
rompe el
1
El gráfico no muestra la rama de características en la segunda imagen, ¿por qué no? Todavía existe, ¿no?
ADJenks
269

Estrategias de fusión

Fusión explícita : crea una nueva confirmación de fusión. (Esto es lo que obtendrás si lo usaste --no-ff).

ingrese la descripción de la imagen aquí

Fusión de avance rápido : avance rápidamente, sin crear una nueva confirmación:

ingrese la descripción de la imagen aquí

Rebase : establezca un nuevo nivel base:

ingrese la descripción de la imagen aquí

Squash: aplastar o exprimir (algo) con fuerza para que quede plano:

ingrese la descripción de la imagen aquí

Premraj
fuente
11
Gráfico interesante, pero en realidad no muestra el caso --no-ff y, por lo tanto, no responde a la pregunta aquí
Vib
22
La primera, la "fusión explícita", titulada aquí como "fusión", es una fusión no ff.
bkribbs
3
que los gráficos realmente me ayudaron mucho
exexzian
2
no responde la pregunta por decir, pero este gráfico es increíble, ¡VOTA!
SovietFrontier
3
Entonces, ¿cuál es la diferencia entre Rebase y combinación de avance rápido?
ThanosFisherman
217

La --no-ffopción garantiza que no se produzca una combinación de avance rápido y que siempre se creará un nuevo objeto de confirmación . Esto puede ser deseable si desea que git mantenga un historial de ramas de características.             git merge --no-ff vs git merge En la imagen de arriba, el lado izquierdo es un ejemplo del historial de git después de usar git merge --no-ffy el lado derecho es un ejemplo de uso git mergedonde era posible una fusión ff.

EDITAR : una versión anterior de esta imagen indicaba solo un padre principal para la confirmación de fusión. Las confirmaciones de fusión tienen varias confirmaciones primarias que git usa para mantener un historial de la "rama característica" y de la rama original. Los enlaces principales múltiples se resaltan en verde.

Daniel Smith
fuente
3
¿Qué indica la flecha verde frente a la flecha negra?
rtconner
11
la flecha verde indica un enlace entre un commit y un padre donde el commit tiene más de un padre. Los commits normales (negro) solo tienen un padre.
Daniel Smith
99
@DanielSmith ¿Cómo dibujas este elegante gráfico?
chancyWu
44
@chancyWu con Adobe Illustrator
Daniel Smith
Parece que en mi empresa todas las solicitudes de extracción se fusionan con la opción --no-ff. En esas circunstancias, ¿tiene sentido volver a crear una base antes de crear una solicitud de extracción?
Nickpick
37

Esta es una vieja pregunta, y esto se menciona de manera algo sutil en las otras publicaciones, pero la explicación que hizo este clic para mí es que las fusiones no rápidas requerirán una confirmación por separado .

Parris Varney
fuente
¿podría revisar el flujo de trabajo en mi respuesta anterior: esto significa que necesito confirmaciones adicionales más allá de lo que tengo ahora? Gracias.
Chris K
3
@ChrisK git merge --no-ff eccsimplemente tendrá una confirmación de fusión adicional en el git logmaestro de sucursal. Esto técnicamente no es necesario en caso de que master apunte a un antecesor directo del commit ecc, pero al especificar la opción --no-ff está forzando la creación de ese commit de fusión. Tendrá el título:Merge branch 'ecc'
Ankur Agarwal
Gran resumen! :)
Eduard
4

El indicador --no-ff hace que la fusión siempre cree un nuevo objeto de confirmación, incluso si la fusión se puede realizar con un avance rápido. Esto evita perder información sobre la existencia histórica de una rama de características y agrupa todas las confirmaciones que juntas agregaron la característica

jsina
fuente