¿Cómo puedo aplastar mis últimas confirmaciones X juntas en una confirmación usando Git?
git
rebase
squash
git-squash
Markdorison
fuente
fuente
Respuestas:
Use
git rebase -i <after-this-commit>
y reemplace "pick" en la segunda y posteriores confirmaciones con "squash" o "arreglo", como se describe en el manual .En este ejemplo,
<after-this-commit>
es el hash SHA1 o la ubicación relativa del HEAD de la rama actual desde la cual se analizan los commits para el comando rebase. Por ejemplo, si el usuario desea ver 5 confirmaciones del HEAD actual en el pasado, el comando esgit rebase -i HEAD~5
.fuente
<after-this-commit>
?<after-this-commit>
es commit X + 1, es decir, padre del commit más antiguo que desea aplastar.rebase -i
enfoque yreset --soft
es,rebase -i
me permite retener al autor de confirmación, mientrasreset --soft
que me permite volver a comprometerme. A veces necesito aplastar las confirmaciones de solicitudes de extracción pero manteniendo la información del autor. A veces necesito restablecer soft en mis propios commits. Votos a favor de ambas grandes respuestas de todos modos.Puede hacerlo con bastante facilidad sin
git rebase
ogit merge --squash
. En este ejemplo, aplastaremos los últimos 3 commits.Si desea escribir el nuevo mensaje de confirmación desde cero, esto es suficiente:
Si desea comenzar a editar el nuevo mensaje de confirmación con una concatenación de los mensajes de confirmación existentes (es decir, similar a lo
git rebase -i
que comenzaría con una lista de instrucciones pick / squash / squash / ... / squash ), entonces debe extraer esos mensajes y pasar ellos agit commit
:Ambos métodos aplastan los últimos tres commits en un solo nuevo commit de la misma manera. El restablecimiento parcial solo vuelve a apuntar HEAD a la última confirmación que no desea aplastar. Ni el índice ni el árbol de trabajo son tocados por el restablecimiento parcial, dejando el índice en el estado deseado para su nueva confirmación (es decir, ya tiene todos los cambios de las confirmaciones que está a punto de "descartar").
fuente
git rebase --squash-recent
, o inclusogit commit --amend-many
.branch@{upstream}
(o solo@{upstream}
para la rama actual; en ambos casos, la última parte se puede abreviar@{u}
; vea gitrevisions ). Esto puede diferir de su "último envío empujado" (por ejemplo, si alguien más empujó algo que se construyó sobre su empuje más reciente y luego lo obtuvo), pero parece que podría estar cerca de lo que desea.push -f
pero por lo demás fue encantador, gracias.git push --force
después para que tome el compromisoPuede usar
git merge --squash
para esto, que es un poco más elegante quegit rebase -i
. Supongamos que estás en master y quieres aplastar los últimos 12 commits en uno.ADVERTENCIA: Primero asegúrese de comprometer su trabajo, verifique que
git status
esté limpio (yagit reset --hard
que descartará los cambios por etapas y por etapas)Entonces:
La documentación de
git merge
describe la--squash
opción con más detalle.Actualización: la única ventaja real de este método sobre el más simple
git reset --soft HEAD~12 && git commit
sugerido por Chris Johnsen en su respuesta es que obtiene el mensaje de confirmación rellenado previamente con cada mensaje de confirmación que está aplastando.fuente
git rebase -i
, pero no das una razón por la cual. Tentativamente estoy haciendo esto porque me parece que, de hecho, lo contrario es cierto y esto es un truco; ¿no estás ejecutando más comandos de los necesarios solo para forzargit merge
a hacer una de las cosas para las quegit rebase
está específicamente diseñado?git merge --squash
también es más fácil de usar en un script. Esencialmente, el razonamiento fue que no necesitas la "interactividad"git rebase -i
para nada.git merge --squash
es menos probable que produzca conflictos de fusión frente a movimientos / eliminaciones / cambios de nombre en comparación con el rebase, especialmente si se está fusionando desde una sucursal local. (descargo de responsabilidad: basado en una sola experiencia, ¡HEAD@{1}
solo estar seguro, por ejemplo, cuando su flujo de trabajo se interrumpe durante una hora por un corte de energía, etc.Recomiendo evitar
git reset
cuando sea posible, especialmente para los novatos de Git. A menos que realmente necesite automatizar un proceso basado en una serie de confirmaciones, hay una forma menos exótica ...git merge --squash (working branch name)
git commit
El mensaje de confirmación se rellenará previamente en función de la calabaza.
fuente
gitk
para etiquetar la línea de código que está aplastando y también etiquetar la base sobre la cual se debe aplastar. En el caso normal, ambas etiquetas ya existirán, por lo que se puede omitir el paso (1).git branch your-feature && git reset --hard HEAD~N
la forma más conveniente. Sin embargo, involucra git reset nuevamente, lo que esta respuesta intentó evitar.Según la respuesta de Chris Johnsen ,
Agregue un alias global de "squash" desde bash: (o Git Bash en Windows)
... o usando el símbolo del sistema de Windows:
Su
~/.gitconfig
ahora debe contener este alias:Uso:
... Que aplasta automáticamente las últimas
N
confirmaciones, inclusive.Nota: El mensaje de confirmación resultante es una combinación de todas las confirmaciones aplastadas, en orden. Si no está satisfecho con eso, siempre puede
git commit --amend
modificarlo manualmente. (O edite el alias para que coincida con sus gustos).fuente
git squash -m "New summary."
y heN
determinado automáticamente como el número de confirmaciones no apresuradas.git commit --amend
para cambiar aún más el mensaje, pero este alias le permite tener un buen comienzo sobre lo que debería estar en el mensaje de confirmación.Gracias a esta útil publicación de blog , descubrí que puedes usar este comando para aplastar las últimas 3 confirmaciones:
Esto es útil ya que funciona incluso cuando se encuentra en una sucursal local sin información de seguimiento / repositorio remoto.
El comando abrirá el editor interactivo de rebase, que luego le permite reordenar, aplastar, reformular, etc., como de costumbre.
Usando el editor de rebase interactivo:
El editor de rebase interactivo muestra los últimos tres commits. Esta restricción fue determinada por
HEAD~3
cuando se ejecuta el comandogit rebase -i HEAD~3
.El commit más reciente
HEAD
, se muestra primero en la línea 1. Las líneas que comienzan con a#
son comentarios / documentación.La documentación que se muestra es bastante clara. En cualquier línea puede cambiar el comando de
pick
a un comando de su elección.Prefiero usar el comando
fixup
ya que esto "aplasta" los cambios del commit en el commit en la línea de arriba y descarta el mensaje del commit.Como el commit en la línea 1 es
HEAD
, en la mayoría de los casos dejaría esto comopick
. No puede usarsquash
ofixup
como no hay otro commit para aplastar el commit.También puede cambiar el orden de las confirmaciones. Esto le permite aplastar o reparar confirmaciones que no son adyacentes cronológicamente.
Un práctico ejemplo cotidiano.
Recientemente he comprometido una nueva característica. Desde entonces, he cometido dos correcciones de errores. Pero ahora he descubierto un error (o tal vez solo un error de ortografía) en la nueva función que cometí. ¡Que molesto! ¡No quiero un nuevo commit que contamine mi historial de commit!
Lo primero que hago es corregir el error y hacer una nueva confirmación con el comentario
squash this into my new feature!
.Luego ejecuto
git log
ogitk
y obtengo el SHA de confirmación de la nueva característica (en este caso1ff9460
).A continuación, traigo el editor interactivo de rebase con
git rebase -i 1ff9460~
. El~
cuando la confirmación SHA dice el editor para incluir que cometen en el editor.A continuación, muevo la confirmación que contiene el arreglo (
fe7f1e0
) debajo de la característica de confirmación y cambiopick
afixup
.Al cerrar el editor, la solución quedará aplastada en la función commit y mi historial de commit se verá bien y limpio.
Esto funciona bien cuando todos los commits son locales, pero si intenta cambiar cualquier commit ya enviado al control remoto, ¡realmente puede causar problemas a otros desarrolladores que hayan verificado la misma rama!
fuente
pick
en la línea 1. Si eligesquash
ofixup
para la confirmación en la línea 1, git mostrará un mensaje que dice "error: no se puede" arreglar "sin una confirmación previa". Luego le dará la opción de arreglarlo: "Puede arreglar esto con 'git rebase --edit-todo' y luego ejecutar 'git rebase --continue'". o simplemente puede abortar y comenzar de nuevo: "O puede abortar el rebase con 'git rebase --abort'".Si usa TortoiseGit, puede la función
Combine to one commit
:Show Log
Combine to one commit
desde el menú contextualEsta función ejecuta automáticamente todos los pasos necesarios de un solo git. Desafortunadamente, solo está disponible para Windows.
fuente
Para hacer esto, puede usar el siguiente comando git.
n (= 4 aquí) es el número de la última confirmación. Entonces tienes las siguientes opciones,
Actualice como abajo
pick
una confirmación ysquash
las otras en la más reciente,Para más detalles, haga clic en el enlace
fuente
Basado en este artículo , encontré este método más fácil para mi caso de uso.
Mi rama 'dev' estaba por delante de 'origin / dev' en 96 commits (por lo que estos commits aún no se enviaron al control remoto).
Quería aplastar estos commits en uno antes de impulsar el cambio. Prefiero restablecer la rama al estado de 'origen / desarrollo' (esto dejará sin cambios todos los cambios de las 96 confirmaciones) y luego los confirmará de inmediato:
fuente
En la rama en la que desea combinar las confirmaciones, ejecute:
ejemplo:
Esto abrirá el editor de texto y debe cambiar la 'selección' frente a cada confirmación con 'squash' si desea que estas confirmaciones se fusionen. De la documentación:
p, pick = usar commit
s, squash = usar commit, pero fusionarse con el commit anterior
Por ejemplo, si está buscando fusionar todos los commits en uno, el 'pick' es el primer commit que realizó y todos los futuros (colocados debajo del primero) deben establecerse en 'squash'. Si usa vim, use : x en modo de inserción para guardar y salir del editor.
Luego para continuar el rebase:
Para obtener más información sobre esta y otras formas de reescribir su historial de confirmación, consulte esta publicación útil
fuente
--continue
y vim:x
.git add
configurado correctamente los archivos que usagit rebase --continue
para pasar al siguiente commit y comenzar a fusionar.:x
es un comando que guardará los cambios del archivo cuando use vim, vea estoLa respuesta de Anomies es buena, pero me sentí insegura al respecto, así que decidí agregar un par de capturas de pantalla.
Paso 0: registro de git
Mira dónde estás
git log
. Lo más importante es encontrar el hash de confirmación de la primera confirmación que no desea eliminar. Entonces solo el:Paso 1: git rebase
Ejecutar
git rebase -i [your hash]
, en mi caso:Paso 2: elige / aplasta lo que quieras
En mi caso, quiero aplastar todo en el commit que fue primero en el tiempo. El orden es del primero al último, así que exactamente al revés
git log
. En mi caso, quiero:Paso 3: ajustar los mensajes
Si seleccionó solo una confirmación y aplastó el resto, puede ajustar un mensaje de confirmación:
Eso es. Una vez que guarde esto (
:wq
), ya está. Míralo congit log
.fuente
git log
Procedimiento 1
1) Identificar el hash corto de confirmación
Aquí incluso
git log --oneline
también se puede usar para obtener hash corto.2) Si quieres aplastar (fusionar) las últimas dos confirmaciones
3) Esto abre un
nano
editor para fusionar. Y se ve a continuación4) Cambie el nombre de la palabra
pick
a lasquash
que está presente antesabcd1234
. Después de cambiar el nombre, debería ser como a continuación.5) Ahora guarda y cierra el
nano
editor. Presionectrl + o
y presioneEnter
para guardar. Y luego presionectrl + x
para salir del editor.6) Luego, el
nano
editor se abre nuevamente para actualizar los comentarios; si es necesario, actualícelo.7) Ahora está aplastado con éxito, puedes verificarlo revisando los registros.
8) Ahora presiona para repo. Nota para agregar
+
signo antes del nombre de la sucursal. Esto significa empuje forzado.Nota: Esto se basa en el uso de git en
ubuntu
shell. Si está utilizando un sistema operativo diferente (Windows
oMac
), los comandos anteriores son los mismos, excepto el editor. Puede obtener un editor diferente.Procedimiento 2
--fixup
opción yOLDCOMMIT
debería estar en la que necesitamos fusionar (aplastar) esta confirmación.Ahora esto crea una nueva confirmación sobre HEAD con
fixup1 <OLDCOMMIT_MSG>
.OLDCOMMIT
.Aquí
^
significa el compromiso anterior conOLDCOMMIT
. Esterebase
comando abre una ventana interactiva en un editor (vim o nano) en el que no necesitamos hacer nada, solo guardar y salir es suficiente. Debido a que la opción pasada a esto moverá automáticamente la última confirmación al lado de la confirmación anterior y cambiará la operación afixup
(equivalente a squash). Luego rebase continúa y termina.Procedimiento 3
--amend
se pueden usar medios congit-commit
.Aquí
--amend
combina los nuevos cambios para la última confirmacióncdababcd
y genera una nueva ID de confirmación1d4ab2e1
Conclusión
fuente
Para aplastar los últimos 10 commits en 1 solo commit:
Si también desea actualizar la rama remota con la confirmación aplastada:
fuente
Si está en una rama remota (llamada
feature-branch
) clonada desde un Golden Repository (golden_repo_name
), entonces esta es la técnica para aplastar sus confirmaciones en una:Mira el repo dorado
Cree una nueva rama a partir de ella (repo de oro) de la siguiente manera
Squash se fusiona con su sucursal local que ya tiene
Confirma tus cambios (esta será la única confirmación que va en dev-branch)
Empuje la rama a su repositorio local
fuente
Lo que puede ser realmente conveniente:
encuentre el hash de confirmación que desea aplastar, por ejemplo
d43e15
.Ahora usa
fuente
Esto es super duper kludgy, pero de una manera genial, así que lo arrojaré al ring:
Traducción: proporcione un nuevo "editor" para git que, si el nombre de archivo que se va a editar es
git-rebase-todo
(la solicitud de rebase interactiva) cambia todo excepto la primera "selección" a "squash", y de lo contrario genera vim, de modo que cuando se le solicite para editar el mensaje de confirmación aplastado, obtienes vim. (Y obviamente estaba aplastando los últimos cinco commits en branch foo, pero puedes cambiar eso como quieras).Sin embargo, probablemente haría lo que Mark Longair me sugirió .
fuente
Si desea dividir cada commit en un solo commit (por ejemplo, al lanzar un proyecto públicamente por primera vez), intente:
fuente
Solución simple 2020 sin rebase:
git reset --soft HEAD~2
git commit -m "new commit message"
git push --force
2 significa que los dos últimos commits serán aplastados. Puedes reemplazarlo por cualquier número
fuente
Creo que la forma más fácil de hacer esto es creando una nueva rama fuera de master y fusionando --quasión de la rama característica.
Entonces tiene todos los cambios listos para confirmar.
fuente
One-liner simple que siempre funciona, dado que actualmente está en la rama que desea aplastar, master es la rama de la que se originó, y la última confirmación contiene el mensaje de confirmación y el autor que desea utilizar:
fuente
si, por ejemplo, desea aplastar las últimas 3 confirmaciones a una única confirmación en una rama (repositorio remoto) en, por ejemplo: https://bitbucket.org
Lo que hice es
fuente
⚠️ ADVERTENCIA: "Mis últimas confirmaciones de X" pueden ser ambiguas.
En esta historia muy abreviada del depósito https://github.com/fleetwood-mac/band-history , ha abierto una solicitud de extracción para fusionarse en el compromiso de Bill Clinton en el compromiso original (
MASTER
) de Fleetwood Mac.Abriste una solicitud de extracción y en GitHub ves esto:
Cuatro commits:
Pensando que a nadie le importaría leer el historial completo del repositorio. (En realidad hay un repositorio, ¡haga clic en el enlace de arriba!) Decide aplastar estas confirmaciones. Entonces vas y corres
git reset --soft HEAD~4 && git commit
. Luegogit push --force
lo colocas en GitHub para limpiar tu PR.¿Y que pasa? Acabas de hacer un solo compromiso que pasa de Fritz a Bill Clinton. Porque olvidaste que ayer estabas trabajando en la versión de Buckingham Nicks de este proyecto. Y
git log
no coincide con lo que ves en GitHub.🐻 MORAL DE LA HISTORIA
git checkout
ellasgit reset --soft
quegit commit
que se dobla directamente de la de la defuente
Si no le importan los mensajes de confirmación de las confirmaciones intermedias, puede usar
fuente
Si está trabajando con GitLab, puede hacer clic en la opción Squash en la Solicitud de fusión como se muestra a continuación. El mensaje de confirmación será el título de la Solicitud de fusión.
fuente
donde el número de ^ es X
(en este caso, aplasta los dos últimos commits)
fuente
Además de otras excelentes respuestas, me gustaría agregar cómo
git rebase -i
siempre me confunde con el orden de confirmación: ¿más antiguo a más nuevo o viceversa? Entonces este es mi flujo de trabajo:git rebase -i HEAD~[N]
, donde N es el número de confirmaciones a las que quiero unirme, comenzando por la más reciente . Entoncesgit rebase -i HEAD~5
significaría "aplastar los últimos 5 commits en uno nuevo";Fuentes y lecturas adicionales: # 1 , # 2 .
fuente
¿Qué pasa con una respuesta a la pregunta relacionada con un flujo de trabajo como este?
merge --squash
después del RP, pero el equipo pensó que eso retrasaría el proceso).No he visto un flujo de trabajo como ese en esta página. (Esos pueden ser mis ojos). Si entiendo
rebase
correctamente, múltiples fusiones requerirían múltiples resoluciones de conflictos . ¡NO quiero ni siquiera pensar en eso!Entonces, esto parece funcionar para nosotros.
git pull master
git checkout -b new-branch
git checkout -b new-branch-temp
git checkout new-branch
git merge --squash new-branch-temp
// pone todos los cambios en el escenariogit commit 'one message to rule them all'
git push
fuente
Creo que una solución más genérica no es especificar confirmaciones 'N', sino más bien la rama / commit-id que desea aplastar. Esto es menos propenso a errores que contar las confirmaciones hasta una confirmación específica: simplemente especifique la etiqueta directamente, o si realmente desea contar puede especificar HEAD ~ N.
En mi flujo de trabajo, comienzo una rama, y mi primer compromiso en esa rama resume el objetivo (es decir, generalmente es lo que presionaré como el mensaje 'final' para la característica al repositorio público). Entonces, cuando haya terminado, todo Quiero
git squash master
volver al primer mensaje y luego estoy listo para presionar.Yo uso el alias:
Esto volcará el historial que se está aplastando antes de que lo haga; esto le brinda la oportunidad de recuperarse al obtener una ID de confirmación anterior de la consola si desea revertirla. (Los usuarios de Solaris notan que usa la
-i
opción GNU sed , los usuarios de Mac y Linux deberían estar de acuerdo con esto).fuente
git squash master
cuando nos desprotegen. que va a pasar vamos a ocultar el conflicto?En cuestión podría ser ambiguo lo que se entiende por "último".
por ejemplo,
git log --graph
genera lo siguiente (simplificado):Luego, las últimas confirmaciones por tiempo son H0, fusionar, B0. Para aplastarlos, deberá volver a crear la rama fusionada en commit H1.
El problema es que H0 contiene H1 y H2 (y generalmente más confirmaciones antes de la fusión y después de la ramificación) mientras que B0 no. Por lo tanto, debe administrar los cambios desde H0, fusionar, H1, H2, B0 al menos.
Es posible usar rebase pero de manera diferente que en otras respuestas mencionadas:
rebase -i HEAD~2
Esto le mostrará las opciones de elección (como se menciona en otras respuestas):
Ponga la calabaza en lugar de recoger a H0:
Después de guardar y salir, rebase aplicará commits a su vez después de H1. Eso significa que le pedirá que resuelva los conflictos nuevamente (donde HEAD será H1 al principio y luego acumulará confirmaciones a medida que se apliquen).
Una vez que finalice el rebase, puede elegir el mensaje para H0 y B0 aplastados:
PD: si solo hace un reinicio a BO: (por ejemplo, el uso
reset --mixed
se explica con más detalle aquí https://stackoverflow.com/a/18690845/2405850 ):luego se comprimen los cambios B0 de H0, H1, H2 (perder completamente los cambios después de la ramificación y antes de la fusión.
fuente
1) reinicio de git --soft HEAD ~ n
n - Número de la confirmación, es necesario aplastar
2) git commit -m "nuevo mensaje de confirmación"
3) git push origin branch_name --force
fuente