¿Por qué git pull realiza una fusión en lugar de un rebase por defecto?

71

Considere la siguiente situación:

  • Tienes un clon de un repositorio git
  • Tiene algunas confirmaciones locales (confirmaciones que aún no se han enviado a ninguna parte)
  • El repositorio remoto tiene nuevas confirmaciones que aún no ha reconciliado.

Entonces algo como esto:

Compromisos no fusionados

Si ejecuta git pullcon la configuración predeterminada, obtendrá algo como esto:

comprometer fusión

Esto se debe a que git realizó una fusión.

Sin embargo, hay una alternativa. Puedes decirle a pull que haga un rebase:

git pull --rebase

y obtendrás esto:

rebase

En mi opinión, la versión rebajada tiene numerosas ventajas que se centran principalmente en mantener limpios tanto el código como el historial, por lo que me sorprende un poco el hecho de que git se fusiona de forma predeterminada. Sí, los hash de sus confirmaciones locales cambiarán, pero esto parece un pequeño precio a pagar por el historial más simple que obtiene a cambio.

Sin embargo, de ninguna manera estoy sugiriendo que esto es de alguna manera un defecto malo o incorrecto. Solo tengo problemas para pensar en las razones por las que la fusión podría ser la preferida por defecto. ¿Tenemos alguna idea de por qué fue elegido? ¿Hay beneficios que lo hacen más adecuado por defecto?

La principal motivación para esta pregunta es que mi empresa está tratando de establecer algunos estándares de línea de base (con suerte, más como pautas) sobre cómo organizamos y gestionamos nuestros repositorios para que sea más fácil para los desarrolladores acercarse a un repositorio con el que no han trabajado antes. Estoy interesado en argumentar que, por lo general, deberíamos cambiar la base en este tipo de situación (y probablemente recomendar a los desarrolladores que configuren su configuración global para rebase de forma predeterminada), pero si me opusiera a eso, sin duda estaría preguntando por qué rebase no t por defecto si es tan genial. Así que me pregunto si hay algo que me falta.

Se ha sugerido que esta pregunta es un duplicado de ¿Por qué tantos sitios web prefieren "git rebase" sobre "git merge"? ; sin embargo, esa pregunta es algo inversa a esta. Discute los méritos de rebase sobre fusionar, mientras que esta pregunta pregunta sobre los beneficios de fusionar sobre rebase. Las respuestas allí reflejan esto, enfocándose en los problemas de fusión y los beneficios de rebase.

jpmc26
fuente
El otro problema es que si accidentalmente se compromete con el maestro, luego realiza una extracción que se fusionó, los dos maestros pueden estar fuera de sincronización a pesar de que la fusión parece normal. Es por eso que mi alias de extracción incluye --ff-only.
Ixrec
si sourcetree alguna vez cambia su vista de gráfico para que muestre rebases / fusiones de manera diferente, ¿cambiará a fusionar?
Ewan
1
@Ewan SourceTree no debería cambiar su vista gráfica de esto. Representa con precisión el gráfico.
jpmc26
44
Para los votantes engañados, tenga en cuenta que el contenido de la respuesta que acepté definitivamente no está presente en la pregunta que afirma que es un duplicado.
jpmc26

Respuestas:

56

Es difícil saber con certeza por qué la fusión es el valor predeterminado sin tener noticias de la persona que tomó esa decisión.

Aquí hay una teoría ...

Git no puede suponer que está bien --rebase cada tirón. Escucha cómo suena eso. "Rebase cada tirón". simplemente suena mal si usa solicitudes de extracción o similar. ¿Revocarías una solicitud de extracción?

En un equipo que no solo usa Git para el control de fuente centralizado ...

  • Puede tirar desde aguas arriba y aguas abajo. Algunas personas hacen muchos esfuerzos desde aguas abajo, desde contribuyentes, etc.

  • Puede trabajar en funciones en estrecha colaboración con otros desarrolladores, extrayéndolos de ellos o de una rama de temas compartidos y, de vez en cuando, actualizándolos de manera ascendente. Si siempre cambias de base, entonces terminas cambiando el historial compartido, sin mencionar los divertidos ciclos de conflicto.

Git fue diseñado para un gran equipo altamente distribuido en el que todos no se mueven y empujan a un único repositorio central. Entonces el valor predeterminado tiene sentido.

  • Los desarrolladores que no saben cuándo está bien volver a crear una fusión se fusionarán de forma predeterminada.
  • Los desarrolladores pueden cambiar la base cuando lo deseen.
  • Los comisionados que hacen muchos tirones y tienen muchos tirones obtienen el valor predeterminado que más les conviene.

