Cofanie bazy git


3178

Czy ktoś wie, jak łatwo cofnąć bazę git?

Jedyny sposób, który przychodzi na myśl, to podejść ręcznie:

  • git wypisanie elementu nadrzędnego zatwierdzenia do obu gałęzi
  • następnie stwórz gałąź temp stamtąd
  • wybierz wszystkie zobowiązania ręcznie
  • zastąp gałąź, w której bazowałem ponownie, ręcznie utworzoną gałęzią

W mojej obecnej sytuacji to zadziała, ponieważ mogę łatwo wykryć zatwierdzenia z obu gałęzi (jeden był moim, a drugi moim kolegą).

Jednak moje podejście wydaje mi się nieoptymalne i podatne na błędy (powiedzmy, że właśnie bazowałem na 2 własnych gałęziach).

Jakieś pomysły?

Wyjaśnienie: Mówię o bazie, podczas której odtwarzano kilka zmian. Nie jedyny.


6
Pamiętaj też, że podczas zmiany bazy możesz wykluczyć zatwierdzenia lub je zgnieść; zmiany te nie są odwracalne bez wskaźnika do oryginalnych węzłów lub przeszukiwania rejestru, więc wybieranie nie działałoby.
ANeves

Odpowiedzi:


4337

Najłatwiejszym sposobem byłoby znalezienie zatwierdzenia głównego oddziału, ponieważ było to bezpośrednio przed uruchomieniem bazy w rejestrze ...

git reflog

i zresetować do niej bieżącą gałąź (ze zwykłymi zastrzeżeniami dotyczącymi absolutnej pewności przed resetowaniem z --hardopcją).

Załóżmy, że stary zatwierdzenie znajduje się HEAD@{5}w dzienniku referencyjnym:

git reset --hard HEAD@{5}

W systemie Windows może być konieczne zacytowanie referencji:

git reset --hard "HEAD@{5}"

Możesz sprawdzić historię starej głowy kandydata, wykonując git log HEAD@{5}( Windows git log "HEAD@{5}" :).

Jeśli nie wyłączyłeś ponownego rejestrowania gałęzi, powinieneś być w stanie to zrobić, git reflog branchname@{1}ponieważ rebase odłącza głowę gałęzi przed ponownym podłączeniem do ostatniej głowy. Chciałbym dokładnie to sprawdzić, ponieważ nie sprawdziłem tego ostatnio.

Domyślnie wszystkie dzienniki logowania są aktywowane dla repozytoriów innych niż zwykłe:

[core]
    logAllRefUpdates = true

114
Git reflog jest niesamowity, pamiętaj tylko, że możesz uzyskać lepiej sformatowane dane wyjściowe git log -g(wskazówka progit.org/book Scotta Chakona ).
karmi

60
@Zach: git rebase --abort( -inie ma sensu z --abort) służy do porzucenia bazy, która nie została ukończona - albo z powodu konfliktów, albo dlatego, że była interaktywna, albo obie; pytanie nie polega na cofnięciu udanego rebase'u. Możesz użyć rebase --abortalbo w reset --hardzależności od sytuacji, w której się znalazłeś. Nie powinieneś robić obu tych rzeczy.
CB Bailey,

311
Na wszelki wypadek zrobić kopię zapasową pierwszy: git tag BACKUP. Możesz do niego wrócić, jeśli coś pójdzie nie tak:git reset --hard BACKUP
kolypto

