git rebase interactivo squash en el próximo compromiso

9 minutos de lectura

Avatar de usuario de Josh R
jose r

En Git puedo usar una reorganización interactiva para reescribir el historial, esto es genial porque en mi rama de características hice un montón de confirmaciones con código que funcionaba parcialmente mientras exploraba diferentes refactores y formas de hacerlo.

Me gustaría juntar muchos de los compromisos antes de reorganizar o fusionar la rama en el maestro.

Algunas confirmaciones inventadas en orden de primero (arriba) a abajo (último)

1. Initial commit on feature branch "Automatic coffee maker UI"
2. Add hot chocolate as product
3. Add tea as product. Products are now generic
4. Create in memory data store for adapter tests
5. Cry because I can't get entity framework to create a composite key. Integration tests broken.
6. Implemented composite key!!
7. All tests green and feature done!

Digamos que quiero mantener las confirmaciones 3, 4 y 7.

Usando rebase quiero “aplastar” confirmaciones

  • 1 y 2 entran en 3.
  • 4 estancias
  • 5 y 6 entran en 7

Idealmente en el rebase interactivo que haría

1. squash
2. squash
3. pick (contains the work of 1 & 2)
4. pick 
5. squash
6. squash
7. pick (contains the work of 5 & 6)

Pero eso es al revés porque squash fusiona una confirmación con su confirmación anterior. No puedo entender cómo hacer que se aplaste hacia adelante.

¿Estoy siendo difícil y debo aceptar que no funcionará (prefiero que funcione) o hay alguna forma de lograrlo?

Estoy invocando este comando con

git checkout My-feature-branch
git rebase master -i

Luego estoy editando la lista de confirmaciones que aparecen e intento terminarla guardando el archivo y editando el editor, lo que normalmente funciona para mí.

  • No estoy completamente seguro de lo que estás experimentando. squash haría exactamente lo que estás buscando; combine 1 y 2 en 3, y 5 y 6 en 7. Lo que quedaría sería (aparentemente) confirmaciones, 3, 4 y 7. ¿Tiene algún problema con estos comandos? Tenga en cuenta que puede probarlos en su repositorio local sin problemas, pero solo si tiene un repositorio remoto puede restablecerlo.

    – Makoto

    26 mayo 2017 a las 22:17

  • Recibo un mensaje “error: no se puede ‘aplastar’ sin una confirmación previa”, que me lanzó a un bucle y me hace pensar que el n. ° 1 está tratando de squash al n. ° 0, que no existe y no al n. ° 2

    – Josh R.

    26 mayo 2017 a las 22:31

  • ¿Cómo estás invocando esto? Muéstranos ese comando.

    – Makoto

    26 mayo 2017 a las 22:33

  • @Makoto estás equivocado, es al revés, tal como dijo Josh en su pregunta. 😉 Además, debido a que el reflog está allí de forma predeterminada, siempre puede deshacer fácilmente lo que hizo con una rebase sin que haya ningún repositorio remoto presente. 😉

    – Vampiro

    26 mayo 2017 a las 23:29

  • Gran pregunta. Necesitaba exactamente esto, ya que reordenar las confirmaciones genera conflictos. La respuesta de @ Vampire es una gran solución que no genera ningún trabajo adicional.

    –Filip Kubicz

    22 de diciembre de 2021 a las 11:13

También debe reordenar las confirmaciones para que la confirmación que se debe mantener se presente antes que las confirmaciones para aplastar si esto es factible.

Si esto no es factible, porque entonces surgirían conflictos que no desea resolver, simplemente hágalo.

1. pick
2. squash
3. squash
4. pick 
5. pick
6. squash
7. squash

Cuando haya terminado con los ajustes, puede editar el mensaje de confirmación para que contenga el mensaje que desea que tengan las confirmaciones finales. Muy fácil. 🙂

Incluso podrías ser capaz de hacer

1. pick
2. fixup
3. squash
4. pick 
5. pick
6. fixup
7. squash

