¿Hay alguna forma de aplastar una serie de confirmaciones de forma no interactiva?


Respuestas:


146

Asegúrese de que su árbol de trabajo esté limpio, luego

git reset --soft HEAD~3
git commit -m 'new commit message'

@wilhelmtell: ¡Genial! Ahora, ¿podría construir un alias de git, por ejemplo, "mysquash 3 'algún mensaje'", para reducir esto a una línea?
Phillip

5
Si es solo el número de líneas:git reset --soft HEAD~3 && git commit -m "my message"
KingCrunch

7
@Phillip: puede incrustar una función de shell en el alias de git. git config alias.mysquash '!f(){ git reset --soft HEAD~$1 && git commit ${2:+-m "$2"}; };f'. git mysquash 3 'some message'funcionará, pero también lo modifiqué, por git musquash 3lo que omitiré la marca -m por completo para que obtenga la git commitinterfaz de usuario interactiva en ese caso.
Lily Ballard

3
Solo para que quede claro: no es lo mismo que una calabaza. Un squash también fusionará los mensajes de confirmación. Si realiza un restablecimiento parcial, perderá todos los mensajes de las confirmaciones. Si quieres aplastar, prueba stackoverflow.com/a/27697274/974186
René Link

1
@sebnukem: es entonces cuando intentamos empujar la rama y el control remoto está configurado para rechazar empujes forzados.
avmohan

30

Personalmente, me gusta la solución de Wilhelmtell :

git reset --soft HEAD~3
git commit -m 'new commit message'

Sin embargo, hice un alias con alguna comprobación de errores para que puedas hacer esto:

git squash 3 'my commit message'

Recomiendo configurar alias que realmente ejecuten scripts para que sea más fácil (a) codificar sus scripts y (b) hacer un trabajo más complejo con la verificación de errores. A continuación se muestra un script que hace el trabajo de squash y, a continuación, hay un script para configurar sus alias de git.

Script para aplastar (squash.sh)

#!/bin/bash
#

#get number of commits to squash
squashCount=$1

#get the commit message
shift
commitMsg=$@

#regular expression to verify that squash number is an integer
regex='^[0-9]+$'

echo "---------------------------------"
echo "Will squash $squashCount commits"
echo "Commit message will be '$commitMsg'"

echo "...validating input"
if ! [[ $squashCount =~ $regex ]]
then
    echo "Squash count must be an integer."
elif [ -z "$commitMsg" ]
then
    echo "Invalid commit message.  Make sure string is not empty"
else
    echo "...input looks good"
    echo "...proceeding to squash"
    git reset --soft HEAD~$squashCount
    git commit -m "$commitMsg"
    echo "...done"
fi

echo
exit 0

Luego, para conectar ese script squash.sh a un alias de git, cree otro script para configurar sus alias de git así ( create_aliases.command o create_aliases.sh ):

#!/bin/sh
echo '-----------------------'
echo 'adding git aliases....'
echo '-----------------------'
echo
git config --global alias.squash "!bash -c 'bash <path to scripts directory>/squash.sh \$1 \$2' -"
#add your other git aliases setup here
#and here
#etc.
echo '------------------------------------'
echo 'here is your global gitconfig file:'
echo '------------------------------------'
more ~/.gitconfig
echo 
echo
echo '----------------'
echo 'end of script...'
echo '----------------'

4
También puede ponerlo en $PATHnamed git-squash.shy automáticamente se le asignará un alias git squash. No cambié tu respuesta, por si acaso hay una razón para usar el create-aiases.shguión que no conozco.

Básicamente, utilizo el script create_aliases.command para que incluso nuestros PM y diseñadores puedan configurar fácilmente. Simplemente haga doble clic en el script y todos estarán configurados (especialmente porque tengo el script de configuración en nuestro repositorio y se conoce la ruta relativa). Entonces ni siquiera necesitan reiniciar la terminal.
n8tr

1
Intenté esto y lee mi mensaje de confirmación como recuento de aplastamiento y falla porque no es un número entero.
Minthos

1
La solución fue agregar - después de /squash.sh \ $ 1 \ $ 2 '
Minthos

1
Me gusta la idea de la solución, pero el comentario anterior aún no se tiene en cuenta en la solución. Debe haber un signo menos entre las comillas simples y dobles.
física

10

Para agregar a la respuesta de wilhelmtell, me resulta conveniente realizar un restablecimiento parcialHEAD~2 y luego enmendar la confirmación de HEAD~3:

git reset --soft HEAD~2
git commit --all --amend --no-edit    

Esto fusionará todas las confirmaciones con la HEAD~3confirmación y usará su mensaje de confirmación. Asegúrese de comenzar con un árbol de trabajo limpio.


2
Esta es la respuesta correcta porque aplasta una serie de confirmaciones que conducen a la cabeza y utiliza el mensaje de la primera confirmación. No es interactivo.
Landon Kuhn

9

Solía:

EDITOR="sed -i '2,/^$/s/^pick\b/s/'" git rebase -i <ref>

Funcionó bastante bien. Simplemente no intente tener un registro de confirmación con una línea que comience con "elegir" :)


Lamentablemente, esto no me funciona en Windows. Aún obtienes el editor interactivo.
Abergmeier

3

Use el siguiente comando para aplastar las últimas 4 confirmaciones dentro de la última confirmación:

git squash 4

Con el alias:

squash = !"f() { NL=$1; GIT_EDITOR=\"sed -i '2,$NL s/pick/squash/;/# This is the 2nd commit message:/,$ {d}'\"; git rebase -i HEAD~$NL; }; f"
sq = !git squash $1
sqpsf = !git squash $1 && git psf 

De https://github.com/brauliobo/gitconfig/blob/master/configs/.gitconfig


No especificó qué sistema operativo / shell se está utilizando aquí. Es probable que esta solución no funcione para todos los demás equipos de escritorio que utilizan las personas.
Rick-777

sí, deberías usar linux / bash | zsh para esto
brauliobo

2

Aquí hay una línea para aplastar las últimas 2 confirmaciones. En este ejemplo, se conservará el mensaje de la penúltima confirmación. Puede cambiar el mensaje como desee.

git commit -am "$(git log -1 --skip=1 --pretty=%B | xargs && git reset --soft HEAD~2)"

Este comando será muy útil si crea un alias para este comando y usa el alias en su lugar.


1

Para aplastar todo desde que la rama se bifurcó del maestro:

git reset --soft $(git merge-base --fork-point master) \
  && git commit --verbose --reedit-message=HEAD --reset-author

--reedit-message=HEADusará el mensaje de la última confirmación que no es parte del aplastamiento . Es probable que este no sea el que desea. Para que se incluya el mensaje de la primera confirmación , (1) reemplácelo HEADcon el hash de la confirmación de la que desea que se incluya el mensaje, o (2) salte a la primera confirmación que se incluirá y git commit --amend --reedit-message=HEAD. Esto es lo que hace la respuesta armoniosa.
Maëlan

-1

Puedes acercarte bastante

git rebase --en HEAD ~ 4 HEAD ~ master

Esto supone que está en el maestro con un historial lineal. No es del todo un squash porque descarta las confirmaciones intermedias. Debería enmendar el nuevo HEAD para modificar el mensaje de confirmación.


Gracias Greg; por 'descartes' ¿quiere decir que esas confirmaciones intermedias están sujetas a limpieza por parte de git gc?
Phillip

@Phillip Sí, las confirmaciones intermedias se convierten en basura al igual que el antiguo HEAD porque se reescribe para tener HEAD~4como padre.
Greg Bacon
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.