Recortar Confirmaciones de Git / Aplastar el Historial de Git


Compruebo mi código en una rama de Git cada pocos minutos más o menos, y los comentarios terminan siendo cosas como "Todo roto comenzando de nuevo" y otros absurdos.

Entonces cada pocos minutos/horas/días hago un commit serio con un comentario real como, "Corregido bug #22.55, 3ra vez." ¿Cómo puedo separar estos dos conceptos? Me gustaría poder eliminar todas mis confirmaciones frecuentes y simplemente dejar las serias.

 62
Author: Dan Rosenstark, 2010-02-20

4 answers

Respuesta editada con ahora (en la segunda mitad de esta entrada) la nueva corrección de Git1.7! acción y --autosquash opción para reordenar confirmaciones rápidas y editar mensajes.


Primero, el clásico proceso de aplastamiento, como se hizo antes de Git1.7.
(Git1. 7 tiene el mismo proceso, solo que se hace más rápido por la posibilidad de reordenamiento automático de commit en lugar de reordenamiento manual, y por mensajes de aplastamiento más limpios)

Me gustaría ser capaz de eliminar todos mis chequeos frecuentes y solo deja los serios.

Esto se llama aplastar commits.
Tienes un buen ejemplo de "limpieza de comités" en este artículo de Git ready :
(Nota: la función interactiva rebase llegó desde septiembre de 2007 , y permite aplastar o dividir o eliminar o reordenar confirmaciones: vea también la página de GitPro )

Una palabra de precaución: Solo haga esto en confirmaciones que no lo han hecho se ha enviado un repositorio externo. Si otros han basado el trabajo fuera de las confirmaciones que vas a eliminar, un montón de conflictos pueden ocurrir. Simplemente no reescriba su historia si se ha compartido con otros.

Texto alternativo http://www.gitready.com/images/squash1.png

Los últimos 4 commits serían mucho más felices si estuvieran envueltos juntos{[49]]}

$ git rebase -i HEAD~4

pick 01d1124 Adding license
pick 6340aaa Moving license into its own file
pick ebfd367 Jekyll has become self-aware.
pick 30e0ccb Changed the tagline in the binary, too.

# Rebase 60709da..30e0ccb onto 60709da
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

Rebase usando las últimas cuatro confirmaciones desde donde HEAD está con HEAD~4.
Vamos a aplastar todo en un solo compromiso.
Por lo tanto, cambiar las primeras cuatro líneas del archivo a esto hará el truco:

pick 01d1124 Adding license
squash 6340aaa Moving license into its own file
squash ebfd367 Jekyll has become self-aware.
squash 30e0ccb Changed the tagline in the binary, too.

Básicamente esto le dice a Git que combine las cuatro confirmaciones en la primera confirmación de la lista. Una vez hecho esto y guardado, aparece otro editor con lo siguiente:

# This is a combination of 4 commits.
# The first commit's message is:
Adding license

# This is the 2nd commit message:

Moving license into its own file

# This is the 3rd commit message:

Jekyll has become self-aware.

# This is the 4th commit message:

Changed the tagline in the binary, too.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# Explicit paths specified without -i nor -o; assuming --only paths...
# Not currently on any branch.
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   LICENSE
#   modified:   README.textile
#   modified:   Rakefile
#   modified:   bin/jekyll
#

Dado que estamos combinando tantos commits, Git te permite modificar el mensaje del nuevo commit basado en el resto del commits involucrados en el proceso. Edite el mensaje como mejor le parezca, luego guárdelo y salga.
Una vez hecho esto, ¡tus commits han sido aplastados con éxito!

Created commit 0fc4eea: Creating license file, and making jekyll self-aware.
 4 files changed, 27 insertions(+), 30 deletions(-)
  create mode 100644 LICENSE
    Successfully rebased and updated refs/heads/master.

Y si miramos la historia de nuevo {{[49]]}

Texto alternativo http://www.gitready.com/images/squash2.png


Nota: para propósitos de" aplastamiento de compromisos", Git1.7 (febrero de 2010) ha introducido 2 nuevos elementos (como se menciona por Dustin en el comentario):

  • "git rebase -i" aprendimos la nueva acción " fixup" que aplasta el cambio pero no afecta el mensaje de registro existente.
  • "git rebase -i" también aprendimos --autosquash opción que es útil junto con la nueva acción "fixup".

Ambos (fixup action y --autosquash option) se ilustran en esta entrada del blog de Thechnosorcery Networks. Esas características se han estado cocinando desde el pasado mes de junio de 2009 y se han debatido más el pasado diciembre .

La acción o directiva fixup es para aplastar un commit que habría reordenado manualmente en la lista de edición de commit de un rebase --interactive, mientras ignora el segundo mensaje de commit, lo que hará que la edición del mensaje sea más rápida (solo puede guardarlo: el commit squashed tendrá el primero solo mensaje de commit)
El mensaje de confirmación resultante solo será el primer mensaje de confirmación.

  # s, squash = use commit, but meld into previous commit
  # f, fixup = like "squash", but discard this commit's log message

