¿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 -ienfoque yreset --softes,rebase -ime permite retener al autor de confirmación, mientrasreset --softque 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 rebaseogit 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 -ique 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 -fpero por lo demás fue encantador, gracias.git push --forcedespués para que tome el compromisoPuede usar
git merge --squashpara 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 statusesté limpio (yagit reset --hardque descartará los cambios por etapas y por etapas)Entonces:
La documentación de
git mergedescribe la--squashopció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 commitsugerido 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 mergea hacer una de las cosas para las quegit rebaseestá específicamente diseñado?git merge --squashtambién es más fácil de usar en un script. Esencialmente, el razonamiento fue que no necesitas la "interactividad"git rebase -ipara nada.git merge --squashes 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 resetcuando 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 commitEl mensaje de confirmación se rellenará previamente en función de la calabaza.
fuente
gitkpara 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~Nla 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
~/.gitconfigahora debe contener este alias:Uso:
... Que aplasta automáticamente las últimas
Nconfirmaciones, 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 --amendmodificarlo manualmente. (O edite el alias para que coincida con sus gustos).fuente
git squash -m "New summary."y heNdeterminado automáticamente como el número de confirmaciones no apresuradas.git commit --amendpara 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~3cuando 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
picka un comando de su elección.Prefiero usar el comando
fixupya 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 usarsquashofixupcomo 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 logogitky 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 cambiopickafixup.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
picken la línea 1. Si eligesquashofixuppara 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 LogCombine to one commitdesde 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
pickuna confirmación ysquashlas 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
--continuey vim:x.git addconfigurado correctamente los archivos que usagit rebase --continuepara pasar al siguiente commit y comenzar a fusionar.:xes 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 logProcedimiento 1
1) Identificar el hash corto de confirmación
Aquí incluso
git log --onelinetambién se puede usar para obtener hash corto.2) Si quieres aplastar (fusionar) las últimas dos confirmaciones
3) Esto abre un
nanoeditor para fusionar. Y se ve a continuación4) Cambie el nombre de la palabra
picka lasquashque está presente antesabcd1234. Después de cambiar el nombre, debería ser como a continuación.5) Ahora guarda y cierra el
nanoeditor. Presionectrl + oy presioneEnterpara guardar. Y luego presionectrl + xpara salir del editor.6) Luego, el
nanoeditor 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
ubuntushell. Si está utilizando un sistema operativo diferente (WindowsoMac), los comandos anteriores son los mismos, excepto el editor. Puede obtener un editor diferente.Procedimiento 2
--fixupopción yOLDCOMMITdeberí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. Esterebasecomando 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
--amendse pueden usar medios congit-commit.Aquí
--amendcombina los nuevos cambios para la última confirmacióncdababcdy genera una nueva ID de confirmación1d4ab2e1Conclusió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~2git commit -m "new commit message"git push --force2 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 --forcelo 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 logno coincide con lo que ves en GitHub.🐻 MORAL DE LA HISTORIA
git checkoutellasgit reset --softquegit commitque 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 -isiempre 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~5significarí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 --squashdespué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
rebasecorrectamente, 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 mastergit checkout -b new-branchgit checkout -b new-branch-tempgit checkout new-branchgit merge --squash new-branch-temp// pone todos los cambios en el escenariogit commit 'one message to rule them all'git pushfuente
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 mastervolver 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
-iopción GNU sed , los usuarios de Mac y Linux deberían estar de acuerdo con esto).fuente
git squash mastercuando 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 --graphgenera 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~2Esto 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 --mixedse 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