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
(diffA
contra su padre) - obtenga una lista de los cambios que realizó en
B
(dif.B
contraA
) - cambiar a
branch2
- realice los mismos cambios que realizó en
A
y confírmelos, copiando su mensaje de confirmación deA
; Llamemos a esto cometerA''
- y luego realice los mismos cambios que hizo en
B
y confírmelos, copiando su mensaje de confirmación deB
; Llamemos a estoB''
.
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
.