Zmiażdż moje ostatnie X zatwierdzeń razem za pomocą Git

3585

Jak zmiażdżyć moje ostatnie X zatwierdzeń razem w jednym zatwierdzeniu za pomocą Git?

markdorison
źródło
12
Podobne pytanie: stackoverflow.com/questions/7275508/…
koppor
2
@matt TortoiseGit to twoje narzędzie. Zapewnia jedną funkcję „Połącz z jednym zatwierdzeniem”, która automatycznie wywoła wszystkie kroki w tle. Niestety dostępne tylko dla systemu Windows. Zobacz moją odpowiedź poniżej.
Matthias M
1
Aby zgnieść do pierwszego zatwierdzenia, zobacz to - stackoverflow.com/questions/1657017/…
goelakash
1
jeden squasha potrzebę zmuszaj Push zakładać stackoverflow.com/questions/10298291/...
vikramvi

Odpowiedzi:

2092

Użyj git rebase -i <after-this-commit>i zamień „pick” na drugim i kolejnych zatwierdzeniach na „squash” lub „fixup”, jak opisano w instrukcji .

W tym przykładzie <after-this-commit>jest skrótem SHA1 lub względną lokalizacją HEAD bieżącej gałęzi, z której analizowane są zatwierdzenia dla polecenia rebase. Na przykład, jeśli użytkownik chce wyświetlić 5 zatwierdzeń z bieżącego HEAD w przeszłości, polecenie to git rebase -i HEAD~5.

Dezorganizacja
źródło
260
To, jak sądzę, odpowiada na to pytanie nieco lepiej stackoverflow.com/a/5201642/295797
Roy Truelove
92
Co to znaczy <after-this-commit>?
2540625
43
<after-this-commit>to zatwierdzenie X + 1, tj. rodzic najstarszego zatwierdzenia, które chcesz zgnieść.
joozek
339
Uważam, że ta odpowiedź jest zbyt zwięzła, aby jednoznacznie ją zrozumieć. Przykład by pomógł.
Ian Ollmann
53
Różnica między tym rebase -ipodejściem a tym reset --soft, rebase -ipozwala mi zachować autora zatwierdzenia, a jednocześnie reset --softpozwala na ponowne uruchomienie. Czasami muszę zmiażdżyć zatwierdzenia żądań ściągnięcia, ale zachowując informacje o autorze. Czasami muszę zresetować soft na własne zobowiązania. Tak czy inaczej, przychylność obu wspaniałych odpowiedzi.
zionyx
3826

Możesz to zrobić dość łatwo bez git rebaselub git merge --squash. W tym przykładzie zgniecimy 3 ostatnie zatwierdzenia.

Jeśli chcesz napisać nowy komunikat zatwierdzenia od zera, wystarczy:

git reset --soft HEAD~3 &&
git commit

Jeśli chcesz rozpocząć edycję nowego komunikatu zatwierdzenia z konkatenacją istniejących komunikatów zatwierdzenia (tj. Podobnie jak w przypadku git rebase -ilisty instrukcji pick / squash / squash /… / squash ), musisz wyodrębnić te wiadomości i przekazać je do git commit:

git reset --soft HEAD~3 && 
git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"

Obie te metody zgniatają ostatnie trzy zatwierdzenia w jeden nowy zatwierdzenie w ten sam sposób. Miękki reset powoduje ponowne skierowanie HEAD do ostatniego zatwierdzenia, którego nie chcesz zgnieść. Miękki reset nie dotyka ani indeksu, ani drzewa roboczego, pozostawiając indeks w pożądanym stanie dla nowego zatwierdzenia (tzn. Ma już wszystkie zmiany względem zatwierdzeń, które zamierzasz „wyrzucić”).

Chris Johnsen
źródło
163
Ha! Podoba mi się ta metoda. To ten zamyka ducha problemu. Szkoda, że ​​wymaga tyle voodoo. Coś takiego należy dodać do jednego z podstawowych poleceń. Możliwe git rebase --squash-recent, a nawet git commit --amend-many.
Adrian Ratnapala
13
@ABB: Jeśli twoja gałąź ma zestaw „upstream”, możesz być w stanie użyć branch@{upstream}(lub tylko @{upstream}dla bieżącego oddziału; w obu przypadkach ostatnia część może być skrócona @{u}; zobacz gitrevisions ). To może różnić się od twojego „ostatniego wciśnięcia zatwierdzenia” (np. Jeśli ktoś inny nacisnął coś, co zbudowało na twoim ostatnim pushie, a potem to pobrałeś), ale wydaje się, że może być blisko tego, czego chcesz.
Chris Johnsen
104
To trochę mnie wymagało, push -fale poza tym było cudowne, dzięki.
2rs2ts
39
@ 2rs2ts git push -f brzmi niebezpiecznie. Uważaj, aby zgnieść tylko lokalne zobowiązania. Nigdy nie dotykaj zatwierdzonych poleceń!
Matthias M
20
Muszę też użyć git push --forcepóźniej, aby zabrał zatwierdzenie
Zach Saucier
738

Możesz użyć git merge --squashdo tego, który jest nieco bardziej elegancki niż git rebase -i. Załóżmy, że jesteś mistrzem i chcesz zmiażdżyć ostatnie 12 zmian w jednym.

OSTRZEŻENIE: Najpierw upewnij się, że wykonałeś pracę - sprawdź, czy git statusjest czysta (ponieważ git reset --hardodrzuci zmiany etapowe i nieetapowe)

Następnie:

