Entiendo el escenario presentado en Pro Git sobre The Perils of Rebasing . El autor básicamente te dice cómo evitar confirmaciones duplicadas:
No reescriba las confirmaciones que ha enviado a un repositorio público.
Voy a contarte mi situación particular porque creo que no se ajusta exactamente al escenario Pro Git y todavía termino con confirmaciones duplicadas.
Digamos que tengo dos sucursales remotas con sus contrapartes locales:
origin/master origin/dev
| |
master dev
Las cuatro ramas contienen los mismos commits y voy a comenzar el desarrollo en dev
:
origin/master : C1 C2 C3 C4
master : C1 C2 C3 C4
origin/dev : C1 C2 C3 C4
dev : C1 C2 C3 C4
Después de un par de confirmaciones, empujo los cambios a origin/dev
:
origin/master : C1 C2 C3 C4
master : C1 C2 C3 C4
origin/dev : C1 C2 C3 C4 C5 C6 # (2) git push
dev : C1 C2 C3 C4 C5 C6 # (1) git checkout dev, git commit
Tengo que volver master
a hacer una solución rápida:
origin/master : C1 C2 C3 C4 C7 # (2) git push
master : C1 C2 C3 C4 C7 # (1) git checkout master, git commit
origin/dev : C1 C2 C3 C4 C5 C6
dev : C1 C2 C3 C4 C5 C6
Y volviendo a dev
redactar los cambios para incluir la solución rápida en mi desarrollo real:
origin/master : C1 C2 C3 C4 C7
master : C1 C2 C3 C4 C7
origin/dev : C1 C2 C3 C4 C5 C6
dev : C1 C2 C3 C4 C7 C5' C6' # git checkout dev, git rebase master
Si visualizo el historial de confirmaciones con GitX / gitk, noto que origin/dev
ahora contiene dos confirmaciones idénticas C5'
y C6'
que son diferentes a Git. Ahora si presiono los cambios a origin/dev
este es el resultado:
origin/master : C1 C2 C3 C4 C7
master : C1 C2 C3 C4 C7
origin/dev : C1 C2 C3 C4 C5 C6 C7 C5' C6' # git push
dev : C1 C2 C3 C4 C7 C5' C6'
Tal vez no entiendo completamente la explicación en Pro Git, por lo que me gustaría saber dos cosas:
- ¿Por qué Git duplica estos commits mientras rebase? ¿Hay alguna razón particular para hacer eso en lugar de simplemente aplicar
C5
yC6
despuésC7
? - ¿Cómo puedo evitar eso? ¿Sería prudente hacerlo?
origin/dev
. Cuandodev
se vuelve a modificar, su historial se modifica (C5 / C6 se elimina temporalmente y se vuelve a aplicar después de C7). La modificación del historial de repositorios empujados es generalmente una Really Bad Idea ™ a menos que sepa lo que está haciendo. En este caso sencillo, el problema podría resolverse haciendo un empuje de fuerzadev
paraorigin/dev
después del rebase y notificar a cualquier otra persona que trabaja fuera delorigin/dev
que probablemente están a punto de tener un mal día. La mejor respuesta, nuevamente, es "no hagas eso ... usa la combinación en su lugar"Respuesta corta
Omitió el hecho de que ejecutó
git push
, obtuvo el siguiente error y luego procedió a ejecutargit pull
:A pesar de que Git intenta ser útil, su consejo de 'git pull' probablemente no sea lo que quieres hacer .
Si usted es:
git push --force
para actualizar el mando a distancia con sus confirmaciones post-rebase ( según respuesta de user4405677 ).git rebase
en primer lugar. Para actualizardev
con los cambios desdemaster
, debe, en lugar de ejecutargit rebase master dev
, ejecutargit merge master
mientras está encendidodev
( según la respuesta de Justin ).Una explicación un poco más larga.
Cada hash de commit en Git se basa en una serie de factores, uno de los cuales es el hash del commit que viene antes.
Si reordena los commits, cambiará los hash de commit; rebasar (cuando hace algo) cambiará los hash de confirmación. Con eso, el resultado de la ejecución
git rebase master dev
, donde nodev
está sincronizadomaster
, creará nuevos commits (y, por lo tanto, hashes) con el mismo contenido que los activadosdev
pero con los commitsmaster
insertados antes que ellos.Puede terminar en una situación como esta de múltiples maneras. Dos maneras en las que puedo pensar:
master
que quiera basar sudev
trabajodev
que ya han sido enviadas a un control remoto, que luego procede a cambiar (reformular mensajes de confirmación, reordenar confirmaciones, confirmaciones de squash, etc.)Comprendamos mejor lo que sucedió. Aquí hay un ejemplo:
Tienes un repositorio:
Luego procedes a cambiar los commits.
(Aquí es donde tendrá que aceptar mi palabra: hay varias maneras de cambiar los commits en Git. En este ejemplo, cambié la hora de
C3
, pero estará insertando nuevos commits, cambiando mensajes de commit, reordenando commits, aplastar cometas juntos, etc.)Aquí es donde es importante notar que los hashes de confirmación son diferentes. Este es el comportamiento esperado ya que ha cambiado algo (cualquier cosa) sobre ellos. Esto está bien, PERO:
Intentar presionar le mostrará un error (y le indicará que debe ejecutar
git pull
).Si corremos
git pull
, vemos este registro:O, mostrado de otra manera:
Y ahora tenemos confirmaciones duplicadas localmente. Si tuviéramos que ejecutar
git push
, los enviaríamos al servidor.Para evitar llegar a esta etapa, podríamos haber corrido
git push --force
(donde corrimosgit pull
). Esto habría enviado nuestras confirmaciones con los nuevos hashes al servidor sin problemas. Para solucionar el problema en esta etapa, podemos restablecerlo antes de ejecutarlogit pull
:Mire el reflog (
git reflog
) para ver cuál era el hash de confirmación antes de ejecutargit pull
.Arriba vemos que
ba7688a
fue el commit en el que estábamos antes de corrergit pull
. Con ese hash de confirmación en la mano, podemos restablecer eso (git reset --hard ba7688a
) y luego ejecutarlogit push --force
.Y ya hemos terminado.
Pero espera, seguí basando el trabajo en las confirmaciones duplicadas
Si de alguna manera no te diste cuenta de que los commits estaban duplicados y procediste a continuar trabajando encima de los commits duplicados, realmente has hecho un desastre por ti mismo. El tamaño del desorden es proporcional al número de confirmaciones que tiene encima de los duplicados.
Cómo se ve esto:
O, mostrado de otra manera:
En este escenario, queremos eliminar las confirmaciones duplicadas, pero mantener las confirmaciones que tenemos basadas en ellas; queremos mantener C6 a C10. Como con la mayoría de las cosas, hay varias maneras de hacerlo:
Ya sea:
cherry-pick
cada commit (C6 a C10 inclusive) en esa nueva rama, y trate esa nueva rama como canónica.git rebase --interactive $commit
, donde$commit
está la confirmación antes de las dos confirmaciones duplicadas 2 . Aquí podemos eliminar directamente las líneas de los duplicados.1 No importa cuál de los dos elija, ya sea
ba7688a
o2a2e220
funciona bien.2 En el ejemplo sería
85f59ab
.TL; DR
Establecer
advice.pushNonFastForward
enfalse
:fuente
git push
's--force-with-lease
hoy en día, ya que es un mejor valor predeterminadoCreo que omitiste un detalle importante al describir tus pasos. Más específicamente, su último paso,
git push
en dev, realmente le habría dado un error, ya que normalmente no puede impulsar cambios no rápidos.Así lo hiciste
git pull
antes del último empuje, lo que resultó en un compromiso de fusión con C6 y C6 'como padres, por lo que ambos permanecerán listados en el registro. Un formato de registro más bonito podría haber hecho más obvio que son ramas fusionadas de confirmaciones duplicadas.O bien, hizo un
git pull --rebase
(o sin explícito--rebase
si está implícito en su configuración), que retiró los C5 y C6 originales en su dev local (y reescribió los siguientes a nuevos hash, C7 'C5' 'C6' ').Una forma de salir de esto podría haber sido
git push -f
forzar el empuje cuando dio el error y borrar C5 C6 del origen, pero si alguien más también los hizo tirar antes de que los borraras, estarías en muchos más problemas ... Básicamente, todos los que tienen C5 C6 necesitarían hacer pasos especiales para deshacerse de ellos. Es exactamente por eso que dicen que nunca debes rebajar nada de lo que ya está publicado. Sin embargo, todavía es factible si dicha "publicación" está dentro de un equipo pequeño.fuente
git pull
es crucial. Su recomendación degit push -f
, aunque peligrosa, es probablemente lo que buscan los lectores.git push --force
, solo para ver qué iba a hacer Git. Aprendí mucho sobre Git desde entonces y hoy en díarebase
es parte de mi flujo de trabajo normal. Sin embargo, lo hagogit push --force-with-lease
para evitar sobrescribir el trabajo de otra persona.--force-with-lease
es un buen valor predeterminado, también dejaré un comentario debajo de mi respuestaDescubrí que en mi caso, este problema es consecuencia de un problema de configuración de Git. (Implicando tirar y fusionar)
Descripción del problema:
Síntomas: Comisiones duplicadas en la rama secundaria después de la nueva versión, lo que implica numerosas fusiones durante y después de la nueva base.
Flujo de trabajo: Estos son los pasos del flujo de trabajo que estaba realizando:
Como consecuencia de este flujo de trabajo, la duplicación de todos los commits de "Feature-branch" desde el rebase anterior ... :-(
El problema se debió al tirón de los cambios de la rama secundaria antes del rebase. La configuración de extracción predeterminada de Git es "fusionar". Esto está cambiando los índices de confirmaciones realizadas en la rama secundaria.
La solución: en el archivo de configuración de Git, configure pull para trabajar en modo rebase:
Espero que pueda ayudar a JN Grx
fuente
Es posible que haya extraído de una rama remota diferente de su actual. Por ejemplo, es posible que haya retirado de Master cuando su sucursal está desarrollando seguimiento de desarrollo. Git obtendrá obedientemente confirmaciones duplicadas si se extrae de una rama no rastreada.
Si esto sucede, puede hacer lo siguiente:
dónde
n == <number of duplicate commits that shouldn't be there.>
Luego, asegúrese de estar tirando de la rama correcta y luego ejecute:
Tirar con él
--rebase
asegurará que no está agregando confirmaciones extrañas que podrían enturbiar el historial de confirmaciones.Aquí hay un poco de mano para git rebase.
fuente