Jak mogę połączyć dwa zatwierdzenia w jeden, jeśli już rozpocząłem rebase?

1157

Próbuję scalić 2 zatwierdzenia w 1, więc podążyłem za „zgniataniem zatwierdzeń z rebase” z git ready .

Pobiegłem

git rebase --interactive HEAD~2

W powstałym edytorze zmieniam pickna, squasha następnie zapisz-wyjdź, ale zmiana bazy kończy się niepowodzeniem z błędem

Nie można „zgnieść” bez wcześniejszego zatwierdzenia

Teraz, gdy moje drzewo pracy osiągnęło ten stan, mam problem z odzyskaniem.

Polecenie git rebase --interactive HEAD~2kończy się niepowodzeniem:

Interaktywny rebase już się rozpoczął

i git rebase --continuekończy się niepowodzeniem

Nie można „zgnieść” bez wcześniejszego zatwierdzenia

Michał
źródło
22
Też uderzyłem. Mój błąd był spowodowany faktem, że git rebase -i wyświetla zatwierdzenia w odwrotnej kolejności do git log; ostatnie zatwierdzenie znajduje się na dole!
lmsurprenant

Odpowiedzi:

1732

Podsumowanie

Komunikat o błędzie

Nie można „zgnieść” bez wcześniejszego zatwierdzenia

oznacza, że ​​prawdopodobnie próbowałeś „zgnieść się w dół”. Git zawsze zgniata nowsze zatwierdzenie do starszego zatwierdzenia lub „w górę”, jak pokazano na interaktywnej liście zadań bazy danych, czyli do zatwierdzenia w poprzedniej linii. Zmiana polecenia w pierwszym wierszu listy zadań do wykonania squashspowoduje zawsze wystąpienie tego błędu, ponieważ nie ma nic, w co mógłby się zgnieść pierwszy zatwierdzenie.

Poprawka

Najpierw wróć do miejsca, w którym zacząłeś

$ git rebase --abort

Powiedz, że twoja historia to

$ git log --pretty=oneline
a931ac7c808e2471b22b5bd20f0cad046b1c5d0d c
b76d157d507e819d7511132bdb5a80dd421d854f b
df239176e1a2ffac927d8b496ea00d5488481db5 a

To znaczy, a był pierwszym zatwierdzeniem, następnie b, a na końcu c. Po popełnieniu c decydujemy się zgnieść razem bic:

(Uwaga: Uruchomienie git logpotokuje wyjście do pagera, lessdomyślnie na większości platform. Aby wyjść z pagera i powrócić do wiersza poleceń, naciśnij qklawisz.)

Uruchamianie git rebase --interactive HEAD~2daje edytor z

pick b76d157 b
pick a931ac7 c

# Rebase df23917..a931ac7 onto df23917
#
# 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
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

(Zauważ, że ta lista rzeczy do zrobienia jest w odwrotnej kolejności w porównaniu do wyniku git log.)

Zmiana b na pickna squashspowoduje błąd, który zobaczyłeś, ale jeśli zamiast tego zgniatasz c do b (nowsze zatwierdzają do starszego lub „squashing up”), zmieniając listę zadań do

pick   b76d157 b
squash a931ac7 c

i po wyjściu z edytora, otrzymasz innego edytora, którego zawartość jest

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

b

# This is the 2nd commit message:

c

Po zapisaniu i zamknięciu zawartość edytowanego pliku staje się komunikatem zatwierdzenia nowego połączonego zatwierdzenia:

$ git log --pretty=oneline
18fd73d3ce748f2a58d1b566c03dd9dafe0b6b4f b and c
df239176e1a2ffac927d8b496ea00d5488481db5 a

Uwaga na temat przepisywania historii

Interaktywny rebase przepisuje historię. Próba przekazania do pilota zawierającego starą historię nie powiedzie się, ponieważ nie jest to przewijanie do przodu.

Jeśli gałąź, którą zmieniłeś, to temat lub gałąź funkcji, w której pracujesz sam , nic wielkiego. Przekazywanie do innego repozytorium będzie wymagało --forceopcji, lub alternatywnie możesz, w zależności od uprawnień zdalnego repozytorium, najpierw usunąć starą gałąź, a następnie wypchnąć wersję ponownie. Przykłady tych poleceń, które potencjalnie zniszczą pracę, są poza zakresem tej odpowiedzi.