# Reset the current branch to the commit just before the last 12:
git reset --hard HEAD~12

# HEAD@{1} is where the branch was just before the previous command.
# This command sets the state of the index to be as it would just
# after a merge from that commit:
git merge --squash HEAD@{1}

# Commit those squashed changes.  The commit message will be helpfully
# prepopulated with the commit messages of all the squashed commits:
git commit

Dokumentacjagit merge opisuje --squashopcję w sposób bardziej szczegółowy.


Aktualizacja: jedyną prawdziwą przewagą tej metody nad prostotą git reset --soft HEAD~12 && git commitsugerowaną przez Chrisa Johnsena w jego odpowiedzi jest to, że komunikat zatwierdzenia jest wypełniany z każdą wiadomością zatwierdzenia, którą zgniatasz.

Mark Longair
źródło
16
Mówisz, że to jest bardziej „eleganckie” niż git rebase -i, ale nie podajesz powodu dlaczego. Wstępnie -1 to, ponieważ wydaje mi się, że w rzeczywistości jest odwrotnie i jest to hack; czy nie wykonujesz więcej poleceń, niż jest to konieczne tylko po to, aby zmusić się git mergedo wykonania jednej z rzeczy, które git rebasesą specjalnie zaprojektowane?
Mark Amery
76
@Mark Amery: Istnieje wiele powodów, dla których powiedziałem, że jest to bardziej eleganckie. Na przykład nie wymaga niepotrzebnego odradzania edytora, a następnie wyszukiwania i zastępowania łańcucha w pliku „do zrobienia”. Używanie git merge --squashjest również łatwiejsze w użyciu w skrypcie. Zasadniczo powodem było to, że wcale nie potrzebujesz do tego „interaktywności” git rebase -i.
Mark Longair,
12
Kolejną zaletą jest to, że git merge --squashrzadziej wywołuje konflikty scalania w obliczu ruchów / usuwania / zmian nazw w porównaniu do zmiany bazy, szczególnie jeśli łączysz się z lokalnym oddziałem. (zrzeczenie się: na podstawie tylko jednego doświadczenia, popraw mnie, jeśli nie jest to prawdą w ogólnym przypadku!)
Cheezmeister
2
Zawsze jestem bardzo niechętny, jeśli chodzi o twarde resetowanie - zamiast tego chciałbym użyć tagu czasowego, HEAD@{1}aby po prostu być bezpiecznym, np. Gdy przepływ pracy jest przerywany na godzinę z powodu przerwy w dostawie prądu itp.
Tobias Kienzler,
8
@BT: Zniszczyłeś swoje zatwierdzenie? :( Nie jestem pewien, co przez to rozumiesz. Cokolwiek, co popełniłeś, z łatwością będziesz mógł wrócić z dziennika git. Jeśli miałeś nieprzyzwoitą pracę, ale pliki były zainscenizowane, powinieneś być w stanie uzyskać ich zawartość z powrotem, choć będzie to więcej pracy . Jeśli twoja praca nawet nie została zainscenizowana, obawiam się, że niewiele można zrobić; dlatego odpowiedź mówi z góry: „Najpierw sprawdź, czy status gita jest czysty (od czasu git reset --hard wyrzuci zmiany inscenizowane i nieetapowane) ".
Mark Longair
218

git resetJeśli to możliwe, zalecam unikanie - szczególnie dla nowicjuszy Git. Chyba że naprawdę potrzebujesz zautomatyzować proces oparty na wielu zatwierdzeniach, istnieje mniej egzotyczny sposób ...

  1. Umieść zgniecione commity na działającej gałęzi (jeśli jeszcze nie są) - użyj do tego gitk
  2. Sprawdź gałąź docelową (np. „Master”)
  3. git merge --squash (working branch name)
  4. git commit

Komunikat zatwierdzenia zostanie wstępnie wypełniony na podstawie squasha.

nobar
źródło
4
Jest to najbezpieczniejsza metoda: bez resetowania miękkiego / twardego (!!) lub ponownego logowania!
TeChn4K
14
Byłoby wspaniale, gdybyś rozwinął (1).
Adam
2
@Adam: Zasadniczo oznacza to użycie interfejsu GUI w gitkcelu oznaczenia linii kodu, który zgniatasz, a także oznaczenia podstawy, na której chcesz zgnieść. W normalnym przypadku obie te etykiety już będą istnieć, więc krok (1) można pominąć.
nobar
3
Zauważ, że ta metoda nie oznacza, że ​​działająca gałąź jest całkowicie scalona, ​​więc jej usunięcie wymaga wymuszenia usunięcia. :(
Kyrstellaine
2
Dla (1) znalazłem git branch your-feature && git reset --hard HEAD~Nnajwygodniejszy sposób. Wymaga to jednak ponownego uruchomienia git, czego ta odpowiedź próbowała uniknąć.
EIS
132

Na podstawie odpowiedzi na Chrisa Johnsen ,

Dodaj globalny alias „squash” z bash: (lub Git Bash w systemie Windows)

git config --global alias.squash '!f(){ git reset --soft HEAD~${1} && git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"; };f'

... lub za pomocą wiersza polecenia systemu Windows:

git config --global alias.squash "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"


Twój ~/.gitconfigpowinna zawierać ten alias:

[alias]
    squash = "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"


Stosowanie:

git squash N

... Który automatycznie zgniata ostatnie Nzatwierdzenia włącznie.

Uwaga: Wynikowy komunikat zatwierdzenia jest kombinacją wszystkich zgniecionych zatwierdzeń, w kolejności. Jeśli nie jesteś zadowolony z tego, zawsze możesz git commit --amendgo zmodyfikować ręcznie. (Lub edytuj alias zgodnie ze swoimi upodobaniami).

EthanB
źródło
6
Interesujące, ale wolałbym raczej sam napisać zgnieciony komunikat zatwierdzenia, jako opisowe podsumowanie moich wielu zatwierdzeń, niż automatycznie wprowadzić go dla mnie. Wolałbym więc określić git squash -m "New summary."i Nustalić automatycznie jako liczbę nieprzypisanych zatwierdzeń.
Acumenus
1
@ABB, Brzmi jak osobne pytanie. (Nie sądzę, żeby dokładnie tego wymagało OP; nigdy nie czułem takiej potrzeby w moim przepływie pracy git squash.)
EthanB
3
To jest całkiem słodkie. Osobiście chciałbym wersję, która wykorzystuje komunikat zatwierdzenia z pierwszego zatwierdzonego zgniatania razem. Byłoby dobre dla takich rzeczy jak poprawki białych znaków.
funroll
@funroll uzgodnione. Po prostu upuszczenie ostatniej wiadomości zatwierdzenia jest dla mnie bardzo powszechną potrzebą. Powinniśmy być w stanie wymyślić, że ...
Steve Clay
2
@ABB możesz użyć git commit --amenddo dalszej zmiany wiadomości, ale ten alias pozwala na dobry początek tego, co powinno być w komunikacie zatwierdzenia.
dashy
131

Dzięki temu przydatnemu wpisowi na blogu odkryłem, że możesz użyć tego polecenia, aby zgnieść 3 ostatnie zatwierdzenia:

git rebase -i HEAD~3

Jest to przydatne, ponieważ działa nawet wtedy, gdy jesteś w lokalnym oddziale bez informacji o śledzeniu / zdalnym repo.

Polecenie otworzy interaktywny edytor bazy, który następnie pozwala na zmianę kolejności, squash, przeredagowanie itp. Zgodnie z normą.


Za pomocą interaktywnego edytora bazy danych:

Interaktywny edytor bazy danych pokazuje trzy ostatnie zatwierdzenia. To ograniczenie zostało określone przez HEAD~3uruchomienie polecenia git rebase -i HEAD~3.

Ostatnie zatwierdzenie, HEADjest wyświetlane jako pierwsze w wierszu 1. Wiersze rozpoczynające się od a #to komentarze / dokumentacja.

Wyświetlana dokumentacja jest dość przejrzysta. W dowolnym wierszu możesz zmienić polecenie z pickna dowolnie wybrane.

Wolę używać polecenia, fixupponieważ to „zgniata” zmiany zatwierdzenia do zatwierdzenia w wierszu powyżej i odrzuca komunikat zatwierdzenia.

Ponieważ zatwierdzenie w wierszu 1 jest HEAD, w większości przypadków pozostawiłbyś to jako pick. Nie można użyć squashlub, fixupponieważ nie ma innego zatwierdzenia do zgniecenia.

Możesz także zmienić kolejność zatwierdzeń. Pozwala to zgnieść lub naprawić zatwierdzenia, które nie sąsiadują chronologicznie.

interaktywny edytor bazy danych


Praktyczny przykład na co dzień

Niedawno wprowadziłem nową funkcję. Od tego czasu wprowadziłem dwie poprawki błędów. Ale teraz odkryłem błąd (a może po prostu błąd pisowni) w nowej funkcji, którą popełniłem. Jak denerwujące! Nie chcę, aby nowe zatwierdzenie zanieczyszczało moją historię zatwierdzeń!

Pierwszą rzeczą, którą robię, jest naprawienie błędu i dokonanie nowego zatwierdzenia z komentarzem squash this into my new feature!.

Następnie uruchamiam git loglub gitkotrzymuję zatwierdzenie SHA nowej funkcji (w tym przypadku 1ff9460).

Następnie uruchamiam interaktywny edytor bazy danych git rebase -i 1ff9460~. ~Po commit SHA mówi edytor zawierać że popełnić w edytorze.

Następnie przesuwam zatwierdzenie zawierające fix ( fe7f1e0) do poniżej zatwierdzenia funkcji i zmieniam pickna fixup.

Po zamknięciu edytora poprawka zostanie zgnieciona w funkcji zatwierdzania funkcji, a moja historia zmian będzie wyglądać ładnie i czysto!

Działa to dobrze, gdy wszystkie zatwierdzenia są lokalne, ale jeśli spróbujesz zmienić jakiekolwiek zatwierdzenia już przekazane do pilota, możesz naprawdę powodować problemy dla innych deweloperów, którzy sprawdzili ten sam oddział!

wprowadź opis zdjęcia tutaj

br3nt
źródło
5
czy musisz wybrać najwyższą, a resztę zgnieść? Powinieneś zredagować swoją odpowiedź, aby bardziej szczegółowo wyjaśnić, jak korzystać z interaktywnego edytora bazy danych
Kolob Canyon
2
Tak, pozostaw pickw wierszu 1. Jeśli wybierzesz squashlub fixupdla zatwierdzenia w wierszu 1, git wyświetli komunikat „błąd: nie można„ naprawić ”bez wcześniejszego zatwierdzenia”. Następnie pojawi się opcja naprawy: „Możesz to naprawić za pomocą„ git rebase --edit-todo ”, a następnie uruchomić„ git rebase --continue ”.” lub możesz po prostu przerwać i zacząć od nowa: „Lub możesz przerwać rebase za pomocą„ git rebase --abort ”.”.
br3nt
56

Jeśli używasz TortoiseGit, możesz użyć funkcji Combine to one commit:

  1. Otwórz menu kontekstowe TortoiseGit
  2. Wybierz Show Log
  3. Zaznacz odpowiednie zatwierdzenia w widoku dziennika
  4. Wybierz Combine to one commitz menu kontekstowego

Połącz zobowiązania

Ta funkcja automatycznie wykonuje wszystkie niezbędne kroki pojedynczego git. Niestety dostępne tylko dla systemu Windows.

Matthias M.
źródło
O ile mi wiadomo, nie będzie to działać w przypadku zatwierdzeń scalania.
Thorkil Holm-Jacobsen
1
Chociaż nie jest to komentowane przez innych, działa nawet w przypadku zatwierdzeń, których nie ma w HEAD. Na przykład, chciałem zmiażdżyć niektóre zatwierdzenia WIP, które zrobiłem, z bardziej rozsądnym opisem przed wypchnięciem. Działał pięknie. Oczywiście nadal mam nadzieję, że nauczę się tego robić za pomocą poleceń.
Charles Roberto Canato,
55

Aby to zrobić, możesz użyć następującego polecenia git.

 git rebase -i HEAD~n

n (= 4 tutaj) to liczba ostatniego zatwierdzenia. Następnie masz następujące opcje,

pick 01d1124 Message....
pick 6340aaa Message....
pick ebfd367 Message....
pick 30e0ccb Message....

Zaktualizuj jak poniżej pickjednego zatwierdzenia, a squashpozostałe do najnowszego,

p 01d1124 Message....
s 6340aaa Message....
s ebfd367 Message....
s 30e0ccb Message....

Aby uzyskać szczegółowe informacje, kliknij link

Jakir Hosen Khan
źródło
48

Na podstawie tego artykułu uznałem tę metodę za łatwiejszą w moim przypadku użycia.

Moja gałąź „dev” wyprzedziła „origin / dev” o 96 commits (więc te commity nie zostały jeszcze wypchnięte do pilota).

Chciałem zmiażdżyć te rewizje w jeden przed wprowadzeniem zmiany. Wolę zresetować gałąź do stanu „origin / dev” (spowoduje to pozostawienie wszystkich zmian z 96 commits bez scen), a następnie zatwierdzić zmiany od razu:

git reset origin/dev
git add --all
git commit -m 'my commit message'
trudolf
źródło
1
Dokładnie to, czego potrzebowałem. Wyciągnij zatwierdzenia z mojej gałęzi funkcji, a następnie wybieram wiśniowe wybieranie, które zatwierdza mój mistrz.
David Victor
1
To nie niszczy poprzednich zobowiązań!
IgorGanapolsky
czy mógłbyś rozwinąć to nieco dalej @igorGanapolsky?
trudolf
3
@trudolf To nie jest tak naprawdę squash (wybieranie pojedynczych zmian do squasha). Chodzi raczej o zatwierdzenie wszystkich zmian na raz.
IgorGanapolsky
15
tak, dlatego zmiażdży wszystkie twoje zobowiązania w jednym. Gratulacje!
trudolf
45

W gałęzi, w której chcesz połączyć zatwierdzenia, uruchom:

git rebase -i HEAD~(n number of commits back to review)

przykład:

git rebase -i HEAD~1

Spowoduje to otwarcie edytora tekstu i musisz przełączyć „pick” przed każdym zatwierdzeniem na „squash”, jeśli chcesz, aby te commity zostały połączone. Z dokumentacji:

p, pick = użyj zatwierdzenia

s, squash = użyj zatwierdzenia, ale połącz z poprzednim zatwierdzeniem

Na przykład, jeśli chcesz scalić wszystkie zatwierdzenia w jeden, „pick” jest pierwszym dokonanym zatwierdzeniem, a wszystkie przyszłe (umieszczone poniżej pierwszego) powinny być ustawione na „squash”. Jeśli używasz vima, użyj : xw trybie wstawiania, aby zapisać i wyjść z edytora.

Następnie, aby kontynuować rebase:

git rebase --continue

Więcej informacji o tym i innych sposobach przepisywania historii zmian znajdziesz w tym pomocnym poście

aabiro
źródło
2
Wyjaśnij także, co robi --continuei vim :x.
not2qubit
Rebase będzie się odbywał w blokach, gdy przechodzi przez zatwierdzenia w twoim oddziale, po git addpoprawnej konfiguracji w plikach używasz git rebase --continuedo przejścia do następnego zatwierdzenia i rozpoczęcia scalania. :xto jedno polecenie, które zapisze zmiany pliku podczas korzystania z vima zobacz to
aabiro
32

Odpowiedź na anomie jest dobra, ale czułem się niepewnie, więc postanowiłem dodać kilka zrzutów ekranu.

Krok 0: dziennik git

Zobacz, gdzie jesteś git log. Co najważniejsze, znajdź skrót zatwierdzenia pierwszego zatwierdzenia, którego nie chcesz zgnieść. Więc tylko:

wprowadź opis zdjęcia tutaj

Krok 1: Git Rebase

Wykonaj git rebase -i [your hash]w moim przypadku:

$ git rebase -i 2d23ea524936e612fae1ac63c95b705db44d937d

Krok 2: wybierz / squash, co chcesz

W moim przypadku chcę zmiażdżyć wszystko przy zatwierdzeniu, które było po raz pierwszy. Kolejność jest od początku do końca, więc dokładnie w drugą stronę git log. W moim przypadku chcę:

wprowadź opis zdjęcia tutaj

Krok 3: Dostosuj wiadomości

Jeśli wybrałeś tylko jedno zatwierdzenie, a resztę zmiażdżyłeś, możesz dostosować jedną wiadomość zatwierdzenia:

wprowadź opis zdjęcia tutaj

Otóż ​​to. Po zapisaniu tego ( :wq), gotowe. Spójrz na to z git log.

Martin Thoma
źródło
2
miło byłoby zobaczyć wynik końcowy, np.git log
Timothy LJ Stewart
2
Nie, dziękuję. Właśnie tak straciłem swoje zobowiązania.
Axalix,
@Axalix Czy usunąłeś wszystkie swoje linie? W ten sposób tracisz swoje zobowiązania.
a3y3
31

Procedura 1

1) Zidentyfikuj krótki skrót zatwierdzenia

# git log --pretty=oneline --abbrev-commit
abcd1234 Update to Fix for issue B
cdababcd Fix issue B
deab3412 Fix issue A
....

Tutaj nawet git log --onelinemożna go użyć do uzyskania krótkiego skrótu.

2) Jeśli chcesz zgnieść (scalić) ostatnie dwa zatwierdzenia