6
Jeśli dokonałeś wielu zatwierdzeń, HEAD @ {#}, którego szukasz, będzie poprzedzony, commit:a nie rebase:. Brzmi oczywisto, ale trochę mnie to zamieszało.
Warpling

4
Dołączenie do drużyny po przypadkowym wyjęciu bazy: D. Czy nie git reset --hard ORIG_HEADzrobiłby tego lewy zaraz po przypadkowym zwrocie?
quaylar

1485

W rzeczywistości rebase zapisuje punkt początkowy, ORIG_HEADwięc jest to zwykle tak proste, jak:

git reset --hard ORIG_HEAD

Jednakże reset, rebasei mergewszystko Zapisz oryginalnego HEADwskaźnik do ORIG_HEADtak, jeśli zrobiłeś żadnej z tych poleceń od rebase starasz cofnąć następnie będziesz musiał użyć reflog.


34
Jeśli ORIG_HEADnie jest już użyteczny, możesz również użyć branchName@{n}składni, gdzie nn-ta poprzednia pozycja wskaźnika gałęzi. Na przykład, jeśli zmienisz featureAgałąź na swoją mastergałąź, ale nie podoba ci się wynik bazy, możesz po prostu git reset --hard featureA@{1}zresetować gałąź z powrotem dokładnie tam, gdzie była przed dokonaniem zmiany bazy. Możesz przeczytać więcej o składni gałęzi @ {n} w oficjalnych dokumentach Git dotyczących poprawek .

15
To jest najłatwiejsze. Kontynuuj to git rebase --abortjednak.
Seph

1
@DaBlick to działało dobrze dla mnie po całkowicie udanym rebase bez żadnych konfliktów git 2.17.0.
dvlsg

4
I pozwól mi to uzupełnić: git reset --hard ORIG_HEADmogę używać wielokrotnie, aby cofać się raz po raz. Powiedz, jeśli A --- zmiana podstawy na --- B --- zmiana podstawy na --- C, teraz jestem w C, mogę wrócić do A, używając dwa razygit reset --hard ORIG_HEAD
CalvinChe

5
@Seph Czy możesz wyjaśnić, dlaczego sugerujesz kontynuację git rebase --abort?
UpTheCreek

386

Odpowiedź Charlesa działa, ale możesz to zrobić:

git rebase --abort

posprzątać po reset.

W przeciwnym razie może zostać wyświetlony komunikat „ Interactive rebase already started”.


4
Ten usunął część „| REBASE” w moim monicie. +1
Wouter Thielen

62
To nie było pytanie. Pytanie dotyczy tego, jak cofnąć ukończenie bazy.
Arunav Sanyal

2
Powinien być komentarzem do odpowiedzi Charlesa, ponieważ nie odpowiada na pytanie na jej temat
Murmel

Hm, nie musiałem tego robić.
Viktor Seč

1
Jedyny, który dla mnie działał!
Alexandre Picard,

90

Zresetowanie gałęzi do wiszącego obiektu zatwierdzania starej końcówki jest oczywiście najlepszym rozwiązaniem, ponieważ przywraca poprzedni stan bez wysiłku. Ale jeśli zdarzyło Ci się zgubić te zatwierdzenia (np. Ponieważ w międzyczasie zrzuciłeś swoje repozytorium, lub jest to świeży klon), zawsze możesz ponownie zmienić podstawę gałęzi. Kluczem do tego jest --ontoprzełącznik.

Powiedzmy, że masz gałąź tematyczną z wyobraźnią topic, którą rozgałęziłeś, mastergdy końcówką masterbyło 0deadbeefzatwierdzenie. W pewnym momencie, kiedy topicbyłeś na gałęzi, zrobiłeś to git rebase master. Teraz chcesz to cofnąć. Oto jak:

git rebase --onto 0deadbeef master topic

Spowoduje to wzięcie wszystkich zatwierdzeń topic, które nie są włączone masteri odtworzenie ich na wierzchu 0deadbeef.

Dzięki --ontomożesz zmienić swoją historię w praktycznie dowolny kształt .

Baw się dobrze. :-)


3
Myślę, że jest to najlepsza opcja ze względu na elastyczność. Rozgałęziłem b1 poza masterem, a następnie przestawiłem b1 na nową gałąź b2, a następnie chciałem ponownie przywrócić b1, aby był oparty na master. Uwielbiam gita - dzięki!
ripper234,

2
To najlepsza opcja tutaj! Zachował wszystkie zmiany, które mam w mojej bieżącej gałęzi, i usunął wszystkie niechciane!
Alicia Tang,

Powiedziałbym to za pomocą kombinacji --ontoi -imożesz zmienić swoją historię w prawie dowolny kształt. Użyj gitk (lub gitx na Macu), aby zobaczyć kształty, które tworzysz :-).
rjmunro

69

W rzeczywistości umieszczam tag zapasowy na gałęzi, zanim wykonam jakąś nierywalną operację (większość zmian w bazie jest trywialna, ale zrobiłbym to, jeśli wygląda gdziekolwiek złożona).

Następnie przywracanie jest tak proste, jak git reset --hard BACKUP.


2
Ja też to robię. Przydatne jest także GIT diff BACKUP..HEAD, aby upewnić się, że zmieniłeś to, co chciałeś.
Paul Bone,

5
Kiedyś to robiłem, ale ponieważ poczułem się lepiej z reflogiem, nie czuję, że jest to konieczne. Reflog zasadniczo robi to w twoim imieniu przy każdej zmianie HEAD.
Pete Hodgson,

4
Cóż, wolę sensowne imiona, ponieważ szukanie odpowiedniego elementu w reflogu czasami nie jest wcale zabawne.
Alex Gontmakher,