Entonces creo que solo debería haber una vez que se inicie el editor de mensajes de confirmación, ya que con la corrección, el mensaje de confirmación anterior simplemente se toma sin iniciar el editor.

En Squash, cuando se activa el editor de mensajes de confirmación, también recibe ambos mensajes de confirmación, el de la confirmación que se va a aplastar y el de la confirmación que se va a aplastar, por lo que simplemente puede eliminar el mensaje de confirmación que no desea. mantener.

  • pero luego mantiene la fecha de la primera confirmación. ¿Cómo hago para que use la fecha de la última confirmación?

    –Michael Hewson

    12 de noviembre de 2019 a las 18:18

  • @MichaelHewson Supongo que no hay una manera fácil; Cambiaré manualmente la fecha.

    – luizfls

    12 abr 2021 a las 19:34

avatar de usuario de tera
tera

De hecho, es posible aplastar un compromiso en el siguiente confirme durante la reorganización interactiva y conserve por completo la identidad de la segunda de esas confirmaciones (incluido el autor, la fecha, etc.).

Sin embargo, el método es algo complicado, por lo que el soporte nativo de git rebase -i todavía sería apreciado sin embargo.

Lo demostraré con solo tres confirmaciones aaaaaaa A, bbbbbbb B y ccccccc Cdonde queremos doblar A en B y preservar BIdentidad de: (El método se generaliza fácilmente a más confirmaciones)

  • git rebase -i aaaaaaa^
  • Modifique el script de edición para que se detenga después de confirmar B y sal del editor:
    pick aaaaaaa A
    edit bbbbbbb B
    pick ccccccc C
    
  • Cuando rebase se ha detenido después de Brevertir la última confirmación dos vecesluego continúa:
    git revert HEAD
    git revert HEAD
    git rebase --continue
    

    Debido a que revertir la reversión restaura el estado anterior, todas las confirmaciones siguientes se aplicarán limpiamente. La secuencia de B y Revert "B" juntos no tiene ningún efecto, pero el primero de esos compromisos lleva la identidad completa del compromiso B (de hecho, en esta etapa todavía es comprometerse B).
    B y Revert "B" podrían ser aplastados juntos para formar un compromiso no operativo que lleva la identidad del compromiso B adelante con un compromiso separado rebase -i --keep-empty aaaaaaa^. Esto es muy útil y recomendado para evitar conflictos de combinación falsos, especialmente en casos más complejos. Sin embargo, omitiremos ese paso aquí y solo mantendremos las confirmaciones juntas.
    El compromiso importante ahora es Revert "Revert "B"" que llevará todos los cambios realizados originalmente por B.

  • Ahora rebase -i aaaaaaa^ otra vez para mover A pasado ambos B y Revert "B"mientras aplasta B en ambos Revert "B" y A:
    pick   bbbbbbb B
    squash b111111 Revert "B"
    squash a222222 A
    squash b333333 Revert "Revert "B""
    pick   c444444 C
    

    porque la secuencia de B y Revert "B" no tiene ningún efecto, puede mover cualquier compromiso más allá de él mientras crea solo conflictos de fusión que se pueden resolver de manera trivial.

  • Cuando rebase se detiene con un conflicto de fusión, use git mergetool con una herramienta que puede resolver automáticamente conflictos triviales y dejar que las herramientas hagan precisamente eso.
  • Rebase se detendrá nuevamente para permitirle editar el mensaje de confirmación de la confirmación aplastada. Edita a tu gusto.
  • git rebase --continue y listo

  • Esta debería ser la respuesta. Resuelve la pregunta correctamente.

    – Diogo

    22 de mayo de 2021 a las 4:59

  • Estoy de acuerdo, aunque mi opinión puede estar sesgada. No estoy seguro de si podemos convencer a @JoshR de que se moleste después de cuatro años, pero me alegro de que mi respuesta te haya resultado útil.

    – tera

    23 de mayo de 2021 a las 10:07

