Co to znaczy zgniatać commity w git?

117

Co oznacza Squashing Commits in git. Jak zmiażdżyć zatwierdzenia na Githubie?

Jestem nowy w Git i poprosiłem o przypisanie mnie do nowego błędu w analizatorze coala. Naprawiłem błąd, a teraz zostałem poproszony o zmiażdżenie moich zatwierdzeń. Jak mam to zrobić?

Lakshmanan Meiyappan
źródło
Hej @Lakshman, nie krępuj się, aby przyjąć najwyższą głosowaną odpowiedź. Właściwie odpowiada na twoje pytanie.
Mostafiz Rahman

Odpowiedzi:

180

Możesz myśleć o Gicie jako o zaawansowanej bazie danych zawierającej migawki katalogów roboczych.

Bardzo fajną cechą Gita jest możliwość przepisania historii zatwierdzeń.
Głównym powodem takiego działania jest to, że wiele takich historii jest istotnych tylko dla programisty, który je wygenerował, dlatego należy je uprościć lub upiększyć przed przesłaniem do współdzielonego repozytorium.

Zgniatanie zatwierdzenia oznacza, z idiomatycznego punktu widzenia, przeniesienie zmian wprowadzonych we wspomnianym zatwierdzeniu do jego rodzica, tak aby w rezultacie otrzymano jedno zatwierdzenie zamiast dwóch (lub więcej).
Jeśli powtórzysz ten proces wiele razy, możesz zredukować n zatwierdzenia do jednego.

Wizualnie, jeśli rozpocząłeś pracę od zatwierdzenia oznaczonego jako Start , chcesz tego

Git zatwierdza zgniatanie

Możesz zauważyć, że nowe zatwierdzenie ma nieco ciemniejszy odcień niebieskiego. To jest zamierzone.

W Git squash osiąga się za pomocą Rebase , w specjalnej formie zwanej Interactive Rebase .
Upraszczając, kiedy przebudowujesz zestaw zatwierdzeń na gałąź B , zastosujesz wszystkie zmiany wprowadzone przez te zatwierdzenia tak, jak zostały wykonane, zaczynając od B zamiast ich pierwotnego przodka.

Wizualna wskazówka

wprowadź opis obrazu tutaj

Zwróć uwagę ponownie na różne odcienie niebieskiego.

Interaktywna rebase pozwala wybrać sposób zmiany bazy zatwierdzeń. Jeśli uruchomisz to polecenie:

 git rebase -i branch

Skończyłbyś z plikiem, który zawiera listę zmian, które zostaną zmienione

 pick ae3...
 pick ef6...
 pick 1e0...
 pick 341...

Nie nazwać zobowiązuje, ale te cztery z nich mają być commity od start do szefa

Zaletą tej listy jest to, że można ją edytować .
Możesz pominąć zmiany lub je zmiażdżyć .
Wszystko, co musisz zrobić, to zmienić pierwsze słowo na squash .

 pick ae3...
 squash ef6...
 squash 1e0...
 squash 341...

Jeśli zamkniesz edytor i nie zostaną znalezione żadne konflikty podczas scalania, otrzymasz taką historię:

wprowadź opis obrazu tutaj

W twoim przypadku nie chcesz przechodzić do innej gałęzi, ale raczej do poprzedniego zatwierdzenia.
Aby przekształcić historię, jak pokazano w pierwszym przykładzie, musisz uruchomić coś takiego

git rebase -i HEAD~4

zmień "polecenia" na squash dla wszystkich zatwierdzeń oprócz pierwszego, a następnie zamknij edytor.


Uwaga dotycząca zmieniania historii

W Gicie zatwierdzenia nigdy nie są edytowane. Mogą być przycinane, niedostępne, klonowane, ale nie zmieniane.
Kiedy zmieniasz bazę, w rzeczywistości tworzysz nowe zatwierdzenia.
Do starych nie ma już dostępu żaden sędzia, więc nie są pokazywane w historii, ale wciąż tam są!

Oto, co faktycznie otrzymujesz za rebase:

wprowadź opis obrazu tutaj

Jeśli już je gdzieś popchnąłeś, przepisanie historii faktycznie stworzy gałąź!

Margaret Bloom
źródło
Fajnie - ale co dzieje się z oryginalnymi komentarzami do zmian - czy łączą się one w jeden duży komentarz do zmian, czy też są zgubione?
Paul R
Od man git rebase: Sugerowany komunikat o zatwierdzeniu dla zwiniętego zatwierdzenia to konkatenacja komunikatów o zmianach z pierwszego zatwierdzenia i tych z poleceniem "squash"
Margaret Bloom
1
To najlepsze wyjaśnienie zmiany bazy, jakie widziałem, dziękuję.
Kerry Jones
O zgniataniu na pierwszym obrazku: Czy możesz zrobić przykład, w którym HEAD ma inny katalog roboczy przed (jasnoniebieski) i po (ciemnoniebieski) zgniataniu? We wszystkich przykładach próbowałem squashing wyglądał tak, jakby po prostu usunął trzy zatwierdzenia między START i HEAD.
reality_panda
@actual_panda Nie jestem pewien, czy śledzę. HEAD jest odniesieniem do zatwierdzenia. Zatwierdza delty sklepu. Katalog roboczy jest własnością repozytorium jako całości, zależy to od tego, który zatwierdziłeś wyewidencjonowany. Ogólnie rzecz biorąc, dwa referencje (takie jak HEAD i START) zawsze dają dwa różne katalogi robocze, gdy są sprawdzane. Jeśli zmienisz bazę squasha na tej samej gałęzi, efektem jest "poluzowanie" pośrednich zatwierdzeń, ale w rzeczywistości git stworzył nowe ze wszystkimi deltami. git diffmoże pomóc ci pokazać, co się stało.
Margaret Bloom,
22