Przepisywanie już opublikowanej historii w oddziale, w którym pracujesz z innymi ludźmi bez bardzo dobrych powodów, takich jak wyciekanie hasła lub innych poufnych szczegółów, wymusza pracę na twoich współpracownikach i jest aspołeczne i będzie drażnić innych programistów. Sekcja „Odzyskiwanie z wcześniejszego uruchomienia” w git rebasedokumentacji wyjaśnia, z dodatkowym naciskiem.

Odbudowywanie (lub jakiejkolwiek innej formy przepisywania) gałęzi, na której inni pracują, jest złym pomysłem: każdy, kto jest za nią zmuszony, musi ręcznie naprawić swoją historię. W tej sekcji wyjaśniono, jak wykonać poprawkę z punktu widzenia dalszego użytkownika. Prawdziwym rozwiązaniem byłoby jednak przede wszystkim uniknięcie ponownego upstreamu.

Greg Bacon
źródło
Jeśli użyję rebase do zgniecenia zatwierdzenia, powstaje nowy „połączony” zatwierdzenie zawierający dwa zestawy zmian, ale skrót jest inny. czy oryginalne zatwierdzenia są również przechowywane przez git?
fabsenet
@fabsenet Tak i nie. Oryginalne zatwierdzenia są nadal dostępne, ale prawdopodobnie nie są już dostępne z żadnego ref (w zależności od szczegółów twojej historii). Niereferencyjne zatwierdzenia są ostatecznie usuwane podczas procesu wyrzucania elementów bezużytecznych.
Greg Bacon
po prostu bawiłem się ... zrobiłem git log hashoftheoldcommiti zadziałało, ale byłem ciekawy, że mogę zobaczyć git log --graphwszystkie te nieosiągalne zobowiązania
fabsenet
ten squash jest dobrym narzędziem do organizowania zatwierdzeń przed wypychaniem, ale jeśli popchnę jeden z zatwierdzeń, nie mogę go zgnieść? git mówi: Udało się przekształcić i zaktualizować odłączony HEAD.
Sérgio
Nie dostaję edytora do drugiej instancji, git bash, wydaje się zamrożony do jakiegoś procesu. Co robić?
411

Jeśli istnieje wiele zatwierdzeń, możesz użyć, git rebase -iaby zgnieść dwa zatwierdzenia w jeden.

Jeśli są tylko dwa zatwierdzenia, które chcesz scalić, i są one „najnowszymi dwoma”, można użyć następujących poleceń, aby połączyć dwa zatwierdzenia w jeden:

git reset --soft "HEAD^"
git commit --amend
użytkownik3828059
źródło
6
Jaki jest minus w porównaniu do rebase? Uważam, że jeden jest o wiele prostszy w użyciu.
Guillaume86,
17
Nie możesz dołączyć w dowolnej kolejności - tylko dwa ostatnie zatwierdzenia .
dr0i
50
@ dr0i Możesz scalić tyle zatwierdzeń, ile chcesz, o ile są to ostatnie zatwierdzenia X, a nie gdzieś pośrodku. Po prostu uruchom git reset --soft HEAD~10, gdzie 10 to liczba zatwierdzeń, które chcesz scalić.
fregante
2
Można tego użyć, jeśli nie masz zestawu zdalnego pochodzenia i masz tylko dwa zatwierdzenia.
atedja
8
Możesz także zresetować do konkretnego zatwierdzenia, jeśli nie chcesz policzyć, ile jest z HEAD, używając git reset --soft 47b5c5...gdzie 47b5c5...jest identyfikator SHA1 zatwierdzenia.
czwartek
112

Rebase: Nie potrzebujesz go:

Prostszy sposób na najczęstszy scenariusz.

W większości przypadków:

W rzeczywistości, jeśli wszystko, czego chcesz, to po prostu połącz kilka ostatnich zatwierdzeń w jeden, ale nie potrzebujesz drop, rewordi inne operacje bazowania.