La respuesta de Vampire es correcta, pero quiero ofrecer una perspectiva diferente. Creo que aquí es donde te estás poniendo más nervioso de lo necesario: empiezas con:

Digamos que quiero mantener las confirmaciones 3, 4 y 7.

pero luego agrega:

  • 1 y 2 entran en 3.
  • 4 estancias
  • 5 y 6 entran en 7

Pero esto significa que desea conservar (el contenido de) todos de 1, 2, 3, 4, 5, 6 y 7… pero no como confirmaciones separadas. El rebase interactivo squash no significa “tirar”, ni siquiera “tirar el mensaje de confirmación”, simplemente significa “combinar”. Sería mejor si el verbo fuera “combinar” o “fundir” o “mezclar” o algo así. Entonces la secuencia (pick, squash, squash) significa: mantenga los tres, mientras los aplica en ese orden, luego haga un gran compromiso con ellos.

Como ya se señaló, una vez que Git va a hacer un gran compromiso con ellos, Git le da otra oportunidad de editar los tres mensajes de compromiso combinados en un gran mensaje de compromiso.

Cuando se hace la rebase, no has guardado ninguna de las confirmaciones originales. En cambio, has hecho nuevo se compromete No importa con precisión cómo se armó su nuevo primer compromiso a partir de partes, solo cuál fue la fuente final y qué puso en el gran mensaje de compromiso.

  • Llámame quisquilloso, pero si bien es totalmente correcto, esto no responde la pregunta OP, sino que solo le dice que usó una redacción incorrecta, ¿no es así? Si es así, debería ser un comentario a la pregunta, no una respuesta.

    – Vampiro

    29 mayo 2017 a las 16:52


  • @Vampire: sí, probablemente debería ser un comentario. Sin embargo, los límites de los comentarios (espacio y formato) hacen que no encaje muy bien. Y, no es necesariamente sólo fraseología: la idea de que la confirmación cambia de ID y, por lo tanto, no es la confirmación original en absoluto, es importante, ya que otras ramas pueden conservar la confirmación original por su ID original.

    – torek

    29 de mayo de 2017 a las 18:07

  • @JoshR si hubiera eliminado las confirmaciones, también habría descartado los cambios que realizó dentro de ellos.

    – Vampiro

    31 de mayo de 2017 a las 9:31

  • Correcto: no quieres soltar ellos, desea resumirlos en un nuevo compromiso 1a que es igual a 1 + 2 + 3 en términos de cambios de fuente (ensamblados por tres git cherry-pick -n operaciones).

    – torek

    1 jun 2017 a las 21:58

  • O squashes o fixups en un rebase interactivo, exactamente lo que quería hacer. Simplemente confundió el orden en el que funcionan las estrofas. Usando cherry-pick porque esto podría funcionar, pero sería bastante engorroso si simplemente pudiera hacer una reorganización interactiva.

    – Vampiro

    2 de junio de 2017 a las 1:07

Mi manera preferida de hacer esto es simplemente un reset suma un commit -C.

git rebase -i origin/main # or some other commit

Lo que abre su editor, que hago que se vea así:

...
pick aafaa Merge me forward
edit bbfbb Into me
...

Luego, cuando el rebase se detiene en bbfbbhacer

git reset --soft HEAD^
git commit --amend -C HEAD@{1} # could put an explicit commit here
git rebase --continue

también se puede poner en una sola lista de tareas pendientes de rebase:

...
pick aafaa Squash me forward
pick bbfbb Into me
exec git reset --soft HEAD^ && git commit --amend -C HEAD@{1}
...

Lo que quieres es:

1. pick
2. squash
3. fixup
4. pick
5. squash
6. squash
7. fixup

Luego, cuando aparezcan las nuevas confirmaciones combinadas en el editor, elimine las líneas de las confirmaciones seleccionadas y deje las correcciones. Los parches de funciones resultantes deben tener el mensaje de confirmación de la última confirmación de la serie de esa manera.

¿Ha sido útil esta solución?