Polecenie rebase ma kilka niesamowitych opcji dostępnych w swoim --interactive(lub -i) trybie, a jedną z najczęściej używanych jest możliwość zgniatania zatwierdzeń. Robi to mniejsze commity i łączy je w większe, co może być przydatne, jeśli kończysz dzień pracy lub po prostu chcesz inaczej spakować zmiany. Omówimy, jak możesz to łatwo zrobić.

Jedna uwaga: rób to tylko w przypadku zatwierdzeń, które nie zostały wypchnięte do zewnętrznego repozytorium. Jeśli inni oparli pracę na zatwierdzeniach, które zamierzasz usunąć, może wystąpić wiele konfliktów. Po prostu nie przepisuj swojej historii, jeśli została udostępniona innym.

Powiedzmy, że właśnie wykonałeś kilka małych zatwierdzeń i chcesz zrobić z nich jedno większe zatwierdzenie. Obecnie historia naszego repozytorium wygląda następująco:

wprowadź opis obrazu tutaj

Ostatnie 4 zatwierdzenia byłyby dużo szczęśliwsze, gdyby zostały połączone razem, więc zróbmy to poprzez interaktywne ponowne bazowanie:

$ git rebase -i HEAD~4

pick 01d1124 Adding license
pick 6340aaa Moving license into its own file
pick ebfd367 Jekyll has become self-aware.
pick 30e0ccb Changed the tagline in the binary, too.

# Rebase 60709da..30e0ccb onto 60709da
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

Tak więc wydarzyło się tutaj kilka rzeczy. Po pierwsze, powiedziałem Gitowi, że chcę zmienić bazę przy użyciu ostatnich czterech zatwierdzeń, z których HEAD jest z HEAD ~ 4. Git umieścił mnie teraz w edytorze z powyższym tekstem i małym wyjaśnieniem, co można zrobić. Na tym ekranie dostępnych jest wiele opcji, ale teraz zamierzamy zgnieść wszystko do jednego zatwierdzenia. Tak więc zmiana pierwszych czterech wierszy pliku na to załatwi sprawę:

pick 01d1124 Adding license
squash 6340aaa Moving license into its own file
squash ebfd367 Jekyll has become self-aware.
squash 30e0ccb Changed the tagline in the binary, too.

Zasadniczo mówi to Gitowi, aby połączył wszystkie cztery zatwierdzenia w pierwszym zatwierdzeniu na liście. Po wykonaniu tej czynności i zapisaniu pojawi się inny edytor z następującymi elementami:

# This is a combination of 4 commits.
# The first commit's message is:
Adding license

# This is the 2nd commit message:

Moving license into its own file

# This is the 3rd commit message:

Jekyll has become self-aware.

# This is the 4th commit message:

Changed the tagline in the binary, too.

    # Please enter the commit message for your changes. Lines starting
    # with '#' will be ignored, and an empty message aborts the commit.
    # Explicit paths specified without -i nor -o; assuming --only paths...
    # Not currently on any branch.
    # Changes to be committed:
    #   (use "git reset HEAD <file>..." to unstage)
    #
    #   new file:   LICENSE
    #   modified:   README.textile
    #   modified:   Rakefile
    #   modified:   bin/jekyll
    #

Ponieważ łączymy tak wiele zatwierdzeń, Git pozwala modyfikować komunikat nowego zatwierdzenia w oparciu o resztę zatwierdzeń zaangażowanych w proces. Edytuj wiadomość według własnego uznania, a następnie zapisz i zakończ. Gdy to zrobisz, twoje zatwierdzenia zostały pomyślnie zmiażdżone!

Created commit 0fc4eea: Creating license file, and making jekyll self-aware.
 4 files changed, 27 insertions(+), 30 deletions(-)
  create mode 100644 LICENSE
    Successfully rebased and updated refs/heads/master.

A jeśli ponownie spojrzymy na historię… wprowadź opis obrazu tutaj

Jak dotąd było to stosunkowo bezbolesne. Jeśli napotkasz konflikty podczas rebase, zwykle są one dość łatwe do rozwiązania, a Git prowadzi cię przez jak najwięcej. Podstawą tego jest naprawienie konfliktu, git addpliku, a następnie git rebase --continuewznowienie procesu. Oczywiście, git rebase --abortjeśli chcesz, powrócisz do poprzedniego stanu. Jeśli z jakiegoś powodu straciłeś zatwierdzenie w rebase, możesz użyć reflog, aby je odzyskać.

Szczegóły można znaleźć pod tym linkiem .

0xAliHn
źródło
3
Dziękuję Ci! O ile ktoś nie czuje się komfortowo z VIM-em tak samo jak ja ... kiedy dojdziesz do punktu, w którym wprowadzasz akcje do edycji, naciśnij „i”, aby przejść do trybu edycji. Kiedy skończysz ze zmianami, naciśnij klawisz Escape, a następnie wpisz „: wq”, a to przeniesie Cię do przodu. (Mac)
Farasi78
+1 za ostrożność i kontekst, kiedy to zrobić. Z mojego osobistego doświadczenia wynika, że ​​dobrze jest, jeśli pracujesz sam nad gałęzią funkcji i masz mnóstwo trywialnych zatwierdzeń, takich jak „zaktualizowany plik readme”, „zrobiłeś coś, na czym nikt się nie troszczy” i jesteś gotowy do scalenia tej gałęzi. po prostu zgnieć to wszystko w jednym zatwierdzeniu. Nikt nie będzie chciał wrócić do twojego „zrobienia czegoś, na czym nikt się nie troszczy” i to zanieczyszcza historię zatwierdzeń
Adam Hughes