możesz po prostu zrobić:

git reset --soft "HEAD~n"
  • Zakładając, że ~njest liczba zobowiązuje się cicho un-commit (czyli ~1, ~2...)

Następnie użyj następującego polecenia, aby zmodyfikować komunikat zatwierdzenia.

git commit --amend

który jest prawie taki sam jak długi zasięg squashi jeden pick.

I działa dla n zatwierdzeń, ale nie tylko dwóch zatwierdzeń, jak wskazano powyżej.

pambda
źródło
3
Jest to przydatne, jeśli chcesz wykonać dodatkowe czyszczenie oprócz zgniatania zatwierdzeń, takich jak usunięcie 1 zatwierdzenia w środku lub zmiana linii kodu.
styfle
1
Zakładając, że ~njest liczba zobowiązuje się cicho un-commit (czyli ~1, ~2, ...)
Ray
1
Co jeśli chcę scalić nie nostatnie zatwierdzenia, ale nzatwierdzenia pośrodku? Czy mogę to zrobić łatwo?
chumakoff
1
To git rebase -ijest to, co musisz zrobić squash. @chumakoff
pambda
3
Tak więc, aby dołączyć do nnajnowszych zatwierdzeń w jednym, pierwszym użyciu git reset --soft @~m, w którymm = n - 1
Łukasz Rajchel
55

Najpierw sprawdź, ile masz zatwierdzeń:

git log

Istnieją dwa statusy:

Jednym z nich jest to, że istnieją tylko dwa zatwierdzenia:

Na przykład:

commit A
commit B

(W takim przypadku nie możesz użyć git rebase), musisz wykonać następujące czynności.

$ git reset --soft HEAD^1

$ git commit --amend

Innym jest, że istnieją więcej niż dwa zobowiązania; chcesz scalić zatwierdzenie C i D.

Na przykład:

commit A
commit B
commit C
commit D

(pod tym warunkiem możesz użyć git rebase)

git rebase -i B

I zamiast tego użyj „squash”. Reszta jest bardzo łatwa. Jeśli nadal nie wiesz, przeczytaj http://zerodie.github.io/blog/2012/01/19/git-rebase-i/

Haimei
źródło
Reset --soft i commit --amend to jedyny sposób, który działa, jeśli masz już działający rebase (i wybrałeś „edit” zamiast „squash” dla tego zatwierdzenia). +1
Jacek Lach
1
Scalenie pierwszego i tylko dwóch zatwierdzeń w repozytorium, dokładnie w moim przypadku :-)
Chris Huang-Leaver
1
Dodaj, że git push -f origin mastermoże to być konieczne.
Rishabh Agrahari,
33

Zakładając, że jesteś we własnej gałęzi tematycznej. Jeśli chcesz scalić ostatnie 2 zatwierdzenia w jeden i wyglądać jak bohater, rozgałęź zatwierdzenie tuż przed dokonaniem dwóch ostatnich zatwierdzeń.

git checkout -b temp_branch HEAD^2

Następnie squash zatwierdza drugą gałąź w tej nowej gałęzi:

git merge branch_with_two_commits --squash

To wprowadzi zmiany, ale ich nie zatwierdzi. Więc po prostu je popełnij i gotowe.

git commit -m "my message"

Teraz możesz połączyć tę nową gałąź tematu z powrotem w swoją główną gałąź.

Homan
źródło
5
To była dla mnie najbardziej pomocna odpowiedź, ponieważ nie wymagała ręcznego zmiany bazy, ale zamiast tego po prostu zgniata wszystkie zatwierdzenia całej gałęzi w jednym zatwierdzeniu. Bardzo dobrze.
Robert,
Dzięki za to! Oto jak zmusić gita do robienia tego, co wyobrażam sobie w mojej głowie!
Marjan Venema
niesamowita odpowiedź, o wiele prostsza niż alternatywy
Najwyraźniej ta odpowiedź nie nadaje się do przypadku, gdy ai cwymaga, aby zostać połączone ze sobą i zachować bjak to jest.
Talha Ashraf
2
Czy coś się zmieniło w ostatnich wersjach git? Kiedy próbuję pierwszego polecenia ( git checkout -b combine-last-two-commits "HEAD^2") w wersji git 2.17, fatal: 'HEAD^2' is not a commit and a branch 'combine-last-two-commits' cannot be created from it
pojawia
23

