explicado - git rebase head master
git pull--rebase--preserve-merges (2)
Técnicamente, y afirmo que esto es un poco estúpido de git, el script de pull
(es un script de shell) debería hacer esto por ti, tienes que ejecutar git pull --rebase=preserve
lugar de intentar usar git pull --rebase --preserve-merges
. (O, como branch. name .rebase
en un comentario sobre la respuesta de Vlad Nikitin , puedes configurar la branch. name .rebase
para preserve
y obtener el mismo efecto automáticamente).
En otras palabras, nunca debe ejecutar git pull --rebase --preserve-merges
cuando pasa (incorrectamente) pasa --preserve-merges
al paso de fetch
, en lugar de al paso de merge
-o-- rebase
. Sin embargo, puedes ejecutar git pull --rebase=preserve
.
La cuestión de cuándo (y si) usar cualquier tipo de rebase, si se trata de preservar la fusión o no, es más una cuestión de opinión. Lo que significa que, en primer lugar, no va bien en el stackoverflow. :-)
Sin embargo, haré una reclamación aquí: solo debería volver a hacer un caso si sabe (en un sentido general) lo que está haciendo, 1 y si sabe lo que está haciendo, es probable que prefiera un cambio de fase para preservar la fusión. como regla general, aunque para el momento en que haya decidido que la rebasada es una buena idea, probablemente encontrará que un historial que tiene sus propios puntos de fusión y fusión integrados no es necesariamente el "historial reescrito final" correcto.
Es decir, si es apropiado hacer una rebase, es al menos bastante probable que la historia que se va a rebasar sea en sí misma lineal, por lo que la cuestión de preservar contra aplanar es discutible de todos modos.
Editar: añadir dibujo
Aquí hay un dibujo de parte de un gráfico de compromiso, que muestra dos ramas con nombre, mainline
y experiment
. La base común para la mainline
y la experiment
es el nodo de confirmación A
, y la mainline
tiene una confirmación G
que no está en la rama de experiment
prueba:
...--o--A-------------G <-- mainline
/
/ .-C-.
B E--F <-- experiment
/_D_/
Sin embargo, tenga en cuenta que la rama del experiment
tiene una rama y combinación: la base para estas dos ramas es B
, una rama contiene el compromiso C
y la otra rama mantiene el compromiso D
Estas dos ramas (sin nombre) se reducen a un solo hilo de desarrollo en la combinación de mezcla E
, y luego la confirmación F
sienta sobre la confirmación de combinación y es la punta del experiment
de la rama.
Esto es lo que sucede si estás experiment
y ejecutando git rebase mainline
:
$ git rebase mainline
First, rewinding head to replay your work on top of it...
Applying: B
Applying: C
Applying: D
Applying: F
Aquí está lo que está ahora en el gráfico de confirmación:
...--o--A--G <-- mainline
/
B''-C''-D''-F'' <-- experiment
La "rama estructural" que solía estar allí en el experiment
rama se ha ido. La operación de rebase
copió todos los cambios que había hecho en las confirmaciones B
, C
, D
y F
; estos se convirtieron en los nuevos compromisos B''
, C''
, D''
y F''
. (Commit E
fue una combinación pura sin cambios y no requirió copia. No he probado lo que sucede si reagrupo una combinación con cambios incorporados, ya sea para resolver conflictos o, como algunos lo llaman, una "combinación maligna".)
Por otro lado, si hago esto:
$ git rebase --preserve-merges mainline
[git grinds away doing the rebase; this takes a bit longer
than the "flattening" rebase, and there is a progress indicator]
Successfully rebased and updated refs/heads/experiment.
Me sale este gráfico en su lugar:
...--o--A--G <-- mainline
/
/ .-C''.
B'' E''-F'' <-- experiment
/_D''/
Esto ha preservado la fusión y, por lo tanto, la "ramificación interna" del experiment
. ¿Es bueno eso? ¿Malo? ¿Indiferente? Lea la (muy larga) nota al pie!
1 Es una buena idea aprender "qué hace la rebase" de todos modos, lo que en git (¡ay!) Requiere aprender también "cómo lo hace", al menos en un nivel medio. Básicamente, rebase hace copias de (los cambios de sus compromisos anteriores), que luego aplica a los compromisos (suyos o de otra persona), haciendo que "parezca" que hizo el trabajo en algún otro orden. Un ejemplo simple: dos desarrolladores, digamos Alice y Bob, están trabajando en la misma rama. Digamos que Marketing ha solicitado una característica llamada Strawberry, y tanto Alice como Bob están haciendo algunos trabajos para implementar la strawberry
, ambos en una rama llamada strawberry
.
Alice y Bob ejecutan git fetch
para traer strawberry
de origin
.
Alice descubre que el archivo abc
necesita algunos cambios para prepararse para la nueva función. Ella escribe eso y se compromete, pero no empuja todavía.
Bob escribe una descripción de la nueva característica, que cambia el archivo README
, pero no tiene otro efecto. Bob comete su cambio y empuja.
Alice luego actualiza el archivo para proporcionar la característica real. Ella escribe y confirma (por separado) eso, y ahora está lista para presionar. Pero, oh no, Bob la venció:
$ git push origin strawberry
...
! [rejected] strawberry -> strawberry (non-fast-forward)
Luego, Alice debería buscar los cambios y mirarlos (no solo fusionar o reajustar a ciegas):
$ git fetch
...
$ git log origin/strawberry
(o usando gitk
o lo que sea, tiendo a usar git lola
mí mismo, y git show
las confirmaciones individuales si es necesario).
Ella puede ver en esto que Bob solo cambió el README
, por lo que sus cambios definitivamente no se ven afectados de ninguna manera. En este punto, puede decir que es seguro volver a ajustar sus cambios en origin/strawberry
:
$ git rebase origin/strawberry
(tenga en cuenta que no hay fusiones que preservar), lo que hace que parezca (en términos de historial de git) que esperó a que Bob actualizara la documentación, y solo entonces realmente comenzó a implementar los cambios, que aún se dividen en dos confirma para que sea fácil saber, más tarde, si el cambio al archivo abc
rompió algo más. Sin embargo, esas dos confirmaciones separadas ahora son adyacentes, por lo que es fácil decir, más tarde, que el punto del cambio a abc
fue habilitar el cambio a la feat
archivo. Y dado que el cambio a README
es lo primero, es aún más claro que este fue el punto del cambio para abc
. No es que sea difícil decirlo, incluso si Alice lo hiciera:
$ git merge origin/strawberry
en cambio, aunque eso crea una confirmación de fusión cuyo único punto parece ser "Alicia comenzó en abc
antes de que Bob terminara de actualizar README
, y terminó después", lo cual no es realmente útil.
En casos más complejos, donde Bob hizo algo más que actualizar la documentación, Alice podría encontrar que es mejor reorganizar sus propios compromisos (probablemente más de dos en este caso) en un nuevo historial lineal diferente, de modo que algunos de los cambios de Bob ( esta vez, probablemente más de un compromiso) están "en el medio", por ejemplo, como si hubieran cooperado en tiempo real (y quién sabe, tal vez lo hicieron). O podría encontrar que es mejor mantener sus cambios como una línea de desarrollo separada que se fusiona, quizás incluso más de una vez, con los cambios de Bob.
Todo es una cuestión de lo que proporcionará la información más útil para alguien (posiblemente Alice y Bob, posiblemente otros desarrolladores) en el futuro, cuando sea necesario volver atrás y ver el (aparente, si es rebasado, o actual si no es) secuencia de eventos. A veces, cada compromiso individual es información útil. A veces es más útil reorganizar y combinar compromisos, o eliminar algunos compromisos por completo: por ejemplo, cambios que demostraron ser una mala idea. (¡Pero considera dejarlos solo por el valor de señalar que "esto fue una mala idea, así que no lo intentes de nuevo en el futuro" también!)
Versión corta: ¿Necesita preservar-fusionar solo si se fusionó explícitamente después de realizar un compromiso local? ¿Qué sucede exactamente de otra manera? ¿Vuelve a aplicar su código comprometido a la rama fusionada?
Explique cuándo es útil git pull --rebase --preserve-merges
frente a un git pull --rebase
regular git pull --rebase
? Leí sobre un problema con git pull --rebase
aquí: http://notes.envato.com/developers/rebasing-merge-commits-in-git/ Esto podría causar que los cambios de código se dupliquen.
Leí aquí: ¿ Cuándo `git pull --rebase` me meterá en problemas?
Eso solo sucede si básicamente se vuelve a generar una base después de que se hayan enviado algunas confirmaciones.
Así que no estoy seguro de entender cuándo necesitaría git pull --rebase --preserve-merges
y si alguna vez es malo usar vs. git pull --rebase
.
git pull --rebase
es lo mismo que git fetch
y luego git rebase
, por lo que git pull --rebase --preserve-merges
es lo mismo que git fetch
y luego git rebase --preserve-merges
. Puede obtener una buena respuesta acerca de git rebase --preserve-merges
aquí ¿Qué hace exactamente la función de Git "Rebase - Preserve-Merges" (y por qué?)