La opción --autosquash se trata de hacer el commit proceso de reordenamiento automáticamente para usted:

Si sabes en qué commit quieres aplastar algo puedes commitarlo con un mensaje de "squash! $other_commit_subject". Luego, si ejecuta @git rebase --interactive --autosquash commitish@, la línea se establecerá automáticamente como squash, y se colocará debajo de la confirmación con el asunto de subject other_commit_subject.

(En realidad, el squash! solo puede usar el que comienza de otro mensaje de confirmación)

$ vim Foo.txt
$ git commit -am "Change all the 'Bar's to 'Foo's"
[topic 8374d8e] Change all the 'Bar's to 'Foo's
 1 files changed, 2 insertions(+), 2 deletions(-)
$ vim Bar.txt
$ git commit -am "Change all the 'Foo's to 'Bar's"
[topic 2d12ce8] Change all the 'Foo's to 'Bar's
 1 files changed, 1 insertions(+), 1 deletions(-)

$ vim Foo.txt
$ git commit -am "squash! Change all the 'Bar's"
[topic 259a7e6] squash! Change all the 'Bar's
 1 files changed, 2 insertions(+), 1 deletions(-)

¿Ves? Aquí el tercero commit utiliza solo el inicio del primer mensaje de confirmación.
A rebase --interactive --autosquash moverá la confirmación aplastada debajo de la correspondiente:

pick 8374d8e Change all the 'Bar's to 'Foo's
squash 259a7e6 squash! Change all the 'Bar's
pick 2d12ce8 Change all the 'Foo's to 'Bar's

La edición del mensaje sería: {[49]]}

# This is a combination of 2 commits.
# The first commit's message is:

Change all the 'Bar's to 'Foo's

# This is the 2nd commit message:

squash! Change all the 'Bar's

Lo que significa que por defecto mantendría la operación de aplastamiento registrada en el mensaje de confirmación.
Pero con la corrección! directiva, puede mantener ese aplastamiento "invisible" en el mensaje de confirmación, mientras se beneficia de la reordenación automática de confirmación con la opción --autosquash (y el hecho de que su segundo el mensaje de confirmación se basa en la primera confirmación con la que desea ser aplastado).

pick 8374d8e Change all the 'Bar's to 'Foo's
fixup cfc6e54 fixup! Change all the 'Bar's
pick 2d12ce8 Change all the 'Foo's to 'Bar's

El mensaje por defecto será:

# This is a combination of 2 commits.
# The first commit's message is:

Change all the 'Bar's to 'Foo's

# The 2nd commit message will be skipped:

#    fixup! Change all the 'Bar's

Observe que el mensaje del commit fixup! ya está comentado.
Puede guardar el mensaje tal cual, y su mensaje de confirmación original se mantendrá.
Muy útil para incluir cambios cuando te das cuenta de que olvidaste agregar parte de un commit anterior.

Ahora si quieres arreglar o aplastar basado en el anterior commit que acabas de hacer, Jacob Helwig (el autor de la entrada del blog Technosorcery Networks) recomienda los siguientes alias:

[alias]
    fixup = !sh -c 'git commit -m \"fixup! $(git log -1 --format='\\''%s'\\'' $@)\"' -
    squash = !sh -c 'git commit -m \"squash! $(git log -1 --format='\\''%s'\\'' $@)\"' -

Y por hacer un rebase interactivo que siempre se beneficiará de la reordenación automática de commits destinados a ser aplastados:

[alias]
    ri = rebase --interactive --autosquash

Actualización para Git 2.18 (Q2 2018): "git rebase -i" a veces se deja intermedio" # This is a combination of N commits " mensaje destinado al consumo humano dentro de un editor en el resultado final en ciertos casos de esquina, que ha sido arreglado.

Véase commit 15ef693, commit dc4b5bc, commit e12a7ef, commit d5bc6f2 (27 Abr 2018) por Johannes Schindelin (dscho).
(Merged by Junio C Hamano -- gitster -- in commit 4a3bf32, 23 May 2018)

rebase --skip: limpiar el mensaje de confirmación después de una reparación fallida/squash

Durante una serie de órdenes de reparación / squash, las compilaciones interactivas de rebase subir un mensaje de confirmación con comentarios. Esto se presentará al usuario en el editor si al menos uno de esos comandos era un squash.

En cualquier caso, el mensaje de confirmación se limpiará eventualmente, eliminando todos esos comentarios intermedios, en el paso final de una cadena de reparación/calabaza.

Sin embargo, si el último comando fixup/squash en dicha cadena falla con conflictos, y si el usuario decide saltar (o resolverlo a un worktree limpio y luego continuar el rebase) , el código actual falla al limpiar el mensaje de confirmación.

