¿Cuándo puede ser perjudicial “w20> pull”?

Tengo un colega que dice que git pull es malo y se enoja cuando alguien lo usa.

El comando git pull es una forma canónica de actualizar el repositorio local. ¿ git pull problemas? ¿Qué problemas crea? ¿Hay una mejor manera de actualizar el repositorio git?

396
10 марта '13 в 1:23 2013-03-10 01:23 Richard Hansen preguntó el 10 de marzo de 2013 a las 1:23 2013-03-10 01:23
@ 4 respuestas

Resumen

De forma predeterminada, git pull crea combinaciones de mezcla, que agregan ruido y complejidad al historial del código. Además, pull hace que sea más fácil no pensar en cómo los cambios entrantes pueden afectar sus cambios.

El comando git pull es seguro siempre y cuando solo realice una fusión rápida. Si git pull configurado solo para la fusión acelerada y cuando la fusión acelerada no es posible, Git saldrá con un error. Esto le dará la oportunidad de examinar las confirmaciones entrantes, pensar cómo pueden afectar sus confirmaciones locales y elegir el mejor curso de acción (fusionar, reubicar, reiniciar, etc.).

Con Git 2.0 y versiones posteriores, puedes ejecutar:

 git config --global pull.ff only 

Cambia el comportamiento predeterminado solo para adelantar. En las versiones de Git entre 1.6.6 y 1.9.x, deberás escribir el hábito:

 git pull --ff-only 

Sin embargo, en todas las versiones de Git, recomiendo git up alias de git up siguiente manera:

 git config --global alias.up '!git remote update -p; git merge --ff-only @{u}' 

