¿Por qué git realiza fusiones de avance rápido por defecto?

641

Viniendo de mercurial, uso ramas para organizar características. Naturalmente, también quiero ver este flujo de trabajo en mi historia.

Comencé mi nuevo proyecto usando git y terminé mi primera función. Al fusionar la función, me di cuenta de que git usa el avance rápido, es decir, aplica mis cambios directamente a la rama maestra si es posible y se olvida de mi rama.

Entonces, para pensar en el futuro: soy el único que trabaja en este proyecto. Si uso el enfoque predeterminado de git (fusión de avance rápido), mi historial daría como resultado una rama maestra gigante. Nadie sabe que usé una rama separada para cada característica, porque al final solo tendré esa rama maestra gigante. ¿No te parecerá poco profesional?

Según este razonamiento, no quiero la fusión de avance rápido y no puedo ver por qué es el valor predeterminado. ¿Qué tiene de bueno?

Florian Pilz
fuente
38
Nota: vea también sandofsky.com/blog/git-workflow.html , y evite el ' no-ff' con sus "confirmaciones de puntos de control" que rompen la culpa o la bisecta.
VonC
15
¿te arrepientes de usar git en un proyecto de un solo hombre?
HaveAGuess
27
¡Absolutamente no! En mi carpeta de trabajo tengo 7 proyectos individuales donde uso git. Permítanme reformular eso: comencé muchos proyectos desde que hice esta pregunta y todos están versionados a través de git. Hasta donde yo sé, solo git y mercurial admiten versiones locales, lo cual es esencial para mí desde que me acostumbré. Es fácil de configurar y siempre tienes toda la historia contigo. En proyectos grupales es aún mejor, ya que puede comprometerse sin interferir con su código de trabajo en progreso. Además, uso github para compartir algunos de mis proyectos (por ejemplo, micro-optparse) donde git es un requisito.
Florian Pilz
77
@Cawas cierto, -no-ff rara vez es una buena idea, pero aún puede ayudar a mantener un historial interno de funciones mientras se registra solo una confirmación en la rama principal. Eso tiene sentido para el largo historial de características, cuando combina de vez en cuando su progresión en la rama principal.
VonC
12
Por cierto, a su pregunta de "¿No parece [un historial de rama lineal] poco profesional?". No hay nada poco profesional sobre el uso de un sistema de código fuente con sus valores predeterminados. No se trata de profesionalismo. Se trata de determinar qué filosofía de ramificación te suscribe. Por ejemplo, @VonC se vinculó al artículo de sandofsky donde defiende el uso de avance rápido como un enfoque superior. No es correcto o incorrecto, solo filosofías diferentes para contextos diferentes.
Marplesoft

Respuestas:

718

La fusión de avance rápido tiene sentido para las ramas de corta duración, pero en una historia más compleja , la fusión sin avance rápido puede hacer que la historia sea más fácil de entender y facilitar la reversión de un grupo de confirmaciones.

Advertencia : el reenvío no rápido también tiene posibles efectos secundarios. Revise https://sandofsky.com/blog/git-workflow.html , evite el 'no-ff' con sus "confirmaciones de puntos de control" que rompen la bisección o la culpa, y considere cuidadosamente si debería ser su enfoque predeterminado master.

texto alternativo
(De nvie.com , Vincent Driessen , publique " Un modelo de ramificación Git exitoso ")

Incorporando una característica terminada en desarrollo

Las funciones terminadas se pueden combinar en la rama de desarrollo para agregarlas a la próxima versión:

$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff myfeature
Updating ea1b82a..05e9557
(Summary of changes)
$ git branch -d myfeature
Deleted branch myfeature (was 05e9557).
$ git push origin develop

El --no-ffindicador 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.

Jakub Narębski también menciona la configuraciónmerge.ff :

