Existe uma maneira de esmagar uma série de commits de forma não interativa?

Respostas:

146

Certifique-se de que sua árvore de trabalho esteja limpa, então

git reset --soft HEAD~3
git commit -m 'new commit message'
Wilhelmtell
fonte
@wilhelmtell: Ótimo! Agora eu seria capaz de construir um alias git, por exemplo, "mysquash 3 'alguma mensagem'", para reduzir isso a uma linha?
Phillip
5
Se for apenas o número de linhas:git reset --soft HEAD~3 && git commit -m "my message"
KingCrunch 01 de
7
@Phillip: Você pode incorporar uma função shell no alias do git. git config alias.mysquash '!f(){ git reset --soft HEAD~$1 && git commit ${2:+-m "$2"}; };f'. git mysquash 3 'some message'funcionará, mas eu também ajustei para git musquash 3omitir totalmente o sinalizador -m para que você obtenha a git commitIU interativa nesse caso.
Lily Ballard,
3
Só para deixar claro: não é o mesmo que abóbora. Um squash também mesclará as mensagens de confirmação. Se você fizer um soft reset, perderá todas as mensagens dos commits. Se você quiser esmagar, experimente stackoverflow.com/a/27697274/974186
René Link
1
@sebnukem - É quando tentamos empurrar o branch e o remoto é configurado para rejeitar push push.
avmohan de
30

Pessoalmente, gosto da solução de wilhelmtell :

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

No entanto, criei um alias com algumas verificações de erro para que você possa fazer isso:

git squash 3 'my commit message'

Eu recomendo configurar aliases que realmente executem scripts para que seja mais fácil (a) codificar seus scripts e (b) fazer um trabalho mais complexo com verificação de erros. Abaixo está um script que faz o trabalho do squash e abaixo dele está um script para configurar seus aliases git.

Script para esmagar (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

Então, para conectar aquele script squash.sh a um alias git, faça outro script para configurar seus aliases git como este ( create_aliases.command ou 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 '----------------'
n8tr
fonte
4
Você também pode colocá-lo em $PATHnamed git-squash.she receberá automaticamente o alias de git squash. Não mudei sua resposta, apenas para o caso de haver um motivo para usar o create-aiases.shscript que não conheço.
Basicamente, uso o script create_aliases.command para que até mesmo nossos PMs e designers possam ser configurados facilmente. Basta um clique duplo no script e todos estão configurados (especialmente porque tenho o script de configuração em nosso repositório e o caminho relativo é conhecido). Então eles nem precisam reiniciar o terminal.
n8tr de
1
Eu tentei isso e ele lê minha mensagem de confirmação como contagem de squash e falha porque não é um número inteiro.
Minthos
1
A solução foi anexar - após /squash.sh \ $ 1 \ $ 2 '
Minthos
1
Gosto da ideia da solução, mas o comentário acima ainda não foi levado em consideração na solução. Deve haver um sinal de menos entre as aspas simples e duplas.
física
10

Para adicionar à resposta de wilhelmtell , acho conveniente fazer uma reinicialização suave para HEAD~2e, em seguida, alterar o commit de HEAD~3:

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

Isso irá mesclar todos os commits para o HEAD~3commit e usar sua mensagem de commit. Certifique-se de começar de uma árvore de trabalho limpa.

harmonioso
fonte
2
Esta é a resposta correta porque esmaga uma série de commits que levam ao head, e usa a mensagem do primeiro commit. Não é interativo.
Landon Kuhn
9

Eu usei:

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

Funcionou muito bem. Só não tente ter um log de commit com uma linha que começa com "pick" :)

Julien Wajsberg
fonte
Infelizmente, isso não funciona para mim no Windows. Ainda pegue o editor interativo.
abergmeier
3

Use o seguinte comando para esmagar os últimos 4 commits dentro do último commit:

git squash 4

Com o 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

brauliobo
fonte
Você não especificou qual sistema operacional / shell está sendo usado aqui. Esta solução provavelmente não funcionará para todos os outros desktops que as pessoas usam.
Rick-777 de
sim, você deve usar linux / bash | zsh para isso
brauliobo
2

Aqui está uma linha para esmagar os últimos 2 commits. Neste exemplo, a mensagem do penúltimo commit será retida. Você pode alterar a mensagem como desejar.

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

Este comando será muito útil se você criar um alias para este comando e usar o alias em seu lugar.

Jasir
fonte
1

Para esmagar tudo desde que o galho foi bifurcado do mestre:

git reset --soft $(git merge-base --fork-point master) \
  && git commit --verbose --reedit-message=HEAD --reset-author
Andy
fonte
--reedit-message=HEADirá usar a mensagem do último commit que não faz parte do squashing . Provavelmente não é o que você deseja. Em vez disso, para obter a mensagem do primeiro commit a ser incluído , (1) substitua HEADpelo hash do commit que você deseja que a mensagem, ou (2) pule para o primeiro commit a ser incluído e git commit --amend --reedit-message=HEAD. Isso é o que a resposta harmoniosa faz.
Maëlan
-1

Você pode chegar bem perto com

git rebase --onto HEAD ~ 4 HEAD ~ master

Isso pressupõe que você está no mestre com um histórico linear. Não é bem um squash porque descarta os commits intermediários. Você precisaria alterar o novo HEAD para modificar a mensagem de confirmação.

Greg Bacon
fonte
Obrigado Greg; por 'discards' você quer dizer que esses commits intermediários estão sujeitos à limpeza pelo git gc?
Phillip,
@Phillip Sim, os commits intermediários se tornam lixo, assim como o antigo HEAD porque foi reescrito para ter HEAD~4como pai.
Greg Bacon