# git rebase -i deab3412 

3) Otwiera nanoedytor do łączenia. I wygląda jak poniżej

....
pick cdababcd Fix issue B
pick abcd1234 Update to Fix for issue B
....

4) Zmiana nazwy słowo pickdo squashktórych jest obecny przed abcd1234. Po zmianie nazwy powinno to wyglądać jak poniżej.

....
pick cdababcd Fix issue B
squash abcd1234 Update to Fix for issue B
....

5) Teraz zapisz i zamknij nanoedytor. Naciśnij ctrl + oi naciśnij, Enteraby zapisać. A następnie naciśnij, ctrl + xaby wyjść z edytora.

6) Następnie nanoponownie otwiera się edytor do aktualizacji komentarzy, w razie potrzeby zaktualizuj go.

7) Teraz jest pomyślnie zgnieciony, możesz go zweryfikować, sprawdzając dzienniki.

# git log --pretty=oneline --abbrev-commit
1122abcd Fix issue B
deab3412 Fix issue A
....

8) Teraz naciśnij przycisk repo. Uwaga, aby dodać +znak przed nazwą oddziału. Oznacza to wymuszone pchanie.

# git push origin +master

Uwaga: Jest to oparte na użyciu git na ubuntupowłoce. Jeśli używasz innego systemu operacyjnego ( Windowslub Mac), powyższe polecenia są takie same, z wyjątkiem edytora. Możesz dostać innego edytora.