De forma predeterminada, Git no crea una confirmación de fusión adicional al fusionar una confirmación que es descendiente de la confirmación actual. En cambio, la punta de la rama actual se reenvía rápidamente.
Cuando se establece en false, esta variable le dice a Git que cree una confirmación de fusión adicional en tal caso (equivalente a dar el--no-ff opción desde la línea de comando).
Cuando se establece en ' only', solo se permiten esas fusiones de avance rápido (equivalente a dar la --ff-onlyopción desde la línea de comando).


El avance rápido es el predeterminado porque:

  • Las ramas de corta duración son muy fáciles de crear y usar en Git
  • Las ramas de corta duración a menudo aíslan muchas confirmaciones que pueden reorganizarse libremente dentro de esa rama
  • esos commits son en realidad parte de la rama principal: una vez reorganizados, la rama principal se reenvía rápidamente para incluirlos.

Pero si anticipa un flujo de trabajo iterativo en un tema / rama de características (es decir, me fusiono, luego vuelvo a esta rama de características y agrego más confirmaciones), entonces es útil incluir solo la fusión en la rama principal, en lugar de todos los commits intermedios de la rama de características.

En este caso, puede terminar configurando este tipo de archivo de configuración :

[branch "master"]
# This is the list of cmdline options that should be added to git-merge 
# when I merge commits into the master branch.

# The option --no-commit instructs git not to commit the merge
# by default. This allows me to do some final adjustment to the commit log
# message before it gets commited. I often use this to add extra info to
# the merge message or rewrite my local branch names in the commit message
# to branch names that are more understandable to the casual reader of the git log.

# Option --no-ff instructs git to always record a merge commit, even if
# the branch being merged into can be fast-forwarded. This is often the
# case when you create a short-lived topic branch which tracks master, do
# some changes on the topic branch and then merge the changes into the
# master which remained unchanged while you were doing your work on the
# topic branch. In this case the master branch can be fast-forwarded (that
# is the tip of the master branch can be updated to point to the tip of
# the topic branch) and this is what git does by default. With --no-ff
# option set, git creates a real merge commit which records the fact that
# another branch was merged. I find this easier to understand and read in
# the log.

mergeoptions = --no-commit --no-ff

El OP agrega en los comentarios:

Veo algo de sentido en el avance rápido para ramas [de corta duración], pero convertirlo en la acción predeterminada significa que git asume que ... a menudo tiene ramas [de corta duración]. ¿Razonable?

Jefromi responde:

Creo que la vida útil de las sucursales varía mucho de un usuario a otro. Sin embargo, entre los usuarios experimentados, probablemente haya una tendencia a tener muchas más ramas de corta duración.

Para mí, una rama de corta duración es la que creo para facilitar una determinada operación (rebase, probable, o parches y pruebas rápidas), y luego eliminar inmediatamente una vez que haya terminado.
Eso significa que probablemente debería ser absorbido por la rama del tema de la que se bifurcó , y la rama del tema se fusionará como una rama. Nadie necesita saber qué hice internamente para crear la serie de confirmaciones que implementan esa característica dada.

Más en general, agrego:

realmente depende de su flujo de trabajo de desarrollo :

  • Si es lineal, una rama tiene sentido.
  • Si necesita aislar características y trabajar en ellas durante un largo período de tiempo y fusionarlas repetidamente, varias ramas tienen sentido.

Consulte "¿ Cuándo debe ramificarse? "

En realidad, cuando considera el modelo de rama Mercurial, está en su núcleo una rama por repositorio (aunque puede crear cabezas anónimas, marcadores e incluso ramas con nombre )
Consulte "Git y Mercurial - Comparar y contrastar" .

Mercurial, por defecto, utiliza líneas de código anónimas y ligeras, que en su terminología se llaman "cabezas".
Git usa ramas con nombre livianas, con mapeo inyectivo para mapear nombres de ramas en repositorio remoto a nombres de ramas de seguimiento remoto.
Git "lo obliga" a nombrar ramas (bueno, con la excepción de una sola rama sin nombre, que es una situación llamada " CABEZA separada "), pero creo que esto funciona mejor con flujos de trabajo con muchas ramas, como el flujo de trabajo de rama temática, lo que significa múltiples ramas en un solo paradigma de repositorio.