y usando git up lugar de git pull . Prefiero este alias git pull --ff-only porque:

  • funciona con todas las versiones (no antiguas) de git,
  • selecciona todas las sucursales en sentido ascendente (no solo la rama en la que está trabajando actualmente), y
  • borra las antiguas ramas de origin/* que ya no existen en el sentido ascendente.

git pull problemas

git pull no git pull malo si se usa correctamente. Varios cambios recientes en Git han simplificado el uso adecuado de git pull , pero desafortunadamente, el comportamiento de git pull regular tiene varios problemas por defecto:

  • Esto introduce no linealidades innecesarias en la historia.
  • esto facilita la introducción accidental de confirmaciones que fueron reubicadas deliberadamente en sentido ascendente
  • Cambia tu directorio de trabajo de una manera impredecible.
  • pausa en lo que estás haciendo para ver el trabajo de otra persona, molesto git pull
  • Esto dificulta el reinicio correcto de una rama remota.
  • no borra las ramas que se eliminaron en el repositorio remoto

Estas cuestiones se describen con más detalle a continuación.

Historia no lineal

De forma predeterminada, el comando git pull es equivalente a git fetch seguido de git merge @{u} . Si hay confirmaciones no expuestas en el repositorio local, la parte de combinación git pull crea una confirmación de combinación.

No hay nada de malo en las combinaciones de combinación, pero pueden ser peligrosas y deben tratarse con respeto:

  • Los compromisos de fusión son esencialmente difíciles de aprender. Para comprender lo que hace una fusión, debe comprender las diferencias entre todos los padres. Regular diff no transmite esta información multidimensional. Por el contrario, una serie de compromisos normales es fácil de ver.
  • Conflicto de un conflicto de combinación es difícil, y los errores a menudo pasan desapercibidos durante mucho tiempo porque los compromisos de combinación son difíciles de ver.
  • Las fusiones pueden reemplazar a la perfección los efectos de los compromisos regulares. El código ya no es la suma de las confirmaciones adicionales, lo que conduce a un malentendido de lo que realmente ha cambiado.
  • Las confirmaciones de fusión pueden romper algunos esquemas de integración continua (por ejemplo, ensamblar automáticamente solo el primer camino -p bajo un acuerdo asumido, según el cual los segundos padres apuntan a trabajar en progreso en el proceso).

Por supuesto, hay un momento y lugar para las fusiones, pero una comprensión de cuándo deben y no deben usarse las fusiones puede aumentar la utilidad de su repositorio.

Tenga en cuenta que el propósito de Git es simplificar el uso compartido y la evolución de la base de código, y no registrar con precisión la historia exactamente como se implementó. (Si no está de acuerdo, considere el comando de rebase y el motivo por el que se creó). Las confirmaciones de fusión creadas por git pull no transfieren semánticas útiles a otros; solo dicen que alguien más realizó la transferencia al repositorio antes de que usted hiciera los cambios. ¿Por qué se combinan estos compromisos si no tienen significado para otros y pueden ser peligrosos?

Puede configurar git pull para reubicar en lugar de fusionar, pero esto también tiene problemas (que se explicarán más adelante). En su lugar, git pull solo debe configurarse para una fusión acelerada.

Reintroducción de comités reubicados.

Supongamos que alguien lanza una rama y la empuja por la fuerza. Esto generalmente no debería suceder, pero a veces es necesario (por ejemplo, eliminar un archivo de registro de 50 GB que se procesó y transmitió accidentalmente). Una fusión realizada por git pull combinará la nueva versión de la rama ascendente con la versión anterior que aún existe en su repositorio local. Si golpeas el resultado, las horquillas y antorchas comenzarán a aparecer en tu camino.

Algunos pueden argumentar que el problema real es una actualización forzada. Sí, generalmente es aconsejable evitar los impulsos de poder cuando sea posible, pero a veces son inevitables. Los desarrolladores deben estar preparados para lidiar con actualizaciones forzadas, porque a veces ocurren. Esto significa que no puede fusionar ciegamente las confirmaciones antiguas utilizando el git pull normal.

Cambios en el directorio de trabajo sorpresa

No hay forma de predecir cómo se verá el directorio o índice de trabajo hasta git pull se ejecute git pull . Es posible que haya conflictos de combinación que deba resolver antes de poder hacer algo, lo que puede ocasionar que aparezca un archivo de registro de 50 GB en su directorio de trabajo, ya que alguien lo pulsó accidentalmente, puede cambiar el nombre del directorio en el que está trabajo, etc.

git remote update -p (o git fetch --all -p ) le permite ver los compromisos de otras personas antes de decidir fusionarse o reubicarse, lo que le permite formular un plan antes de tomar medidas.

Dificultades para tratar a otras personas.

Supongamos que está en el proceso de realizar algunos cambios, y alguien más desea que vea algunos de los compromisos en los que acaba de hacer clic. La operación git pull merge (o rebase) cambia el directorio e índice de trabajo, lo que significa que su directorio e índice de trabajo deben estar limpios.

Puede usar git stash y luego git pull , pero ¿qué hará cuando termine la revisión? Para volver a donde estaba, debe cancelar la fusión creada por git pull y aplicar el caché.

git remote update -p (o git fetch --all -p ) no cambia el directorio o índice de trabajo, por lo que puede ejecutarlo de forma segura en cualquier momento, incluso si realizó y / o no realizó cambios. Puede pausar lo que está haciendo y ver el compromiso de otro sin preocuparse por guardar o completar el compromiso en el que está trabajando. git pull no te da esa flexibilidad.

Reubicarse en una sucursal remota

El patrón de uso habitual de Git es hacer que git pull realice los últimos cambios, seguido de git rebase @{u} para excluir la confirmación de fusión introducida por git pull . Muy a menudo, Git tiene varias opciones de configuración para reducir estos dos pasos a un solo paso, y le dice a git pull que se reubique en lugar de fusionar (vea la branch.<branch>.rebase , branch.autosetuprebase y pull.rebase ).

Desafortunadamente, si tiene una confirmación de fusión descargada que desea guardar (por ejemplo, una confirmación que combina una rama extendida de un objeto en un master ), no reubique -p ull ( git pull with branch.<branch>.rebase establece en true ) ni una fusión -p ull (el comportamiento por defecto de git pull ) seguido de rebase no funcionará. Esto se debe a que git rebase elimina las fusiones (linealiza DAG) sin --preserve-merges . La operación cero rebase -p no se puede configurar para guardar fusiones, y después de la fusión -p seguida de git rebase -p @{u} , la fusión causada por la fusión -p no se eliminará. Actualización: Git v1.8.5 agregó git pull --rebase=preserve y git config pull.rebase preserve . Esto hace que git pull realice git rebase --preserve-merges después de buscar confirmaciones en sentido ascendente. (¡Gracias a la maestra de fantasía por el heads-up!)

Borrado de ramas eliminadas

git pull no elimina las ramas de seguimiento remoto correspondientes a las sucursales que se eliminaron del repositorio remoto. Por ejemplo, si alguien elimina la rama foo de un repositorio remoto, aún verá origin/foo .

Esto lleva al hecho de que los usuarios accidentalmente resucitan ramas muertas, porque creen que todavía están activas.

La mejor alternativa: usar git up lugar de git pull

En lugar de git pull recomiendo crear y usar el siguiente alias git up :

 git config --global alias.up '!git remote update -p; git merge --ff-only @{u}' 

Este alias carga todas las confirmaciones más recientes de todas las ramas en sentido ascendente (podando ramas muertas) e intenta rebobinar la rama local hasta el último compromiso en la rama en sentido ascendente. Si las confirmaciones locales tuvieron éxito, no había riesgo de un conflicto de fusión. El avance rápido fallará si hay confirmaciones locales (no impulsadas), lo que le dará la oportunidad de revisar las confirmaciones anteriores antes de realizar las acciones.

Todavía cambia su directorio de trabajo de una manera impredecible, pero solo si no tiene cambios locales. A diferencia de git pull , git up nunca te dará una pista en la que esperas que corrijas un conflicto de combinación.

Alternativamente: git pull --ff-only --all -p

El siguiente es un alias git up alternativo:

 git config --global alias.up 'pull --ff-only --all -p' 

Esta versión de git up tiene el mismo comportamiento que el alias de git up anterior, excepto por:

  • el mensaje de error es un poco más misterioso si su sucursal local no está configurada con una rama ascendente
  • se basa en una característica no documentada (el argumento -p que se pasa a la fetch ), que puede cambiar en futuras versiones de Git

Si está utilizando Git 2.0 o posterior

En Git 2.0 y versiones posteriores, puede configurar git pull para que solo se realicen combinaciones aceleradas de forma predeterminada:

 git config --global pull.ff only 

Esto hace que git pull actúe como git pull --ff-only , pero aún así no extrae todas las confirmaciones anteriores y no git pull --ff-only old origin/* así que todavía prefiero git up .

535
10 марта '13 в 1:23 2013-03-10 01:23 Respuesta dada por Richard Hansen el 10 de marzo de 2013 a la 1:23 2013-03-10 01:23

Mi respuesta, sacada de la discusión que se originó en HackerNews:

Estoy tentado de simplemente responder a la pregunta utilizando la ley de titulares de Betteridge: ¿por qué se considera dañino a git pull ? No lo es

border=0
  • Las no linealidades no son internamente malas. Si representan una historia real, están bien.
  • La reintroducción accidental de confirmaciones rebasadas en sentido ascendente es el resultado de una reescritura incorrecta de la historia hacia arriba. No puede volver a escribir el historial cuando el historial se replica en múltiples repositorios.
  • Cambiar el directorio de trabajo - el resultado esperado; Utilidad discutible, es decir, en las condiciones de comportamiento Hg / monotonous / Darcs / other_dvcs_predating_git, pero de nuevo, no por su propia naturaleza.
  • La fusión requiere la suspensión de la consideración del trabajo de otros usuarios, así como el comportamiento esperado al estirar git. Si no desea fusionar, debe usar git fetch. Nuevamente, esto es una idiosincrasia de git en comparación con los DVD populares anteriores, pero este es un comportamiento esperado, no internamente malo.
  • Haga que sea difícil volver a instalar en una rama remota. No reescriba la historia si no la necesita. No puedo dejar que la vida entienda este deseo de una historia lineal (falsa).
  • No limpiar las ramas es bueno. Cada repo sabe lo que quiere. git no tiene un concepto maestro-esclavo.
194
12 марта '14 в 18:15 2014-03-12 18:15 La respuesta la da Sérgio Carvalho el 12 de marzo de 2014 a las 18:15 2014-03-12 18:15

Esto no se considera dañino si está utilizando Git correctamente. Veo cómo esto afecta negativamente su caso de uso, pero puede evitar problemas simplemente cambiando la historia general.

26
12 марта '14 в 18:43 2014-03-12 18:43 La respuesta la dio Hunt Burdick el 12 de marzo de 2014 a las 18:43 2014-03-12 18:43

Requisitos de respuesta aceptados

La operación rebase-pull no se puede configurar para guardar una combinación

pero con Git 1.8.5 , que publica esta respuesta, puedes hacer

 git pull --rebase=preserve 

o

 git config --global pull.rebase preserve 

o

 git config branch.<name>.rebase preserve 

los doctores dicen

Cuando se preserve, también pasa --preserve-merges con 'git rebase', por lo que las combinaciones locales comprometidas no se suavizarán al ejecutar 'git pull'.

En esta discusión anterior hay información más detallada y diagramas: Git pull --rebase - preserve-merges . Esto también explica por qué git pull --rebase=preserve no coincide con git pull --rebase --preserve-merges , que no es el caso.

Esta otra discusión anterior explica qué es lo que realmente implementa la opción de rebase de ahorro y fusión, y cuánto más difícil es que un rebase regular: ¿Qué hace exactamente (y por qué?) Git "rebase-preserve-merges"

17
12 марта '14 в 19:38 2014-03-12 19:38 Marc Liyanage da la respuesta el 12 de marzo de 2014 a las 19:38 2014-03-12 19:38

Otras preguntas sobre etiquetas de o hacer una pregunta