Procedura 2

  1. Najpierw dodaj wymagane pliki do zatwierdzenia
git add <files>
  1. Następnie zatwierdź używając --fixupopcji i OLDCOMMITpowinno być to, na którym musimy scalić (zgnieść) to zatwierdzenie.
git commit --fixup=OLDCOMMIT

Teraz tworzy nowe zatwierdzenie na HEAD za pomocą fixup1 <OLDCOMMIT_MSG>.

  1. Następnie wykonaj poniższe polecenie, aby scalić (zgnieść) nowy zatwierdzenie z OLDCOMMIT.
git rebase --interactive --autosquash OLDCOMMIT^

Tutaj ^oznacza poprzedni zobowiązać się OLDCOMMIT. To rebasepolecenie otwiera interaktywne okno w edytorze (vim lub nano), w którym nie musimy nic robić, wystarczy zapisać i wyjście jest wystarczające. Ponieważ opcja przekazana do tego spowoduje automatyczne przeniesienie ostatniego zatwierdzenia do następnego zatwierdzenia i zmianę operacji na fixup(równoważne squashowi). Następnie rebase kontynuuje i kończy.

Procedura 3

  1. W razie potrzeby dodania nowych zmian do ostatniego zatwierdzenia --amendmożna użyć środków z git-commit.
    # git log --pretty=oneline --abbrev-commit
    cdababcd Fix issue B
    deab3412 Fix issue A
    ....
    # git add <files> # New changes
    # git commit --amend
    # git log --pretty=oneline --abbrev-commit
    1d4ab2e1 Fix issue B
    deab3412 Fix issue A
    ....  