12
W rzeczywistości nie musisz nawet tworzyć gałęzi zapasowej, możesz po prostu użyć branchName@{n}składni, oto nn-ta poprzednia pozycja wskaźnika gałęzi. Na przykład, jeśli zmienisz featureAgałąź na swoją mastergałąź, ale nie podoba ci się wynik bazy, możesz po prostu git reset --hard featureA@{1}zresetować gałąź z powrotem dokładnie tam, gdzie była przed dokonaniem zmiany bazy. Możesz przeczytać więcej o branch@{n}składni w oficjalnych dokumentach Git dotyczących poprawek .

2
W rzeczywistości nie musisz nawet używać powyższej składni, zgodnie z odpowiedzią Pata Notza , oryginał HEADgałęzi jest tymczasowo przechowywany ORIG_HEAD. Ale technika użycia zapasowej etykiety oddziału również działa, to tylko kolejne kroki.

68

W przypadku , gdy wypchnąłeś swoją gałąź do zdalnego repozytorium (zwykle jest to jej początek), a następnie wykonałeś udany rebase (bez scalania) ( git rebase --abortdaje „No rebase in progress”), możesz łatwo zresetować gałąź za pomocą polecenia:

git reset --hard origin / {branchName}

Przykład:

$ ~/work/projects/{ProjectName} $ git status
On branch {branchName}
Your branch is ahead of 'origin/{branchName}' by 135 commits.
  (use "git push" to publish your local commits)

nothing to commit, working directory clean

$ ~/work/projects/{ProjectName} $ git reset --hard origin/{branchName}
HEAD is now at 6df5719 "Commit message".

$ ~/work/projects/{ProjectName} $ git status
On branch {branchName}
Your branch is up-to-date with 'origin/{branchName}.

nothing to commit, working directory clean

To jest dla mnie poprawna odpowiedź. Rebase i commit przed rebase miały ten sam identyfikator zatwierdzenia, a powrót do HEAD {1} po prostu nie przywróciłby rebase!
Bill Kotsias,

23

Korzystanie reflognie działało dla mnie.

To, co zadziałało, było podobne do opisanego tutaj . Otwórz plik w .git / logs / refs nazwany na podstawie gałęzi, która została ponownie wydana, i znajdź wiersz zawierający „sfinalizowany rebase”, coś w stylu:

5fce6b51 88552c8f Kris Leech <me@example.com> 1329744625 +0000  rebase finished: refs/heads/integrate onto 9e460878

Kasy drugiego zatwierdzenia wymienionego w wierszu.

git checkout 88552c8f

Po potwierdzeniu, że zawiera moje utracone zmiany, rozgałęziłem się i odetchnąłem z ulgą.

git log
git checkout -b lost_changes


3
Whoa - z tego linku: „Jest jedno zastrzeżenie: straciłem historię oddziału, ale w tym przypadku tak naprawdę nie miało to znaczenia. Byłem szczęśliwy, że odzyskałem swoje zmiany”. ?
ruffin

16

W przypadku wielu zatwierdzeń pamiętaj, że wszelkie zatwierdzenia odwołują się do całej historii prowadzącej do tego zatwierdzenia. Tak więc w odpowiedzi Karola, należy przeczytać „stare zatwierdzenie” jako „najnowsze ze starych zatwierdzeń”. Jeśli zresetujesz to zatwierdzenie, cała historia prowadząca do tego zatwierdzenia pojawi się ponownie. To powinno zrobić, co chcesz.


11

Po rozwiązaniu @Allan i @Zearin chciałbym po prostu zrobić komentarz, ale nie mam wystarczającej reputacji, więc użyłem następującego polecenia:

Zamiast robić git rebase -i --abort (uwaga -i ) Musiałem po prostu zrobić git rebase --abort( bez -i ).

Używając obu -ii --abortjednocześnie powoduje Git, aby pokazać mi listę wykorzystanie / opcji.

Tak więc mój poprzedni i aktualny status oddziału z tym rozwiązaniem to:

matbhz@myPc /my/project/environment (branch-123|REBASE-i)
$ git rebase --abort

matbhz@myPc /my/project/environment (branch-123)
$

11

Jeśli udało Ci się dokonać repartycji przeciwko zdalnemu oddziałowi i git rebase --abortnadal nie możesz, możesz wykonać kilka sztuczek, aby zapisać swoją pracę i nie mieć wymuszonych wypchnięć. Załóżmy, że twoja obecna gałąź, która została przez pomyłkę zwolniona, nazywa się your-branchi śledziorigin/your-branch

  • git branch -m your-branch-rebased # zmień nazwę bieżącej gałęzi
  • git checkout origin/your-branch # kasy do najnowszego stanu, o którym wiadomo, że pochodzi
  • git checkout -b your-branch
  • sprawdź git log your-branch-rebased, porównaj git log your-branchi zdefiniuj brakujące zatwierdzeniayour-branch
  • git cherry-pick COMMIT_HASH za każde zatwierdzenie w your-branch-rebased
  • popchnij swoje zmiany. Należy pamiętać, że z dwoma lokalnymi oddziałami są powiązane remote/your-branchi należy tylko wypychaćyour-branch