Como prueba de intención, aquí hay un enlace a un correo electrónico bien conocido de Linus Torvalds con sus puntos de vista sobre cuándo no deberían volver a redactar. Dri-devel git pull email

Si sigue todo el hilo, puede ver que un desarrollador está tirando de otro desarrollador y Linus está tirando de ambos. Él deja su opinión bastante clara. Dado que probablemente decidió los valores predeterminados de Git, esto podría explicar por qué.

Muchas personas ahora usan Git de forma centralizada, donde todos en un equipo pequeño solo extraen de un repositorio central ascendente y empujan a ese mismo control remoto. Este escenario evita algunas de las situaciones en las que un rebase no es bueno, pero generalmente no los elimina.

Sugerencia: no establezca una política de cambio del valor predeterminado. Cada vez que reúnes a Git con un gran grupo de desarrolladores, algunos de los desarrolladores no entenderán a Git tan profundamente (incluido yo mismo). Irán a Google, SO, recibirán consejos sobre libros de cocina y luego se preguntarán por qué algunas cosas no funcionan, por ejemplo, ¿por qué se git checkout --ours <path>obtiene la versión incorrecta del archivo? Siempre puede revisar su entorno local, crear alias, etc. a su gusto.

joshp
fuente
55
+1 para no cambiar el valor predeterminado: en lugar de crear su propio flujo de trabajo de git, probablemente sea mejor tomar uno que exista (por ejemplo, cualquiera de los documentados por Atlassian atlassian.com/git/tutorials/comparing-workflows/… )
Rob Church
1
Gracias por la respuesta. Lo que me faltaba era la información acerca de tirar río abajo. Además, curiosamente, el enlace que proporcionó alienta exactamente lo que me gustaría lograr: "La gente puede (y probablemente debería) rebase sus árboles privados (su propio trabajo)".
jpmc26
2
@jpmc Genial. En efecto. Rebasar árboles privados puede mantener una gran cantidad de distracción fuera del historial compartido. Lo hago. Lo importante es la claridad sobre cuándo no hacerlo.
joshp
"Algunos de los desarrolladores no entenderán a Git tan profundamente". Rebase NO es una característica súper elegante ("profunda", etc.) de Git. ¿Realmente consideras esto tan difícil de entender? Creo que esto debería ser un desencadenante para llamar a un desarrollador incompetente.
Victor Yarema
15

Si lees la página de manual de git para rebase, dice :

Rebasar (o cualquier otra forma de reescribir) una rama en la que otros han basado su trabajo es una mala idea: cualquiera que esté aguas abajo se ve obligado a corregir manualmente su historial. En esta sección se explica cómo hacer la corrección desde el punto de vista aguas abajo. La solución real, sin embargo, sería evitar rebasear el flujo ascendente en primer lugar.

Creo que eso lo dice lo suficientemente bien como una razón para no usar rebase en absoluto, y mucho menos hacerlo automáticamente para cada tirón. Algunas personas consideran que rebase es dañino . Quizás nunca debería haberse puesto en git, ya que todo lo que parece hacer es embellecer la historia, algo que no debería ser necesario en cualquier SCM cuyo único trabajo esencial es preservar la historia.

Cuando diga 'mantener limpia la historia', piense que está equivocado. Puede parecer más agradable, pero para una herramienta diseñada para mantener el historial de revisiones, es mucho más limpio mantener cada confirmación para que pueda ver lo que sucedió. Desinfectar la historia después es como pulir la pátina, haciendo que una antigüedad antigua parezca una reproducción brillante :-)