Tutaj --amendscala nowe zmiany do ostatniego zatwierdzenia cdababcdi generuje nowy identyfikator zatwierdzenia1d4ab2e1

Wniosek

  • Zaletą 1. procedury jest zmiażdżenie wielu zatwierdzeń i zmiana kolejności. Ale ta procedura będzie trudna, jeśli będziemy musieli połączyć poprawkę do bardzo starego zatwierdzenia.
  • Tak więc druga procedura pomaga łatwo połączyć zatwierdzenie z bardzo starym zatwierdzeniem.
  • A trzecia procedura jest przydatna w przypadku zmiażdżenia nowych zmian do ostatniego zatwierdzenia.
rashok
źródło
Aktualizuje tylko dwa ostatnie zatwierdzenia, nawet ja resetuję do ID zatwierdzenia do 6. ostatniego zatwierdzenia, nie wiem dlaczego
Carlos Liu,
Nawet ty możesz zmienić kolejność zatwierdzania. To działa dobrze.
rashok
29

Aby zmiażdżyć ostatnie 10 zmian w jednym zatwierdzeniu:

git reset --soft HEAD~10 && git commit -m "squashed commit"

Jeśli chcesz również zaktualizować gałąź zdalną za pomocą zgniatanego zatwierdzenia:

git push -f
Ayan
źródło
--force jest niebezpieczny, gdy wiele osób pracuje nad udostępnionym oddziałem, ponieważ ślepo aktualizuje zdalnie za pomocą lokalnej kopii. Opcja --force-with-leasing może być lepsza, ponieważ zapewnia, że ​​pilot nie ma żadnych zobowiązań od innych od czasu ostatniego pobrania.
bharath
27

