Nuestro equipo de redactores ha estado largas horas investigando la respuesta a tu pregunta, te brindamos la solución así que nuestro objetivo es servirte de gran apoyo.
Solución:
Tenga en cuenta que git rebase
tiene un diferente trabajo que git merge
(con o sin --ff-only
). Qué rebase
hace es tomar confirmaciones existentes y Copiar ellos. Suponga, por ejemplo, que está en branch1
y he hecho dos confirmaciones A
y B
:
...-o--o--A--B <-- HEAD=branch1
o--C <-- branch2
y decides que prefieres que esas dos confirmaciones estén en branch2
en lugar de. Usted puede:
- obtener una lista de los cambios que realizó en
A
(diffA
contra su padre) - obtener una lista de los cambios que realizó en
B
(diffB
contraA
) - cambiar a
branch2
- haz los mismos cambios que hiciste en
A
y cometerlos, copiando tu mensaje de compromiso deA
; vamos a llamar a este compromisoA'
- y luego haz los mismos cambios que hiciste en
B
y cometerlos, copiando tu mensaje de compromiso deB
; vamos a llamar a estoB'
.
Hay un comando git que hace este diff-and-then-copy-and-commit por ti: git cherry-pick
. Entonces:
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 to B'
Ahora tienes esto:
...-o--o--A--B <-- branch1
o--C--A'-B' <-- HEAD=branch2
Ahora puedes volver a branch1
y borra tu original A
y B
, utilizando git reset
(Usaré --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 quita el original A
y B
,1 así que ahora tienes:
...-o--o <-- HEAD=branch1
o--C--A'-B' <-- branch2
Ahora solo necesita volver a realizar el check-out branch2
para seguir trabajando allí.
Esto es lo que git rebase
hace: "mueve" las confirmaciones (aunque no moviéndolas realmente, porque no puede: en git, una confirmación nunca se puede cambiar, por lo que incluso cambiar el ID de padre requiere copiarlo a una confirmación nueva y ligeramente diferente).
En otras palabras, mientras git cherry-pick
es un diff-and-redo automatizado de uno cometer, git rebase
es un proceso automatizado de rehacer múltiple confirma, además, al final, mover las etiquetas para "olvidar" u ocultar los originales.
Lo anterior ilustra el traslado de confirmaciones desde una rama local. branch1
a otra sucursal local branch2
, pero git usa el exactamente el mismo proceso mover confirmaciones cuando tienes una rama de seguimiento remoto que adquiere algunas confirmaciones nuevas cuando haces una git fetch
(incluyendo el fetch
ese es el primer paso de git pull
). Puede comenzar trabajando en la rama feature
, que tiene un flujo ascendente de origin/feature
y haz un par de confirmaciones por tu cuenta:
...-o <-- origin/feature
A--B <-- HEAD=feature
Pero luego decides que deberías ver lo que ha sucedido río arriba, así que corres git fetch
,2 y, ajá, alguien río arriba escribió un compromiso C
:
...-o--C <-- origin/feature
A--B <-- HEAD=feature
En este punto, simplemente puede reajustar su feature
's A
y B
sobre C
, donación:
...-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 1 al pie de página) una vez que las copias están completas.
A veces no hay nada que reajustar, es decir, ningún trabajo que usted mismo hizo. Es decir, el gráfico antes del fetch
se parece a esto:
...-o <-- origin/feature
`-- HEAD=feature
Si tu entonces git fetch
y comprometerse C
entra, sin embargo, te quedas con tufeature
rama apuntando a la confirmación anterior, mientras que origin/feature
ha avanzado:
...-o--C <-- origin/feature
`---- <-- HEAD=feature
Aquí es donde git merge --ff-only
entra: si solicita fusionar su rama actual feature
con origin/feature
, git ve que es posible simplemente deslizar la flecha hacia adelante, por así decirlo, para que feature
apunta directamente a comprometerse C
. No se requiere una fusión real.
Si tuvieras tus propias confirmaciones A
y B
, sin embargo, y solicitó fusionarlos con C
, git haría una fusión real, haciendo un nuevo compromiso de fusión M
:
...-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
para A'
y B'
y luego esconde el original A
y B
.
Entonces, en resumen (ok, demasiado tarde :-)), simplemente hacen cosas diferentes. A veces, el resultado es el mismo y otras no. Si está bien copiar A
y B
, puedes usar git rebase
; pero si hay alguna buena razón no para copiarlos, puedes usar git merge
, tal vez con --ff-only
, para fusionar o fallar según corresponda.
1De hecho, 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 apunta cada rama y dónde HEAD
apuntado, antes de cada cambio que actualizara la sucursal y / o HEAD
.
Eventualmente, las entradas del historial de reflog caducan, momento en el que estas confirmaciones se vuelven elegibles para la recolección de basura.
2O, nuevamente, puede 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 se ejecuta git merge
o git rebase
, dependiendo de cómo lo configure y ejecute.
Sí, hay una diferencia. git merge --ff-only
abortará si no puede avanzar rápidamente, y requiere una confirmación (normalmente una rama) para fusionarse. Solo creará una confirmación de fusión si no puede avanzar rápidamente (es decir, nunca lo hará con --ff-only
).
git rebase
reescribe el historial en la rama actual, o se puede utilizar para volver a establecer la base de una rama existente en una rama existente. En ese caso, no creará una confirmación de fusión porque está reajustando, en lugar de fusionarse.
Sí, --ff-only
siempre fallará donde un llano git merge
fallaría, y podría fallar donde un llano git merge
tendría éxito. Ese es el punto: si está tratando de mantener un historial lineal, y la fusión no se puede hacer de esa manera, querer 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.
valoraciones y reseñas
Puedes secundar nuestro cometido fijando un comentario y puntuándolo te damos la bienvenida.