tutorial stackoverflow into explicado git git-merge git-rebase

stackoverflow - git rebase tutorial



¿Hay alguna diferencia entre git rebase y git merge--ff-only? (3)

Por lo que leí, ambos nos ayudan a obtener una historia lineal.

Por lo que experimenté, rebase funciona todo el tiempo. Pero fusionar --ff-only solo funciona en escenarios en los que se puede reenviar rápidamente.

También noté que git merge crea un commit de mezcla, pero si usamos --ff-only, da un historial lineal que esencialmente es igual a git rebasing Así que --ff-only mata el propósito de git merge, ¿verdad?

Entonces, ¿cuál es la diferencia real entre ellos?


Sí, --ff-only siempre fallará donde una git merge simple de git merge fallaría, y podría fallar donde una git merge simple de git merge tendría éxito. Ese es el punto: si estás tratando de mantener un historial lineal, y la fusión no se puede hacer de esa manera, quieres que falle.

Una opción que agrega casos de falla a un comando no es inútil; Es una forma de validar una condición previa, por lo que si el estado actual del sistema no es el esperado, no empeora el problema.


Sí, hay una diferencia. git merge --ff-only si no puede avanzar rápidamente, y git merge --ff-only un commit (normalmente una rama) para fusionar. Solo creará un commit de fusión si no puede avanzar (es decir, nunca lo hará con --ff-only ).

git rebase vuelve a escribir el historial en la rama actual, o se puede usar para volver a escribir una rama existente en una rama existente. En ese caso, no creará una confirmación de fusión porque se está rebasando, en lugar de fusionarse.


Tenga en cuenta que git rebase tiene un trabajo diferente al de git merge (con o sin --ff-only ). Lo que hace rebase es tomar las confirmaciones existentes y copiarlas . Supongamos, por ejemplo, que estás en branch1 y has realizado dos confirmaciones A y B :

...-o--o--A--B <-- HEAD=branch1 / o--C <-- branch2

y decides que prefieres que esos dos compromisos estén en branch2 en branch2 lugar. Usted puede:

  • obtener una lista de los cambios que realizó en A (diff A contra su padre)
  • obtenga una lista de los cambios que realizó en B (dif. B contra A )
  • cambiar a branch2
  • realice los mismos cambios que realizó en A y confírmelos, copiando su mensaje de confirmación de A ; Llamemos a esto cometer A''
  • y luego realice los mismos cambios que hizo en B y confírmelos, copiando su mensaje de confirmación de B ; Llamemos a esto B'' .

Hay un comando de git que hace esto hace "diff-and-then-copy-and-commit": git cherry-pick . Asi que:

git checkout branch2 # switch HEAD to branch2 (commit C) git cherry-pick branch1^ # this copies A to A'' git cherry-pick branch1 # and this copies B

Ahora tienes esto:

...-o--o--A--B <-- branch1 / o--C--A''-B'' <-- HEAD=branch2

Ahora puedes volver a la branch1 y borrar tus A y B originales, usando git reset ( --hard aquí, es más conveniente de esa manera, ya que también limpia el árbol de trabajo):

git checkout branch1 git reset --hard HEAD~2

Esto elimina el original A y B , 1 por lo que ahora tiene:

...-o--o <-- HEAD=branch1 / o--C--A''-B'' <-- branch2

Ahora solo tiene que volver a realizar el check-out de branch2 para continuar trabajando allí.

Esto es lo que hace git rebase : "mueve" las confirmaciones (aunque no las mueve realmente, porque no puede: en git, una confirmación nunca se puede cambiar, por lo que incluso cambiar la ID de los padres requiere copiarla en nueva y compromiso ligeramente diferente).

En otras palabras, mientras que git cherry-pick es un proceso automático de rehacer y rehacer de una confirmación, git rebase es un proceso automatizado de rehacer múltiples confirmaciones, además, al final, mover las etiquetas para "olvidar" o esconder el originales

Lo anterior ilustra los compromisos en movimiento de una rama branch1 a otra rama branch2 , pero git usa el mismo proceso exacto para mover compromisos cuando tiene una rama de seguimiento remoto que adquiere algunos nuevos compromisos cuando realiza una git fetch (incluida la fetch Es el primer paso de git pull ). Puede comenzar trabajando en la feature bifurcación, que tiene una fuente origin/feature sentido ascendente, y hacer un par de confirmaciones propias:

...-o <-- origin/feature / A--B <-- HEAD=feature

Pero luego decides que deberías ver lo que sucedió en sentido ascendente, así que ejecutas git fetch , 2 y, aha, alguien en sentido ascendente escribió una confirmación C :

...-o--C <-- origin/feature / A--B <-- HEAD=feature

En este punto, puede simplemente cambiar la base de sus feature A y B a C , dando:

...-o--C <-- origin/feature / A''-B'' <-- HEAD=feature

Estas son copias de su original A y B , y los originales se desechan (pero consulte la nota al pie de página 1) después de completar las copias.

A veces no hay nada que cambiar, es decir, no hay trabajo que usted mismo haya hecho. Es decir, la gráfica antes de la fetch ve así:

...-o <-- origin/feature `-- HEAD=feature

Si luego git fetch y commit C entra, sin embargo, te quedas con la rama de tu feature apuntando al antiguo commit, mientras que el origin/feature ha avanzado:

...-o--C <-- origin/feature `---- <-- HEAD=feature

Aquí es donde git merge --ff-only : si solicita fusionar su feature rama actual con origin/feature , git ve que es posible simplemente deslizar la flecha hacia adelante, por así decirlo, por lo que esa feature apunta directamente a cometer C No se requiere una fusión real.

Sin embargo, si tuvieras tus propios A y B , y pediste fusionar los con C , git haría una verdadera fusión, haciendo un nuevo M17:

...-o--C <-- origin/feature / `-_ A--B--M <-- feature

Aquí, --ff-only se detendrá y le dará un error. Rebase, por otro lado, puede copiar A y B a A'' y B'' y luego ocultar el original A y B

Entonces, en resumen (ok, muy tarde :-)), simplemente hacen cosas diferentes. A veces el resultado es el mismo, y otras veces no lo es. Si está bien copiar A y B , puedes usar git rebase ; pero si hay una buena razón para no copiarlos, puede usar git merge , quizás con --ff-only , para fusionar o fallar según sea apropiado.

1 En realidad, Git conserva los originales durante algún tiempo, normalmente un mes en este caso, pero los oculta. La forma más fácil de encontrarlos es con los "reflogs" de git, que mantienen un historial de dónde apuntó cada rama y dónde apuntó HEAD , antes de cada cambio que actualizó la rama y / o HEAD .

Eventualmente, las entradas del historial de reflog caducan, momento en el cual estas confirmaciones son elegibles para la recolección de basura .

2 O, nuevamente, puedes usar git pull , que es un script de conveniencia que comienza ejecutando git fetch . Una vez que se realiza la búsqueda, el script de conveniencia ejecuta git merge o git rebase , dependiendo de cómo lo configures y git rebase .