Jeśli znajdujesz się na zdalnej gałęzi (nazywanej feature-branch) sklonowanej ze Złotego repozytorium ( golden_repo_name), oto technika zgniatania twoich commits w jeden:

  1. Sprawdź złote repozytorium

    git checkout golden_repo_name
    
  2. Utwórz z niego nową gałąź (złote repozytorium) w następujący sposób

    git checkout -b dev-branch
    
  3. Squash łączy się z lokalnym oddziałem, który już masz

    git merge --squash feature-branch
    
  4. Zatwierdź zmiany (będzie to jedyne zatwierdzenie, które wejdzie w gałąź programistów)

    git commit -m "My feature complete"
    
  5. Wciśnij gałąź do lokalnego repozytorium

    git push origin dev-branch
    
Sandesh Kumar
źródło
Ponieważ właśnie zgniatałem ~ 100 zatwierdzeń (do synchronizacji gałęzi svn za pomocą git-svn), jest to o wiele szybsze niż interaktywne przekształcanie!
mędrzec
1
Czytając, widzę komentarz @ Chrisa, który tak robiłem (rebase - miękki ...) - szkoda, że ​​stackoverflow nie stawia już odpowiedzi z setkami pozytywnych opinii na górze ...
mędrzec
1
zgadzam się z tobą @sage, miejmy nadzieję, że kiedyś to zrobią
Sandesh Kumar
To jest właściwa droga. Podejście do bazowania jest dobre, ale powinno być stosowane tylko do squasha jako rozwiązanie ostateczne.
Axalix,
20

Co może być naprawdę wygodne:
Znajdź, na przykład, skrót zatwierdzenia, który chcesz zgnieść d43e15.

Teraz użyj

git reset d43e15
git commit -am 'new commit name'
Ariel Gabizon
źródło
2
To. Dlaczego więcej osób tego nie używa? Jest to o wiele szybsze niż przekształcanie i zgniatanie pojedynczych zatwierdzeń.
a3y3
17

To kludgy super-duper, ale w pewien fajny sposób, więc po prostu wrzucę to na pierścień:

GIT_EDITOR='f() { if [ "$(basename $1)" = "git-rebase-todo" ]; then sed -i "2,\$s/pick/squash/" $1; else vim $1; fi }; f' git rebase -i foo~5 foo

Tłumaczenie: podaj nowy „edytor” dla git, jeśli nazwa pliku do edycji to git-rebase-todo (interaktywny monit rebase), zmienia wszystko oprócz pierwszego „wybierz” na „squash”, a w przeciwnym razie odradza vim - tak, że gdy pojawi się monit aby edytować komunikat zgniatanego zatwierdzenia, otrzymujesz vim. (I oczywiście zgniatałem ostatnie pięć zmian w gałęzi foo, ale możesz to zmienić, jak chcesz.)

Prawdopodobnie zrobiłbym to, co zasugerował Mark Longair .

Cascabel
źródło
7
+1: to zabawne i pouczające, ponieważ nie było dla mnie oczywiste, że możesz umieścić coś bardziej złożonego niż nazwa programu w zmiennej środowiskowej GIT_EDITOR.
Mark Longair,
16

Jeśli chcesz podzielić każde zatwierdzenie na jedno zatwierdzenie (np. Podczas publicznego publikowania projektu po raz pierwszy), spróbuj:

git checkout --orphan <new-branch>
git commit
William Denniss
źródło
14

Myślę, że najłatwiejszym sposobem jest zrobienie z gałęzi nowego oddziału i scalenie - potknięcie gałęzi funkcji.

git checkout master
git checkout -b feature_branch_squashed
git merge --squash feature_branch

Następnie wszystkie zmiany są gotowe do zatwierdzenia.

użytkownik1376350
źródło
14

2020 Proste rozwiązanie bez bazy:

git reset --soft HEAD~2

git commit -m "new commit message"

git push --force

2 oznacza, że ​​ostatnie dwa zatwierdzenia zostaną zmiażdżone. Możesz go zastąpić dowolnym numerem

Xys
źródło
12

Prosty jednowierszowy, który zawsze działa, biorąc pod uwagę, że aktualnie znajdujesz się w gałęzi, którą chcesz zgnieść, master jest gałęzią, z której pochodzi, a najnowsze zatwierdzenie zawiera komunikat zatwierdzenia i autora, którego chcesz użyć:

git reset --soft $(git merge-base HEAD master) && git commit --reuse-message=HEAD@{1}
ColinM
źródło
4
Byłem całkowicie sfrustrowany frustracją związaną ze zgniataniem spraw i tym, jak głupio jest to skomplikowane - wystarczy, że użyję ostatniej wiadomości i zmiażdżysz je wszystkie przy jednym zatwierdzeniu! Dlaczego to takie trudne ???? Ta jedna wkładka robi to dla mnie. Dziękuję z głębi mojego gniewnego serca.
Locane
12

jeśli na przykład chcesz zgnieść ostatnie 3 zatwierdzenia do pojedynczego zatwierdzenia w oddziale (zdalne repozytorium) na przykład: https://bitbucket.org