4

Powiedzmy, że dokonuję zmiany bazy na mój dział funkcji i otrzymuję 30 nowych zatwierdzeń, które coś psują. Przekonałem się, że często najłatwiej jest po prostu usunąć złe zatwierdzenia.

git rebase -i HEAD~31

Interaktywny rebase dla ostatnich 31 zatwierdzeń (nie boli, jeśli wybierzesz zdecydowanie za dużo).

Po prostu weź commity, których chcesz się pozbyć i oznacz je „d” zamiast „pick”. Teraz zatwierdzenia są skutecznie usuwane, cofając zmianę bazy (jeśli usuniesz tylko zatwierdzenia, które właśnie otrzymałeś podczas zmiany bazy).


3

Dla początkujących / każdego, kto zbytnio boi się twardego resetu, możesz pobrać zatwierdzenie z dziennika, a następnie zapisać go jako nowy oddział.

git reflog

Znajdź zatwierdzenie tuż przed rozpoczęciem bazowania. Może być konieczne przewinięcie w dół, aby je znaleźć (naciśnij Enter lub PageDown). Zanotuj numer HEAD i zastąp 57:

git checkout HEAD@{57}

Przejrzyj gałąź / zatwierdzenia, jeśli wygląda dobrze, utwórz nową gałąź za pomocą tego HEAD:

git checkout -b new_branch_name

2

Jeśli jesteś na oddziale, możesz użyć:

git reset --hard @{1}

Istnieje nie tylko dziennik referencyjny HEAD (uzyskany przez git reflog), ale także dzienniki referencyjne dla każdej gałęzi (otrzymane przez git reflog <branch>). Tak więc, jeśli jesteś włączony, masterto git reflog masterwyświetli listę wszystkich zmian w tej gałęzi. Można odwołać się do tego, by zmiany master@{1}, master@{2}itp

git rebase zwykle zmienia HEAD wiele razy, ale bieżąca gałąź zostanie zaktualizowana tylko raz.

@{1}jest po prostu skrótem do bieżącej gałęzi , więc jest równy, master@{1}jeśli jesteś włączony master.

git reset --hard ORIG_HEADnie będzie działać, jeśli korzystałeś git resetpodczas interaktywnego rebase.


1

Zwykle robię to git reset #commit_hash

do ostatniego zatwierdzenia, gdzie myślę, że rebase nie przyniosło żadnego efektu.

następnie git pull

Teraz twoja gałąź powinna pasować dokładnie tak samo jak master, a zmiany w repbased nie powinny w niej być.

Teraz można po prostu wybrać commits na tej gałęzi.


1

Wypróbowałem wszystkie sugestie po zresetowaniu i ponownym zalogowaniu bez powodzenia. Przywrócenie lokalnej historii IntelliJ rozwiązało problem utraconych plików


Dzięki! Nigdy wcześniej nie korzystałem z historii lokalnej, ale okazało się, że jest to dla mnie najłatwiejsza opcja odzyskania kodu przypadkowo zmienionego. Bardzo miła, ale nieco ukryta funkcja.
Magnus W

0

git reset --hard origin / {branchName}

jest poprawnym rozwiązaniem, aby zresetować wszystkie lokalne zmiany dokonane przez rebase.


1
jeśli twardy reset spowoduje origin/branchutratę zmian między HEAD a tym punktem. Ogólnie nie chcesz tego.
bluesmonk

Właśnie tego chciałem w moim przypadku. Tak entuzjastycznie.
Mandar Vaze

-4

Jeśli coś zepsujesz w ramach bazy danych git, np. git rebase --abortGdy masz nieprzydzielone pliki, zostaną one utracone i git reflognie pomogą. To mi się przydarzyło i musisz pomyśleć tutaj nieszablonowo. Jeśli masz szczęście, jak ja i korzystasz z IntelliJ Webstorm, możesz right-click->local historyi możesz przywrócić poprzedni stan pliku / folderów bez względu na to, jakie błędy popełniłeś przy oprogramowaniu do wersji. Zawsze dobrze jest mieć kolejny działający bezpiecznie.


5
git rebase --abortPrzerywa aktywną rebase, nie cofnąć się rebase. Również używanie dwóch VCS w tym samym czasie jest złym pomysłem. Jest to miła funkcja w oprogramowaniu Jetbrains, ale nie powinieneś używać obu. Lepiej jest po prostu nauczyć się Git, szczególnie podczas odpowiadania na pytania dotyczące przepełnienia stosu, które dotyczą Git.
dudewad
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.