możesz anulować rebase za pomocą

git rebase --abort

a kiedy ponownie uruchomisz interaktywne polecenie rebase, squash; zatwierdzenie musi znajdować się poniżej listy wyboru zatwierdzenia na liście

Leom Burke
źródło
16

Często używam git reset --mixed, aby przywrócić wersję podstawową przed wieloma zatwierdzeniami, które chcesz scalić, a następnie dokonuję nowego zatwierdzenia, w ten sposób możesz pozwolić na twoje najnowsze zatwierdzenie, upewniając się, że Twoja wersja jest HEAD po przekazaniu na serwer.

commit ac72a4308ba70cc42aace47509a5e
Author: <[email protected]>
Date:   Tue Jun 11 10:23:07 2013 +0500

    Added algorithms for Cosine-similarity

commit 77df2a40e53136c7a2d58fd847372
Author: <[email protected]>
Date:   Tue Jun 11 13:02:14 2013 -0700

    Set stage for similar objects

commit 249cf9392da197573a17c8426c282
Author: Ralph <[email protected]>
Date:   Thu Jun 13 16:44:12 2013 -0700

    Fixed a bug in space world automation

Jeśli chcę scalić dwie główne zmiany w jedną, najpierw używam:

git reset --mixed 249cf9392da197573a17c8426c282

„249cf9392da197573a17c8426c282” była trzecią wersją, jest też wersją podstawową przed scaleniem, a następnie dokonuję nowego zatwierdzenia:

git add .
git commit -m 'some commit message'

To wszystko, nadzieja jest innym sposobem dla wszystkich.

Do Twojej wiadomości, od git reset --help:

 --mixed
     Resets the index but not the working tree (i.e., the changed files are
     preserved but not marked for commit) and reports what has not been
     updated. This is the default action.
VinceStyling
źródło
Nie czytałem dokumentów dla „--mixed”, ale jestem pewien, że inni czytają post i zastanawiają się nad tym samym: jaka jest zaleta używania --mixed? Możesz ulepszyć swój post, dodając fragment strony podręcznika man.
funroll
@funroll, którego nie znałem - zmieszałem bardzo dużo przed napisaniem tej odpowiedzi, zgodnie z moim własnym doświadczeniem, mieszana operacja zmieni wersję, którą przekazuję jako argument jako wersję HEAD repozytorium, i po tej wersji nic nie może być stracone, więc nadal możemy obsłużyć te zmiany.
VinceStyling,
14

$ git rebase --abort

Uruchom ten kod w dowolnym momencie, jeśli chcesz cofnąć bazę git

$ git rebase -i HEAD~2

Aby ponownie zastosować ostatnie dwa zatwierdzenia. Powyższe polecenie otworzy edytor kodu

  • [ Ostatnie zatwierdzenie będzie na dole ]. Zmień ostatnie zatwierdzenie na squash (s). Ponieważ squash połączy się z poprzednim zatwierdzeniem.
  • Następnie naciśnij klawisz esc i wpisz: wq, aby zapisać i zamknąć

Po: wq będziesz w aktywnym trybie bazowym

Uwaga : Otrzymasz innego edytora, jeśli nie pojawi się komunikat ostrzegawczy / komunikat o błędzie. Jeśli wystąpi błąd lub ostrzeżenie, że inny edytor się nie wyświetli, możesz przerwać uruchamianie, $ git rebase --abortjeśli zobaczysz błąd lub ostrzeżenie, po prostu kontynuuj uruchamianie$ git rebase --continue

Zobaczysz swój 2 komunikat zatwierdzenia. Wybierz jedną lub napisz własną wiadomość zatwierdzenia, zapisz i wyjdź [: wq]

Uwaga 2: Może być konieczne wymuszenie wypchnięcia zmian do zdalnego repozytorium, jeśli uruchomisz polecenie rebase

$ git push -f

$ git push -f origin master