To co zrobiłem jest

  1. git reset - miękka głowa ~ 3 i&
  2. git commit
  3. git push origin (nazwa_gałęzi) --force
Albert Ruelan
źródło
3
Bądź ostrożny, ponieważ jeśli użyjesz siły, nie ma sposobu na odzyskanie poprzednich zatwierdzeń, ponieważ ją usunąłeś
Albert Ruelan
12

⚠️ OSTRZEŻENIE: „Moje ostatnie X zatwierdzeń” może być niejednoznaczne.

  (MASTER)  
Fleetwood Mac            Fritz
      ║                    ║
  Add Danny  Lindsey     Stevie       
    Kirwan  Buckingham    Nicks                                              
      ║         ╚═══╦══════╝     
Add Christine       ║          
   Perfect      Buckingham
      ║           Nicks            
    LA1974══════════╝                                    
      ║                  
      ║                  
    Bill <══════ YOU ARE EDITING HERE
  Clinton        (CHECKED OUT, CURRENT WORKING DIRECTORY)              

W tej bardzo skróconej historii repozytorium https://github.com/fleetwood-mac/band-history otworzyłeś prośbę o połączenie w celu zatwierdzenia Billa Clintona z pierwotnym ( MASTER) zatwierdzeniem Fleetwood Mac.

Otworzyłeś żądanie ściągnięcia i na GitHub widzisz to:

Cztery zobowiązania:

  • Dodaj Danny Kirwan
  • Dodaj Christine Perfect
  • LA1974
  • Bill Clinton

Myśląc, że nikt nigdy nie chciałby przeczytać pełnej historii repozytorium. (W rzeczywistości istnieje repozytorium, kliknij powyższy link!) Decydujesz się zmiażdżyć te zmiany. Więc idź i biegnij git reset --soft HEAD~4 && git commit. To tygit push --force przejdź na GitHub, aby wyczyścić swój PR.

A co się stanie Właśnie dokonałeś pojedynczego zatwierdzenia, które dostało się od Fritza do Billa Clintona. Ponieważ zapomniałeś, że wczoraj pracowałeś nad wersją tego projektu Buckingham Nicks. I git lognie pasuje do tego, co widzisz na GitHub.

OR MORAL OF THE STORY

  1. Znaleźć dokładny pliki, które chcesz dostać się , a git checkoutich
  2. Znajdź dokładne wcześniejsze zatwierdzenie, które chcesz zachować w historii i git reset --softto
  3. Zrób taki, git commitktóry wypacza bezpośrednio od z do do
William Entriken
źródło
1
Jest to w 100% najłatwiejszy sposób na zrobienie tego. Jeśli bieżący HEAD ma właściwy stan, możesz pominąć numer 1.
Stan
To jedyny znany mi sposób, który pozwala przepisać pierwszą historię zatwierdzeń.
Vadorequest,
9

Jeśli nie zależy ci na komunikatach zatwierdzeń między zatwierdzeniami, możesz użyć

git reset --mixed <commit-hash-into-which-you-want-to-squash>
git commit -a --amend
Zsolt Z.
źródło
7

Jeśli pracujesz z GitLab, możesz po prostu kliknąć opcję Squash w żądaniu scalenia, jak pokazano poniżej. Komunikat zatwierdzenia będzie tytułem żądania scalenia.

wprowadź opis zdjęcia tutaj

arush436
źródło
6
git rebase -i HEAD^^

gdzie liczba ^ to X

(w tym przypadku zmiażdż dwa ostatnie zatwierdzenia)

Yoaz Menda
źródło
6

Oprócz innych doskonałych odpowiedzi, chciałbym dodać, jak git rebase -izawsze myli mnie z kolejnością zatwierdzania - starszą na nowszą lub odwrotnie? Oto mój obieg pracy:

  1. git rebase -i HEAD~[N], gdzie N jest liczbą zatwierdzeń, które chcę dołączyć, zaczynając od najnowszej . Więcgit rebase -i HEAD~5Oznaczałoby to „zgniecenie ostatnich 5 zmian w nowy”;
  2. pojawi się edytor, pokazujący listę zatwierdzeń, które chcę scalić. Teraz są wyświetlane w odwrotnej kolejności : starsze zatwierdzenie jest na górze. Oznacz jako „squash” lub „s” wszystkie zatwierdzenia, oprócz pierwszego / starszego : będzie on używany jako punkt początkowy. Zapisz i zamknij edytor;
  3. edytor wyskakuje ponownie z domyślnym komunikatem dla nowego zatwierdzenia: zmień go na swoje potrzeby, zapisz i zamknij. Squash zakończony!

Źródła i dodatkowe informacje: # 1 , # 2 .

Nieświadomy
źródło
6

Co powiesz na odpowiedź na pytanie związane z takim przepływem pracy?

  1. wiele lokalnych zatwierdzeń, zmieszanych z wieloma połączeniami z wzorca ,
  2. wreszcie nacisk na pilota,
  3. PR i scal TO TO master przez recenzenta . (Tak, deweloperowi łatwiej byłoby merge --squashpo PR, ale zespół pomyślał, że to spowolni proces).

Na tej stronie nie widziałem takiego przepływu pracy. (To mogą być moje oczy.) Jeśli dobrze rozumiem rebase, wiele połączeń wymagałoby rozwiązania wielu konfliktów . Nie chcę nawet o tym myśleć!