Esta confirmación corrige ese comportamiento.

La solución está bastante más involucrada de lo que parece porque es no solo sobre la cuestión de si estamos git rebase --skiping a reparación o squash. También se trata de eliminar la corrección omitida / squash mensaje de confirmación del mensaje de confirmación acumulado. Y también se trata de la cuestión de si debería permitir al usuario editar la confirmación final mensaje o no ("Había una calabaza en la cadena que no fue saltado ?").

Por ejemplo, en este caso vamos a querer arreglar el mensaje de confirmación, pero no abrirlo en un editor:

pick  <- succeeds
fixup   <- succeeds
squash  <- fails, will be skipped

Aquí es donde el nuevo archivo current-fixups viene en real útil. Una mirada rápida y podemos determinar si hubo un no saltado Squash. Solo tenemos que asegurarnos de mantenerlo actualizado con respecto a saltar órdenes de reparación / squash. Como bono, incluso podemos evitar cometer innecesariamente, por ejemplo, cuando solo había una reparación, y falló, y fue omitido.

Para corregir solo el error donde el mensaje de confirmación final no se limpió correctamente, pero sin arreglar el resto, habría sido más complicado que arreglarlo todo de una sola vez, por lo tanto, este commit se agrupa más de una sola preocupación.


Git 2.19 (Q3 2018) corrige un error: Cuando" git rebase -i " se le dice a squash dos o más confirmaciones en una, etiquetó el mensaje de registro para cada confirmación con su número.
Llamó correctamente al primero "1st commit", pero el siguiente fue " commit #1", que fue off-by-one (!).

Véase commit dd2e36e (15 de agosto de 2018) por Phillip Wood (phillipwood).
(Merged by Junio C Hamano -- gitster -- in commit 36fd1e8, 20 Aug 2018)

rebase -i: corregir la numeración en el mensaje de squash

Commit e12a7ef ("rebase -i: Manejar "combinación de <n> confirmaciones" con GETTEXT_POISON", 2018-04-27, Git 2.18) cambió la forma en que la confirmación individual los mensajes se etiquetan cuando se aplastan confirmaciones juntas.
Al hacerlo, se introdujo una regresión donde la numeración de los mensajes está desactivada por uno. Esta confirmación corrige eso y agrega una prueba para la numeración.

 97
Author: VonC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-21 21:17:05

Usando Soft Reset En lugar de Rebase para aplastar el Historial de GIT

Creo que la longitud de las respuestas de VonC dice mucho literally literalmente about sobre lo complicado que es git rebase. Esta es mi extensión de otra respuesta a una pregunta mía .

  1. Tienes una rama ticket-201 de la que te ramificaste master. Quieres fingir que todas las confirmaciones de ticket-201 nunca ocurrieron, pero que hiciste todo el trabajo de una sola vez.
  2. Restablecimiento suave de la rama punto usando git reset --soft hash donde hash debe ser un hash de confirmación que está en el registro de ticket-201.
  3. Confirma tus cambios usando add then commit. Ahora el historial de rama solo tendrá el primer commit y el nuevo con las cosas nuevas.

Inventando Historias De Confirmaciones Arbitrarias en Diferentes Ramas

Usando resets puedes reescribir el historial como mejor te parezca, aunque tus ediciones perderán el encanto de tener la marca de tiempo correcta. Asumiendo que no te importa eso (el las horas / fechas en sus archivos serán suficientes, tal vez?), o si quieres jugar con las confirmaciones a medida que avanzas, puedes seguir estos pasos:

  1. Comprueba una nueva rama en commit0 (finge que es un hash): git checkout -b new-history commit0
  2. Ahora puedes obtener los archivos de commit5: git reset --hard commit5
  3. Vuelva a su punto de índice: git reset --soft commit0
  4. Commit y este será el segundo commit en la rama.

Esta idea es simple, eficaz y flexible.

 27
Author: Dan Rosenstark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:29

Usando Squash En Su Lugar

Recientemente, he estado trabajando en otra rama y usando squash. La otra rama se llama temp, y luego uso git merge temp --squash para traerla a la rama real que se envía al servidor.

El flujo de trabajo es algo así, suponiendo que estoy trabajando en Ticket65252:

git branch -d temp #remove old temp bbranch
git checkout -b temp
# work work work, committing all the way
git checkout Ticket65252
git merge temp --squash
git commit -m "Some message here"

Ventajas sobre el uso rebase? Mucho menos complicado.

Ventajas sobre el uso de reset --hard y luego reset --soft? Menos confuso y ligeramente menos error propenso.

 6
Author: Dan Rosenstark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-12 20:36:03

Usa git rebase-i para recoger y aplastar tus commits juntos.

 0
Author: Jason Punyon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-20 16:00:38