gbjbaanb
fuente
3
@Ewan IMHO "no funciona pero se ve bonito" es algo que debería reservarse exclusivamente para la industria de la moda, no para TI. En mi humilde opinión, git debería eliminarlo, ya que obviamente alienta a muchas personas a pensar que el estilo es más importante que la sustancia.
gbjbaanb
12
Discutiría su sugerencia de que mantener el historial limpio es inútil. No es. Un historial de repositorio está destinado al consumo humano y, como tal, tiene mucho valor hacerlo legible. En mi caso de uso particular, incluso tenemos la intención de que los gerentes de proyecto que no sean desarrolladores puedan leer el historial. Un historial limpio solo puede ayudar con eso, y puede ayudar a un nuevo desarrollador que nunca ha visto la base del código y tiene que sumergirse y corregir un error para comprender lo que sucedió en un área particular del código.
jpmc26
3
El código existe ante todo para ser ejecutado por una máquina (posiblemente después de ser compilado). Aplicando su filosofía anterior, concluiríamos que la legibilidad del código no importa porque ninguna cantidad de legibilidad resolverá la dificultad de comprender el código. Nunca sugerí que algo debería ser aplastado. Simplemente digo que un gráfico complejo que diverge en muchos cruces es difícil de seguir, por lo que vale la pena invertir un poco para mantener su historial bastante limpio. Tenga en cuenta también que un historial lineal es más propicio para aprovechar las herramientas de git como culpar y bisecar.
jpmc26
66
Los documentos no dicen "no rebase". Dice, "no rebajes los cambios que están en contra de otras personas ". Hay un mundo de diferencia entre esos dos. El propio Linus aconseja algunos cambios de opinión para organizar la historia, como se puede ver en el enlace de la respuesta aceptada. ¿Y cómo sabría cualquier herramienta qué confirmaciones son irrelevantes (especialmente si las herramientas tienen dificultades para analizar un historial no lineal), y cómo sabría con qué confirmaciones desea diferir (especialmente sin poder profundizar en el historial)? Estás tomando una palabra de precaución sobre una situación específica a un extremo dogmático.
jpmc26
44
Definitivamente no desea "mantener el historial de revisiones" si eso incluye cada confirmación que un desarrollador haya hecho. Siguiendo esa lógica, también debemos registrar la versión original del código cada vez que se pulsa la tecla de retroceso. Cada confirmación debe ser un cambio mínimo y completo que aún mantenga todo el proyecto en estado estable. Si luego se descubre que su confirmación original es defectuosa, es mucho mejor cambiar la base / editar / modificar la confirmación que crear otra. Sin embargo, vea también mail-archive.com/[email protected]/msg39091.html
Mikko Rantalainen
12

Tienes razón, suponiendo que solo tienes un repositorio local / modificado. Sin embargo, considere que hay una segunda PC local, por ejemplo.

Una vez que su copia local / modificada es empujada a otro lugar, el rebase arruinará esas copias. Por supuesto, podrías forzar a empujar, pero eso rápidamente se vuelve complicado. ¿Qué sucede si uno o más de estos tienen otra confirmación completamente nueva?

Como puede ver, es muy situacional, pero la estrategia base parece (para mí) mucho más práctica en casos no especiales / de colaboración.


Una segunda diferencia: la estrategia de fusión mantendrá una estructura clara y consistente en el tiempo. Después de los rebases, es muy probable que los commits más antiguos sigan los cambios más nuevos, haciendo que la historia completa y su flujo sean más difíciles de entender.

Mario
fuente
1
"Después de los rebases es muy probable que los commits más antiguos sigan los cambios más nuevos", aunque eso también sucede con las fusiones, por lo que es posible que (necesites) un gráfico bonito para darle sentido. El momento en que se realizó una confirmación podría ser mucho antes de la fusión que lo llevó a esta rama.
Steve Jessop
6

La gran razón es probablemente que el comportamiento predeterminado debería "funcionar" en repositorios públicos. Rebasar la historia de que otras personas ya se han fusionado les causará problemas. Sé que estás hablando de tu repositorio privado, pero en general, git no sabe ni le importa lo que es privado o público, por lo que el valor predeterminado elegido será el predeterminado para ambos.

Utilizo git pull --rebasemucho en mi repositorio privado, pero incluso allí tiene una desventaja potencial, que es que la historia de mi HEADya no refleja el árbol ya que realmente trabajé en él.

Entonces, para un gran ejemplo, supongamos que siempre ejecuto pruebas y me aseguro de que pasen antes de realizar una confirmación. Esto tiene menos relevancia después de hacer un git pull --rebase, porque ya no es cierto que el árbol en cada uno de mis commits haya pasado las pruebas. Siempre y cuando los cambios no interfieren de ninguna manera, y el código de saco ha sido probado, es de suponer que sería pasar las pruebas, pero no sabemos porque nunca he probado. Si la integración continua es una parte importante de su flujo de trabajo, cualquier tipo de rebase en un repositorio que se está creando CI es problemático.

Realmente no me importa esto, pero molesta a algunas personas: preferirían que su historial en git refleje el código en el que realmente trabajaron (o tal vez para cuando lo presionen, una versión simplificada de la verdad después de algún uso de "arreglo").

No sé si este problema en particular es la razón por la que Linus eligió fusionarse en lugar de rebase por defecto. Podría haber otras desventajas que no he encontrado. Pero dado que no tiene miedo de expresar su opinión en código, estoy bastante seguro de que se trata de lo que él cree que es un flujo de trabajo adecuado para las personas que no quieren pensar demasiado en ello (y especialmente aquellos que trabajan en público que un repositorio privado). Evitar líneas paralelas en el gráfico bonito, a favor de una línea recta limpia que no represente el desarrollo paralelo como sucedió, probablemente no sea su principal prioridad, aunque sea la suya :-)

Steve Jessop
fuente