VonC
fuente
Wow eso fue rápido. ;) Conozco la opción --no-ff, pero solo descubro el avance rápido después de que arruiné mi función. Veo algún sentido en el avance rápido para las ramas de vida corta, pero convertirlo en la acción predeterminada significa que git asume que a menudo tienes ramas de vida tan corta. ¿Razonable?
Florian Pilz
@ Florian: Creo que esta es una visión razonable del proceso. He agregado un ejemplo de una configuración que configurará la forma en que desea administrar la fusión con master.
VonC
Gracias, el archivo de configuración debería ayudar a evitar tales problemas. :) Solo aplicó esta configuración localmente con "git config branch.master.mergeoptions '--no-ff'"
Florian Pilz
2
@BehrangSaeedzadeh: el rebase es otro tema (que no se ha mencionado en esta página): siempre y cuando no haya empujado su featuresucursal a un repositorio público, puede rehacerla mastertantas veces como desee. Ver stackoverflow.com/questions/5250817/…
VonC
44
@BehrangSaeedzadeh: un rebase por sí solo no hará que una historia sea lineal. Es la forma en que integra los cambios de su featurerama en lo masterque hace que dicho historial sea lineal o no. Una simple fusión de avance rápido lo hará lineal. Lo que tiene sentido si ha limpiado el historial de esa featurerama antes de la fusión de avance rápido, dejando solo confirmaciones significativas, como se menciona en stackoverflow.com/questions/7425541/… .
VonC
42

Permítanme ampliar un poco en un VonC 's respuesta muy amplia :


Primero, si lo recuerdo correctamente, el hecho de que Git por defecto no crea confirmaciones de fusión en el caso de avance rápido proviene de considerar los "repositorios iguales" de una sola rama, donde se usa la atracción mutua para sincronizar esos dos repositorios (un puede encontrar el primer flujo de trabajo como primer ejemplo en la documentación de la mayoría de los usuarios, incluido "El manual del usuario de Git" y "Control de versiones con ejemplos"). En este caso, no usa pull para fusionar una rama completamente realizada, la usa para mantenerse al día con otros trabajos. No desea tener hechos efímeros y sin importancia cuando realiza una sincronización guardada y almacenada en el repositorio, guardada para el futuro.

Tenga en cuenta que la utilidad de las ramas de características y de tener múltiples ramas en un solo repositorio se produjo solo más tarde, con un uso más extendido de VCS con un buen soporte de fusión, y al probar varios flujos de trabajo basados ​​en la fusión. Es por eso que, por ejemplo, Mercurial originalmente admitía solo una rama por repositorio (más sugerencias anónimas para rastrear ramas remotas), como se vio en revisiones anteriores de "Mercurial: la guía definitiva".


En segundo lugar, cuando se siguen las mejores prácticas de la utilización de ramas de características , a saber, que las ramas de características deben todo inicio de la versión estable (por lo general de la última versión), para ser capaz de cereza recoger y seleccionar las funciones que desea incluir seleccionando las ramas de características en combinación, se Por lo general, no están en una situación de avance rápido ... lo que hace que este problema sea discutible. Debe preocuparse por crear una verdadera fusión y no avanzar rápidamente al fusionar una primera rama (suponiendo que no coloque cambios de confirmación única directamente en 'maestro'); todas las otras fusiones posteriores son, por supuesto, en una situación que no avanza rápidamente.

HTH

Jakub Narębski
fuente
Con respecto a las ramas de características de versiones estables: ¿Qué sucede si una característica que estoy desarrollando para la próxima versión depende de los cambios para otra característica que ya he desarrollado y fusionado en master? ¿Seguramente eso significa que tengo que crear esta segunda rama de características desde master?
dOxxx
1
@dOxxx: Sí, hay excepciones, como por ejemplo, donde una rama se construye sobre la otra (ya sea directamente o después de fusionar la anterior con la maestra).
Jakub Narębski