Czy istnieje sposób na zgniatanie wielu zatwierdzeń w sposób nieinteraktywny?

Odpowiedzi:

147

W takim razie upewnij się, że drzewo robocze jest czyste

git reset --soft HEAD~3
git commit -m 'new commit message'
wilhelmtell
źródło
@wilhelmtell: Świetnie! Czy byłbym w stanie utworzyć alias git, np. „Mysquash 3 'jakaś wiadomość'”, aby ograniczyć to do jednej linii?
Phillip
5
Jeśli to tylko liczba wierszy:git reset --soft HEAD~3 && git commit -m "my message"
KingCrunch
7
@Phillip: Możesz osadzić funkcję powłoki w aliasie git. git config alias.mysquash '!f(){ git reset --soft HEAD~$1 && git commit ${2:+-m "$2"}; };f'. git mysquash 3 'some message'zadziała, ale poprawiłem go tak git musquash 3, że całkowicie pominie flagę -m, aby git commitw takim przypadku uzyskać interaktywny interfejs użytkownika.
Lily Ballard
3
Żeby było jasne: to nie to samo, co squash. Squash również połączy komunikaty o zmianach. Jeśli wykonasz miękki reset, utracisz wszystkie komunikaty o zatwierdzeniach. Jeśli chcesz grać w squasha, wypróbuj stackoverflow.com/a/27697274/974186
René Link
1
@sebnukem - wtedy próbujemy pchnąć gałąź, a pilot jest skonfigurowany tak, aby odrzucał pchnięcia siłowe.
avmohan,
30

Osobiście podoba mi się rozwiązanie Wilhelmtella :

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

Jednak utworzyłem alias z pewnym sprawdzaniem błędów, abyś mógł to zrobić:

git squash 3 'my commit message'

Zalecam skonfigurowanie aliasów, które faktycznie uruchamiają skrypty, aby łatwiej było (a) kodować skrypty i (b) wykonywać bardziej złożoną pracę ze sprawdzaniem błędów. Poniżej znajduje się skrypt, który działa jak squash, a poniżej jest skrypt do konfigurowania aliasów git.

Skrypt do zgniatania (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

Następnie, aby podłączyć ten skrypt squash.sh do aliasu git, utwórz kolejny skrypt do ustawiania aliasów git, takich jak ten ( create_aliases.command lub 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
źródło
4
Możesz również umieścić go w $PATHnamed, git-squash.sha zostanie automatycznie utworzony alias jako git squash. Nie zmieniłem Twojej odpowiedzi, na wypadek, gdyby był powód do użycia create-aiases.shskryptu, którego nie jestem świadomy.
Zasadniczo używam skryptu create_aliases.command, aby nawet nasi PM i projektanci mogli łatwo skonfigurować. Wystarczy dwukrotnie kliknąć skrypt i wszystkie są ustawione (zwłaszcza, że ​​mam skrypt instalacyjny w naszym repozytorium i znana jest ścieżka względna). Wtedy nie muszą nawet ponownie uruchamiać terminala.
n8tr
1
Próbowałem tego i odczytuje moją wiadomość o zatwierdzeniu jako liczbę squasha i kończy się niepowodzeniem, ponieważ nie jest to liczba całkowita.
Minthos
1
Rozwiązaniem było dodanie - po /squash.sh \ $ 1 \ $ 2 '
Minthos
1
Podoba mi się pomysł rozwiązania, ale powyższy komentarz nie jest jeszcze uwzględniony w rozwiązaniu. Między pojedynczym a podwójnym cudzysłowem musi znajdować się znak minus.
fizyczne
10

Aby dodać do odpowiedzi wilhelmtell , wygodnie jest wykonać miękki reset, HEAD~2a następnie zmienić zatwierdzenie HEAD~3:

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

Spowoduje to scalenie wszystkich zatwierdzeń z HEAD~3zatwierdzeniem i użycie jego komunikatu o zatwierdzeniu. Pamiętaj, aby zacząć od czystego drzewa roboczego.

harmonijny
źródło
2
To jest poprawna odpowiedź, ponieważ zmiażdży serię zatwierdzeń prowadzących do głowy i używa komunikatu pierwszego zatwierdzenia. To jest nieinteraktywne.
Landon Kuhn
9

Użyłem:

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

Działało całkiem dobrze. Po prostu nie próbuj mieć dziennika zmian z linią zaczynającą się od „pick” :)

Julien Wajsberg
źródło
Niestety nie działa to dla mnie w systemie Windows. Nadal pobierz interaktywny edytor.
abergmeier
3

Użyj następującego polecenia, aby zmiażdżyć ostatnie 4 zatwierdzenia w ostatnim zatwierdzeniu:

git squash 4

Z aliasem:

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 

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

brauliobo
źródło
Nie określiłeś, jaki system operacyjny / powłoka jest tutaj używany. To rozwiązanie prawdopodobnie nie będzie działać na wszystkich innych komputerach używanych przez użytkowników.
Rick-777
Tak, należy użyć linux / bash | zsh do tego
brauliobo
2

Oto jedna linijka do zgniatania ostatnich 2 zatwierdzeń. W tym przykładzie zostanie zachowana wiadomość przedostatniego zatwierdzenia. Możesz zmienić wiadomość, jak chcesz.

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

To polecenie będzie bardzo przydatne, jeśli utworzysz alias dla tego polecenia i zamiast niego użyjesz aliasu.

Jasir
źródło
1

Aby zgnieść wszystko od czasu rozwidlenia gałęzi od mistrza:

git reset --soft $(git merge-base --fork-point master) \
  && git commit --verbose --reedit-message=HEAD --reset-author
Andy
źródło
--reedit-message=HEADużyje wiadomości z ostatniego zatwierdzenia, która nie jest częścią zgniatania . To prawdopodobnie nie jest ten, którego chcesz. Aby raczej otrzymać wiadomość o pierwszym zatwierdzeniu do dołączenia , albo (1) zastąp HEADhashem zatwierdzenia, którego ma dotyczyć wiadomość, albo (2) przeskocz do pierwszego zatwierdzenia, które ma zostać uwzględnione i git commit --amend --reedit-message=HEAD. Tak właśnie działa harmonijna odpowiedź.
Maëlan
-1

Możesz podejść całkiem blisko

git rebase --onto HEAD ~ 4 HEAD ~ master

Zakłada się, że jesteś mistrzem z linearną historią. To nie jest całkiem squash, ponieważ odrzuca pośrednie zatwierdzenia. Będziesz musiał zmienić nowy HEAD, aby zmodyfikować komunikat o zatwierdzeniu.

Greg Bacon
źródło
Dzięki Greg; przez „odrzucenia” masz na myśli, że te pośrednie zatwierdzenia podlegają czyszczeniu przez git gc?
Phillip
@Phillip Tak, pośrednie zatwierdzenia stają się śmieciami, podobnie jak stare HEAD, ponieważ zostało przepisane, aby mieć HEAD~4jako rodzica.
Greg Bacon,