Jak zgnieść dwa niesekwencyjne zatwierdzenia?

183

Jestem trochę nowy w całej funkcji rebasingu w git. Powiedzmy, że dokonałem następujących zatwierdzeń:

A -> B -> C -> D

Potem zdaję sobie sprawę, że Dzawiera poprawkę, która zależy od dodanego nowego kodu Ai że te zatwierdzenia należą do siebie. Jak mogę zgnieść Ai Drazem i odchodzenie Bi Cspokoju?

Nik Reiman
źródło

Odpowiedzi:

258

Możesz biegać git rebase --interactivei zmieniać kolejność D przed B i wciskać D w A.

Git otworzy edytor i zobaczysz taki plik, np .: git rebase --interactive HEAD~4

pick aaaaaaa Commit A
pick bbbbbbb Commit B
pick ccccccc Commit C
pick ddddddd Commit D

# Rebase aaaaaaa..ddddddd onto 1234567 (4 command(s))
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

Teraz zmienisz plik, który wygląda następująco:

pick aaaaaaa Commit A
squash ddddddd Commit D
pick bbbbbbb Commit B
pick ccccccc Commit C

A git będzie teraz łączyć zmiany A i D razem w jeden zatwierdzenie, a następnie wstawi B i C. Gdy nie chcesz zachować komunikatu zatwierdzenia D, zamiast tego squashużyjesz fixupsłowa kluczowego. Aby uzyskać więcej informacji fixup, możesz zapoznać się z git rebasedokumentami lub sprawdzić to pytanie, które ma kilka dobrych odpowiedzi.

Rudi
źródło
5
Początkowo czytam to jako „rebase D na A, squash D na A, a następnie rebase B na DA”. Z odpowiedzi nie wynika jasno, że można tego dokonać, zmieniając kolejność wierszy w edytorze tekstu.
Victor Sergienko
3
Jeśli Twój oddział ma charakter lokalny, There is no tracking information for the current branchpodczas ponownego naliczania pojawi się błąd. W tym przypadku trzeba określić liczbę zatwierdzeń chcesz pracować, tak: git rebase -i HEAD~4. Zobacz tę odpowiedź .
johndodo
1
Używam trybu interaktywnego (git rebase -i) dla lat, zdałem sobie sprawę, że może być zreorganizowane . Dzięki 🤟🏻
CalvinChe,
43

Uwaga: Nie powinieneś w żaden sposób zmieniać zatwierdzeń, które zostały przekazane do innego repo, chyba że znasz konsekwencje .

git log --oneline -4

D commit_message_for_D
C commit_message_for_C
B commit_message_for_B
A commit_message_for_A

git rebase --interactive

pick D commit_message_for_D
pick C commit_message_for_C
pick B commit_message_for_B
pick A commit_message_for_A

Wpisz i(Przełącz VIM w tryb wstawiania)

Zmień listę, aby wyglądała następująco (nie musisz usuwać ani dołączać komunikatu zatwierdzenia). Nie literuj źle squash! :

pick C commit_message_for_C
pick B commit_message_for_B
pick A commit_message_for_A
squash D

EscNastępnie wpisz ZZ(Zapisz i zamknij VIM)

# This is a combination of 2 commits.
# The first commit's message is:

commit_message_for_D

# This is the 2nd commit message:

commit_message_for_A

Rodzaj i

Zmień tekst na wygląd nowej wiadomości zatwierdzenia. Polecam, aby był to opis zmian w zatwierdzeniu Ai D:

new_commit_message_for_A_and_D

Wpisz EscwięcZZ

git log --oneline -4

E new_commit_message_for_A_and_D
C commit_message_for_C
B commit_message_for_B

git show E

(You should see a diff showing a combination of changes from A and D)

Utworzyłeś nowy zatwierdzenie E. Zobowiązania Ai Dnie są już w twojej historii, ale ich nie ma. Nadal możesz je odzyskać w tym momencie i przez chwilę git rebase --hard D( git rebase --hardzniszczy wszelkie lokalne zmiany! ).

Nate
źródło
3

Dla osób korzystających z SourceTree :

Upewnij się, że nie przeforsowałeś jeszcze zatwierdzeń.

  1. Repozytorium> Interactive Rebase ...
  2. Przeciągnij D (nowsze zatwierdzenie), aby znajdować się bezpośrednio nad A (starsze zatwierdzenie)
  3. Upewnij się, że zatwierdzenie D jest podświetlone
  4. Kliknij Squash with previous
Adam Johns
źródło
1

Interaktywny rebase działa dobrze, dopóki nie będziesz mieć dużego oddziału funkcji z 20-30 zatwierdzeniami i / lub kilkoma połączeniami z master lub / i naprawiającymi konflikty podczas zatwierdzania w oddziale. Nawet ze znalezieniem moje zobowiązuje przez historię i zastąpienie pickze squashnie działało tutaj. Więc szukałem innego sposobu i znalazłem ten artykuł . Wprowadziłem zmiany, aby pracować z tym w osobnym oddziale:

git checkout master
git fetch
git pull
git merge branch-name
git reset origin/master
git branch -D branch-name
git checkout -b branch-name
git add --all
#Do some commit
git push -f --set-upstream origin branch-name

Wcześniej otrzymałem moją prośbę o ściągnięcie z około ~ 30 zatwierdzeniami z 2-3 połączeniami z master + konflikty naprawcze. A potem uzyskałem wyraźny PR z jednym zatwierdzeniem.

PS tutaj jest skrypt bash do wykonywania tych kroków w trybie automatycznym.

Ołeksij Guzenko
źródło
Pierwsze rozwiązanie w tym artykule jest naprawdę fajne, dzięki za link
Hoody
-1

$ git Checkout master

$ git log --oneline

D
C
B
A

$ git rebase --onto HEAD ^^^ HEAD ^

$ git log --oneline

D
A
Bawełna
źródło
Myślę, że masz na myśli --oneline? I wygląda na to, że upuściłeś Ci B, czego nie zamierzał OP.
bstpierre
Nie działało dla mnie. Przeniósł zarówno moją GŁOWĘ, jak i mistrza do A, ale nie połączył D w A ( git show A), a D, C i B zostały utracone w moim dzienniku ref. Musiałem git rebase Dwrócić.
Nate