Gnanasekar S.
źródło
1
Uwaga 2: brak git push -f origin/masterinnych odpowiedzi. +1
Rishabh Agrahari
2

Ponieważ używam git cherry-pickdo wszystkiego, dla mnie to naturalne, że robię to nawet tutaj.

Biorąc pod uwagę, że branchXsprawdziłem i na końcu są dwa zatwierdzenia, z których chcę utworzyć jedno zatwierdzenie łączące ich zawartość, robię to:

git checkout HEAD^ // Checkout the privious commit
git cherry-pick --no-commit branchX // Cherry pick the content of the second commit
git commit --amend // Create a new commit with their combined content

Jeśli chcę również zaktualizować branchX(i przypuszczam, że jest to wada tej metody), muszę również:

git checkout branchX
git reset --hard <the_new_commit>
Martin G.
źródło
1

Jeśli twoja gałąź główna git logwygląda mniej więcej tak:

commit ac72a4308ba70cc42aace47509a5e
Author: <[email protected]>
Date:   Tue Jun 11 10:23:07 2013 +0500

    Added algorithms for Cosine-similarity

commit 77df2a40e53136c7a2d58fd847372
Author: <[email protected]>
Date:   Tue Jun 11 13:02:14 2013 -0700

    Set stage for similar objects

commit 249cf9392da197573a17c8426c282
Author: Ralph <[email protected]>
Date:   Thu Jun 13 16:44:12 2013 -0700

    Fixed a bug in space world automation

i chcesz scalić dwie najważniejsze zmiany, wykonaj następujące proste czynności:

  1. Najpierw przejdź do bezpiecznej strony kasy, drugi ostatni zatwierdzenie w oddzielnej gałęzi. Możesz nazwać oddział dowolną nazwą.git checkout 77df2a40e53136c7a2d58fd847372 -b merged-commits
  2. Teraz tylko cherry-pick zmiany od ostatniego zatwierdzenia do tej nowej gałęzi jak: git cherry-pick -n -x ac72a4308ba70cc42aace47509a5e. (Rozwiąż konflikty, jeśli wystąpią)
  3. Więc teraz zmiany w ostatnim zatwierdzeniu są obecne w drugim ostatnim zatwierdzeniu. Ale nadal musisz zatwierdzić, więc najpierw dodaj zmiany, które właśnie wybrałeś, a następnie wykonaj git commit --amend.

Otóż ​​to. Możesz wcisnąć tę scaloną wersję w gałęzi „scalone-zatwierdzenia”, jeśli chcesz.

Ponadto możesz teraz odrzucić dwa commity w swoim głównym oddziale. Po prostu zaktualizuj swój główny oddział jako:

git checkout master
git reset --hard origin/master (CAUTION: This command will remove any local changes to your master branch)
git pull
Usman
źródło
0

Jeśli chcesz połączyć dwa najnowsze zatwierdzenia i po prostu użyć komunikatu starszego zatwierdzenia, możesz zautomatyzować proces za pomocą expect.

Zakładam:

  • Używasz vi jako edytora
  • Twoje zobowiązania są w jednej linii

Testowałem z git version 2.14.3 (Apple Git-98).


#!/usr/bin/env expect
spawn git rebase -i HEAD~2

# change the second "pick" to "squash"
# down, delete word, insert 's' (for squash), Escape, save and quit
send "jdwis \033:wq\r"

expect "# This is a"

# skip past first commit message (assumed to be one line), delete rest of file
# down 4, delete remaining lines, save and quit
send "4jdG\r:wq\r"

interact
erwaman
źródło
Nie jest jasne, co robi twój skrypt.
buhtz
@buhtz Dodałem kilka komentarzy. Daj mi znać, jeśli nadal jest to mylące, a jeśli tak, to która część.
erwaman
Nadal nie jest jasne, co robi twoja zdrapka. expectJest również nieopisany.
buhtz
@buhtz która część jest niejasna? Podałem link do strony z większą dokumentacją expect.
erwaman
Brakuje ogólnego opisu skryptu. Nie jest jasne, co robi. Żadna część nie jest niejasna. Cel samego skryptu jest niejasny.
buhtz