tener - ¿qué hace git log-oneline?
Recortar los compromisos de Git/aplastar la historia de Git (4)
Usa git rebase -i para elegir y aplastar tus commits juntos.
Compruebo mi código en una rama de Git cada pocos minutos más o menos, y los comentarios terminan siendo cosas como "Todo comenzó de nuevo" y otros absurdos.
Luego, cada pocos minutos / horas / días hago un compromiso serio con un comentario real como "Error reparado # 22.55, 3 ° vez". ¿Cómo puedo separar estos dos conceptos? Me gustaría poder eliminar todas mis confirmaciones frecuentes y dejar las serias.
¡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 la rápida reordenación de commit y edición de mensaje.
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 compromiso en lugar de reordenamiento manual, y por mensajes de limpieza más limpios)
Me gustaría poder eliminar todos mis checkins frecuentes y dejar los graves.
Esto se llama compromisos de aplastamiento .
Tienes un buen ejemplo de "limpieza de comités" en este artículo listo para Git :
(Nota: la función interactiva de rebase apareció desde septiembre de 2007 y permite aplastar o dividir o eliminar o reordenar commits: ver también la página de GitPro )
Una advertencia : solo haz esto en commits que no hayan sido enviados a un repositorio externo. Si otros han basado el trabajo fuera de los commits que va a eliminar, pueden ocurrir muchos conflictos. Simplemente no reescriba su historial si ha sido compartido con otros.
texto alternativo http://www.gitready.com/images/squash1.png
Los últimos 4 commits serían mucho más felices si estuvieran juntos
$ 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 los últimos cuatro commits desde donde
HEAD
está conHEAD~4
.
Vamos a aplastar todo en un compromiso.
Entonces, 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 los cuatro commits en el primer commit de la lista. Una vez hecho 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 de la nueva confirmación en función del resto de las confirmaciones involucradas 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 otra vez ...
texto alternativo http://www.gitready.com/images/squash2.png
Nota: para propósitos de "commit squashing", Git1.7 (febrero de 2010) ha introducido 2 nuevos elementos (como lo menciona Dustin en el comentario):
- "
git rebase -i
" aprendió acción nueva "fixup
" que aplasta el cambio pero no afecta el mensaje de registro existente.- "
git rebase -i
" también aprendió--autosquash
opción que es útil junto con la nueva acción de "corrección".
Ambos (acción de --autosquash
y opción de --autosquash
) se ilustran en esta entrada del blog de Thechnosorcery Networks . Esas características se han cocinado desde el pasado junio de 2009 y se debatieron aún más en diciembre pasado .
La acción de fixup
o directiva es para aplastar una confirmación que habría reordenado manualmente en la lista de edición de confirmación de una rebase --interactive
, mientras ignora el segundo mensaje de confirmación, lo que hará que la edición del mensaje sea más rápida (puede simplemente guardarla: la confirmación aplastada solo tendrá el primer mensaje de confirmación)
El mensaje de confirmación resultante solo será el primero 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 realizar automáticamente el proceso de reordenamiento de compromiso:
Si sabes en qué compromiso quieres aplastar algo puedes hacerlo con un mensaje de "
squash! $other_commit_subject
squash! $other_commit_subject
". Entonces, si ejecuta@git rebase --interactive --autosquash commitish@
, la línea se establecerá automáticamente como squash, y se colocará debajo del commit con el asunto de $ other_commit_subject.
(En realidad, squash!
Solo puede usar el comienzo 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(-)
¿Ver? Aquí el tercer compromiso utiliza solo el comienzo del primer mensaje de compromiso.
Una rebase --interactive --autosquash
moverá la confirmación aplastada debajo de la relevante:
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:
# 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 por defecto es que mantendría la operación de aplastamiento registrada en el mensaje de confirmación.
¡Pero con la corrección! directivo, podría mantener ese aplastamiento "invisible" en el mensaje de compromiso, mientras se beneficia del reordenamiento de compromiso automático con la opción --autosquash
(y el hecho de que su segundo mensaje de compromiso se basa en el primer compromiso que desea aplastar con )
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
¡Fíjate que la
fixup!
El mensaje de compromiso ya está comentado.
Puede guardar el mensaje tal como está, y su mensaje de confirmación original se mantendrá .
Muy útil para incluir cambios cuando te das cuenta de que olvidaste agregar parte de una confirmación anterior .
Ahora, si quiere corregir o aplastar en función de la confirmación anterior que acaba de hacer, Jacob Helwig (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 para hacer una rebase interactiva que siempre se beneficiará de la reordenación automática de las confirmaciones destinadas a aplastarse:
[alias]
ri = rebase --interactive --autosquash
Actualización para Git 2.18 (Q2 2018): " git rebase -i
" a veces dejó 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 se ha corregido .
Consulte commit 15ef693 , commit dc4b5bc , commit e12a7ef , commit d5bc6f2 (27 de abril de 2018) por Johannes Schindelin ( dscho
) .
(Fusionado por Junio C Hamano - gitster
- en commit 4a3bf32 , 23 de mayo de 2018)
rebase --skip
: mensaje de confirmación de limpieza después de una corrección / squash fallidaDurante una serie de comandos de corrección / compresión, la rebase interactiva genera un mensaje de confirmación con comentarios. Esto se presentará al usuario en el editor si al menos uno de esos comandos fue una
squash
.En cualquier caso, el mensaje de compromiso se limpiará eventualmente, eliminando todos los comentarios intermedios, en el paso final de dicha cadena de reparación / squash.
Sin embargo, si el último comando de corrección / squash en dicha cadena falla con los conflictos de combinación, y si el usuario decide saltearlo (o resolverlo en un árbol de trabajo limpio y luego continuar la rebase), el código actual no puede limpiar el cometer mensaje
Esta confirmación corrige ese comportamiento.
La solución es un poco más complicada de lo que parece, porque no se trata solo de la cuestión de si somos
git rebase --skip
una corrección o squash. También se trata de eliminar el mensaje de compromiso saltado de fixup / squash del mensaje de compromiso acumulado. Y también se trata de la cuestión de si deberíamos dejar que el usuario edite el mensaje de compromiso final o no ("¿Hubo una calabaza en la cadena que no se saltó ?").Por ejemplo, en este caso, querremos corregir el mensaje de confirmación, pero no abrirlo en un editor:
pick <- succeeds fixup <- succeeds squash <- fails, will be skipped
Aquí es donde el archivo de
current-fixups
recientemente introducido es realmente útil. Una mirada rápida y podemos determinar si hubo una calabaza sin saltar. Solo necesitamos asegurarnos de mantenerlo actualizado con respecto a los comandos de reparación / squash omitidos. Como beneficio adicional, incluso podemos evitar comprometernos innecesariamente, por ejemplo, cuando solo hubo una corrección, falló y se omitió.Solucionar solo el error donde el mensaje de confirmación final no se limpió correctamente, pero sin arreglar el resto, hubiera sido más complicado que solucionarlo todo de una vez, por lo tanto, este compromiso agrupa más de una preocupación.
Usar Squash en cambio
Recientemente, he estado trabajando en otra sucursal y usando squash
. La otra rama se llama temp, y luego uso git merge temp --squash
para llevarla a la rama real que se git merge temp --squash
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 de rebase
? Mucho menos complicado.
Ventajas sobre el uso del reset --hard
y luego reset --soft
? Menos confuso y ligeramente menos propenso a errores.
Uso de reinicio suave en lugar de rebase para aplastar el historial de GIT
Creo que la duración de las respuestas de VonC dice mucho, literalmente, sobre lo complicado que es el git rebase
. Esta es mi extensión de otra respuesta a una pregunta mía .
- Tienes un
ticket-201
ramificaciónticket-201
que has ramificado delmaster
. Desea pretender que todos los compromisos delticket-201
nunca ocurrieron, pero que hizo todo el trabajo de una vez. - Restablecimiento suave al punto de bifurcación con
git reset --soft hash
dondehash
debe ser un hash de confirmación que está en el registro deticket-201
. - Confirme sus cambios usando agregar y confirmar. Ahora el historial de la sucursal solo tendrá el primer compromiso y el nuevo con el nuevo material.
Hacer historias de comisiones arbitrarias en diferentes ramas
Con los restablecimientos puede volver a escribir el historial como mejor le parezca, aunque sus ediciones perderán el encanto de tener la marca de tiempo correcta. Suponiendo que no le importe (¿las horas / fechas en sus archivos serán suficientes, quizás?), O si quiere jugar con las confirmaciones sobre la marcha, puede seguir estos pasos:
- Pagar una nueva sucursal en
commit0
(pretender que es un hash):git checkout -b new-history commit0
- Ahora puede obtener los archivos de
commit5
:git reset --hard commit5
- Regrese a su punto de índice:
git reset --soft commit0
- Comprometerse y este será el segundo compromiso en la rama.
Esta idea es simple, efectiva y flexible.