Wydaje się to dla nas działać.

  1. git pull master
  2. git checkout -b new-branch
  3. git checkout -b new-branch-temp
  4. edytuj i popełniaj dużo lokalnie, regularnie scal master
  5. git checkout new-branch
  6. git merge --squash new-branch-temp // umieszcza wszystkie zmiany na etapie
  7. git commit 'one message to rule them all'
  8. git push
  9. Recenzent robi PR i łączy się w master.
allenjom
źródło
Z wielu opinii podoba mi się twoje podejście. Jest to bardzo wygodne i szybkie
Artem Solovev
5

Uważam, że bardziej ogólnym rozwiązaniem nie jest określanie zatwierdzeń „N”, ale identyfikator gałęzi / zatwierdzenia, który chcesz zgnieść na wierzchu. Jest to mniej podatne na błędy niż liczenie zatwierdzeń do konkretnego zatwierdzenia - wystarczy podać tag bezpośrednio, a jeśli naprawdę chcesz policzyć, możesz podać HEAD ~ N.

W moim przepływie pracy uruchamiam gałąź, a moje pierwsze zatwierdzenie w tej gałęzi podsumowuje cel (tj. Zwykle to, co pcham jako „końcową” wiadomość dla funkcji do publicznego repozytorium). Więc kiedy skończę, wszystko Chcę to zrobić git squash master wrócić do pierwszej wiadomości, a następnie jestem gotowy do wysłania.

Używam aliasu:

squash = !EDITOR="\"_() { sed -n 's/^pick //p' \"\\$1\"; sed -i .tmp '2,\\$s/^pick/f/' \"\\$1\"; }; _\"" git rebase -i

Spowoduje to zrzucenie historii, która jest zmiażdżona, zanim to zrobi - daje to szansę na odzyskanie poprzez pobranie starego identyfikatora zatwierdzenia z konsoli, jeśli chcesz przywrócić. (Użytkownicy systemu Solaris zauważają, że korzysta z -iopcji GNU sed , użytkownicy komputerów Mac i Linux powinni się z tym pogodzić).

Ethan
źródło
Próbowałem aliasu, ale nie jestem pewien, czy zamienniki sed mają jakikolwiek efekt. Co powinni zrobić?
raine
Pierwszy sed po prostu zrzuca historię do konsoli. Drugi sed zamienia wszystkie „pick” na „f” (fixup) i przepisuje plik edytora w miejscu (opcja -i). Więc drugi wykonuje całą pracę.
Ethan
Masz rację, liczenie N konkretnych zatwierdzeń jest bardzo podatne na błędy. Zepsuło mnie to kilka razy i zmarnowałem godziny, próbując cofnąć rebase.
IgorGanapolsky
Cześć Ethan, chciałbym wiedzieć, czy ten przepływ pracy ukryje możliwe konflikty podczas scalania. Zastanów się, czy masz dwie gałęzie master i slave. Jeśli slave ma konflikt z masterem i używamy go, git squash mastergdy jesteśmy sprawdzani na slave. co się stanie ukryjemy konflikt?
Sergio Bilello
@Sergio Jest to przypadek przepisywania historii, więc prawdopodobnie będziesz mieć konflikty, jeśli zmiażdżysz zatwierdzenia, które zostały już wypchnięte, a następnie spróbujesz scalić / zmienić bazę zgniecionej wersji z powrotem. (Niektóre trywialne sprawy mogą uciec.)
Ethan
5

Pytanie może być dwuznaczne, co oznacza „ostatni”.

na przykład git log --graphgeneruje następujące (uproszczone):

* commit H0
|
* merge
|\
| * commit B0
| |
| * commit B1
| | 
* | commit H1
| |
* | commit H2
|/
|

Następnie ostatnie zatwierdzenia według czasu to H0, scalenie, B0. Aby je zmiażdżyć, musisz zmienić bazę połączonego oddziału na zatwierdzenie H1.

Problem polega na tym, że H0 zawiera H1 i H2 (i ogólnie więcej zatwierdzeń przed scaleniem i po rozgałęzieniu), podczas gdy B0 nie. Musisz więc zarządzać zmianami przynajmniej od H0, scalania, H1, H2, B0.

Możliwe jest użycie rebase, ale w inny sposób niż w innych wymienionych odpowiedziach:

rebase -i HEAD~2

Spowoduje to wyświetlenie opcji wyboru (jak wspomniano w innych odpowiedziach):

pick B1
pick B0
pick H0

Ustaw squash zamiast pick na H0:

pick B1
pick B0
s H0

Po zapisaniu i wyjściu rebase zastosuje zatwierdzenia po H1. Oznacza to, że poprosi cię o ponowne rozwiązanie konfliktu (gdzie HEAD będzie najpierw H1, a następnie gromadzi zatwierdzenia w miarę ich stosowania).

Po zakończeniu rebase możesz wybrać wiadomość dla zgniecionych H0 i B0:

* commit squashed H0 and B0
|
* commit B1
| 
* commit H1
|
* commit H2
|

PS Jeśli po prostu zresetujesz BO: (na przykład użycie reset --mixedtego wyjaśniono bardziej szczegółowo tutaj https://stackoverflow.com/a/18690845/2405850 ):

git reset --mixed hash_of_commit_B0
git add .
git commit -m 'some commit message'

następnie zgniatasz w B0 zmiany H0, H1, H2 (całkowite utracenie zatwierdza zmiany po rozgałęzieniu i przed scaleniem.

trochę
źródło
4

1) GIT Reset - SOFT HEAD ~ n

n - Numer zatwierdzenia, który trzeba zgnieść

2) git commit -m „nowa wiadomość zatwierdzenia”

3) git push origin nazwa_gałęzi --force

Poonkodi
źródło