Czy mogę usunąć zatwierdzenie git, ale zachować zmiany

1055

W jednej z moich gałęzi programistycznych wprowadziłem pewne zmiany w mojej bazie kodu. Zanim byłem w stanie ukończyć funkcje, nad którymi pracowałem, musiałem zmienić swój obecny oddział na master, aby zaprezentować niektóre funkcje. Ale samo użycie „git checkout master” zachowało zmiany, które wprowadziłem również w mojej gałęzi programistycznej, a tym samym zepsuło niektóre funkcje w master. Więc to, co zrobiłem, to zatwierdzenie zmian w mojej gałęzi programistycznej z komunikatem zatwierdzenia „tymczasowe zatwierdzenie”, a następnie kasa kontrolna dla wersji demonstracyjnej.

Teraz, kiedy skończyłem wersję demo i wróciłem do pracy w mojej gałęzi programistycznej, chciałbym usunąć „tymczasowe zatwierdzenie”, które wykonałem, zachowując wprowadzone zmiany. Czy to jest możliwe?

tanookiben
źródło
65
Następnym razem:git stash
Matt Ball
51
@MattBall, niekoniecznie. Chociaż git stashjest to dobre narzędzie, zatwierdzanie „wyrzucania w toku” jest również całkiem uzasadnionym urządzeniem.
kostix
3
To świetna cieśnina zasobów od Githuba: jak cofnąć (prawie) cokolwiek z Gitem
jasonleonhard
9
@MattBall @kostix Tak, skrytka jest szczególnie nieodpowiednia do przechowywania „długoterminowego”, biorąc pod uwagę, że jest to jeden globalny stos dla całego repo. Chcę mieć możliwość ukrywania zmian na gałęzi, a następnie przechodzenia do innych gałęzi, robienia czegokolwiek innego, wracam kilka dni później, nie martwiąc się, że git stashw międzyczasie mogłem użyć innej gałęzi.
Alec,
4
Jedną z rzeczy wartych odnotowania stashjest to, że jest on całkowicie lokalny i będzie podatny na utratę kodu w wyniku usunięcia repo lub odtworzenia lub awarii lub utraty sprzętu. IMO, naprawdę powinno się go używać tylko do krótkoterminowego PWT. Uwielbiam zatwierdzenie WIP przed wyjazdem na wakacje: P ... nazwij to zatwierdzeniem zrzutu mózgu!
ϹοδεMεδιϲ

Odpowiedzi:

1617

To takie proste:

git reset HEAD^

Uwaga: niektóre powłoki traktują ^jako znak specjalny (na przykład niektóre powłoki Windows lub ZSH z włączonym globowaniem ), więc "HEAD^"w takich przypadkach może być konieczne zacytowanie .

git resetbez a --hardlub --softprzenosi twój HEADpunkt do wskazanego zatwierdzenia, bez zmiany żadnych plików. HEAD^odnosi się do (pierwszego) zatwierdzenia nadrzędnego twojego bieżącego zatwierdzenia, którym w twoim przypadku jest zatwierdzenie przed tymczasowym.

Zauważ, że inną opcją jest kontynuowanie normalnie, a następnie uruchomienie następnego punktu zatwierdzenia:

git commit --amend [-m … etc]

który zamiast tego edytuje ostatni zatwierdzenie, dając taki sam efekt jak powyżej.

Zauważ, że to (jak w przypadku prawie każdej odpowiedzi git) może powodować problemy, jeśli już zepchnąłeś złe zatwierdzenie do miejsca, z którego ktoś inny mógł je wyciągnąć. Staraj się tego unikać

Gareth
źródło
47
Jest to zdecydowanie najprostsze, ale jeśli już wypchnąłeś swoje zobowiązanie do pilota, a ktoś inny go wyciągnął, bardzo bym się wahał, by zrobić coś innego niż przeprosić.
atw13
11
@sicophrenic, nie przegap okazji przeczytania „Reset Demystified” przy tej okazji.
kostix
33
Dostaję More?po zrobieniu tego. Cokolwiek fatal: ambiguous argument 'HEADwhateverItypedIn': unknown revision or path not in the working tree.
wpisze
50
@DaAwesomeP brzmi, jakbyś używał powłoki, która traktuje ^jako znak specjalny. Możesz zacytować odwołanie "HEAD^"lub skorzystać z alternatywnej składni bez HEAD~1cudzysłowu
Gareth
8
Pracował dla mnie, musiałem jednak uciec od postacigit reset HEAD\^
cevaris
188

Istnieją dwa sposoby radzenia sobie z tym. To, co jest łatwiejsze, zależy od twojej sytuacji

Resetowanie

Jeśli zatwierdzenie, którego chcesz się pozbyć, było ostatnim zatwierdzeniem i nie wykonałeś żadnej dodatkowej pracy, której możesz po prostu użyć git-reset

git reset HEAD^

Przenosi gałąź z powrotem do zatwierdzenia tuż przed bieżącym HEAD. Jednak tak naprawdę nie zmienia plików w twoim drzewie roboczym. W rezultacie zmiany, które były w tym zatwierdzeniu, pokazują się jako zmodyfikowane - to jest jak polecenie „niezalecane”. W rzeczywistości mam do tego alias.

git config --global alias.uncommit 'reset HEAD^'

Następnie możesz po prostu użyć git uncommitw przyszłości, aby wykonać kopię zapasową jednego zatwierdzenia.

Squashing

Zgniecenie zatwierdzenia oznacza połączenie dwóch lub więcej zatwierdzeń w jeden. Robię to dość często. W twoim przypadku zatwierdzono połowę wykonanej funkcji, a następnie zakończyłbyś ją i zatwierdził ponownie z odpowiednim, stałym komunikatem zatwierdzenia.

git rebase -i <ref>

Mówię powyżej, ponieważ chcę wyjaśnić, że może to być dowolna liczba zatwierdzeń z powrotem. Uruchom git logi znajdź zatwierdzenie, którego chcesz się pozbyć, skopiuj jego SHA1 i użyj go zamiast <ref>. Git przeniesie Cię w interaktywny tryb bazy. Pokaże wszystkie zobowiązania między twoim obecnym stanem a tym, co umieścisz na miejscu <ref>. Więc jeśli <ref>jest 10 zatwierdzeń temu, pokaże ci wszystkie 10 zatwierdzeń.

Przed każdym zatwierdzeniem będzie mieć słowo pick. Znajdź zatwierdzenie, którego chcesz się pozbyć i zmień je z pickna fixuplub squash. Używanie fixuppo prostu odrzuca, który zatwierdza wiadomość i łączy zmiany z jej bezpośrednim poprzednikiem na liście. Słowo squashkluczowe robi to samo, ale pozwala edytować komunikat zatwierdzenia nowo połączonego zatwierdzenia.

Pamiętaj, że zatwierdzenia zostaną ponownie zatwierdzone w kolejności, w jakiej pojawiają się na liście po wyjściu z edytora. Więc jeśli dokonałeś tymczasowego zatwierdzenia, a następnie wykonałeś inną pracę w tej samej gałęzi i ukończyłeś funkcję w późniejszym zatwierdzeniu, to użycie rebase pozwoliłoby ci ponownie posortować zatwierdzenia i zmiażdżyć je.

OSTRZEŻENIE:

Wycofanie modyfikuje historię - NIE rób tego w stosunku do wszystkich zobowiązań, które już udostępniłeś innym programistom.

Schowanie

W przyszłości, aby uniknąć tego problemu, rozważ git stashtymczasowe przechowywanie niezamówionej pracy.

git stash save 'some message'

Spowoduje to zapisanie bieżących zmian z boku na liście skrytek. Powyżej znajduje się najbardziej wyraźna wersja polecenia ukrywania, umożliwiająca komentarz opisujący to, co ukrywasz. Możesz także po prostu uruchomić git stashi nic więcej, ale żadna wiadomość nie zostanie zapisana.

Możesz przeglądać listę skrytek za pomocą ...

git stash list

Spowoduje to wyświetlenie wszystkich skrytek, gałęzi, na których zostały wykonane, a także wiadomości i na początku każdej linii oraz identyfikatora skrytki, która wygląda tak, stash@{#}gdzie # jest pozycją w tablicy skrytek.

Aby przywrócić skrytkę (można to zrobić na dowolnym oddziale, niezależnie od tego, gdzie skrytka została pierwotnie utworzona), po prostu uruchom ...

git stash apply stash@{#}

Znów # jest pozycją w tablicy skrytek. Jeśli skrytka, którą chcesz przywrócić, znajduje się w 0pozycji - to znaczy, jeśli była to najnowsza skrytka. Następnie można po prostu uruchomić polecenie bez określania położenia stash git obejmie chodziło Ci ostatni: git stash apply.

Jeśli na przykład pracuję nad niewłaściwą gałęzią - mogę uruchomić następującą sekwencję poleceń.

git stash
git checkout <correct_branch>
git stash apply

W twoim przypadku poruszałeś się trochę po oddziałach, ale ten sam pomysł nadal obowiązuje.

Mam nadzieję że to pomoże.

eddiemoya
źródło
6
git config --global alias.uncommit reset HEAD^tylko aliasy niezalecane do zresetowania. Zamiast zrobićgit config --global alias.uncommit 'reset HEAD^'
mernst
3
Pamiętaj, że będziesz musiał użyć ^^ zamiast ^, jeśli używasz w wierszu polecenia systemu Windows.
Pramod BR
106

Myślę, że tego szukasz

git reset --soft HEAD~1

Cofa ostatnie zatwierdzenie, zachowując zmiany wprowadzone w tym zatwierdzeniu do inscenizacji.

Sudhanshu Jain
źródło
7
Dzięki. To zadziałało dla mnie. Dzwonienie git reset HEAD^w systemie Windows powoduje jedynie wyświetlenie monitu „Więcej?” - cokolwiek to znaczy
Tyron
12
@Tyron ^to znak ucieczki w DOS. Po sparowaniu z nowym wierszem służy jako monit o kontynuację poprzedniego polecenia. Pisanie git reset HEAD^^powinno działać w systemie Windows.
trk
40

Tak, możesz usunąć swoje zatwierdzenie bez usuwania zmian: git reset @ ~

DURGESZ
źródło
7
To jest naprawdę to, czego chcę i moim zdaniem będzie to odpowiedź zaakceptowana, wielkie dzięki!
Carlos Liu
2
Ciekawa, zwarta składnia, nie widziałem go wcześniej. Czym to się różni od git reset --softlub git reset --keep?
user776686,
18

Szukasz albo git reset HEAD^ --softalbo git reset HEAD^ --mixed.

Istnieją trzy tryby polecenia resetowania, jak podano w dokumentacji :

git reset HEAD^ --soft

cofnij git commit. Zmiany nadal istnieją w działającym drzewie (folder projektu) + indeks (--cached)

git reset HEAD^ --mixed

cofnij git commit+ git add. Zmiany nadal istnieją w działającym drzewie

git reset HEAD^ --hard

Jak nigdy nie wprowadziłeś tych zmian w bazie kodu. Zmiany zniknęły z działającego drzewa.

Bar Horing
źródło
9

Dla tych, którzy używają zsh, będziesz musiał użyć:

git reset --soft HEAD\^

Wyjaśniono tutaj: https://github.com/robbyrussell/oh-my-zsh/issues/449

W przypadku utraty adresu URL ważną częścią jest:

Ucieknij ^ w swoim poleceniu

Alternatywnie możesz użyć HEAD ~, abyś nie musiał uciekać za każdym razem.

Greg Hilston
źródło
15
Nigdy nie pamiętam tego polecenia i muszę go znaleźć w Google i znaleźć własną odpowiedź, hahahaha
Greg Hilston,
2
git reset HEAD^działa dla mnie w Zsh, być może został naprawiony.
Ben Kolya Mansley,
@BenKolyaMansley Jaką wersję Zsh używasz?
Greg Hilston,
Używamzsh 5.3 (x86_64-apple-darwin18.0)
Ben Kolya Mansley
7

W moim przypadku już przeforsowałem repozytorium. Auć!

Możesz cofnąć określone zatwierdzenie, zachowując zmiany w plikach lokalnych, wykonując:

git revert -n <sha>

W ten sposób mogłem zachować potrzebne zmiany i cofnąć zatwierdzenie, które już zostało wypchnięte.

Wim Feijen
źródło
W moim przypadku musiałem przywrócić coś, co już wypchnąłem do zdalnego repozytorium. Jeszcze więcej ouch! Najlepszym sposobem było stamtąd git revert bad-commit-sha, git revert -n revert-commit-just-created-shaa potem naprawa. Masz mnie w połowie drogi. Dzięki!
TinkerTenorSoftwareGuy
To wydaje się robić odwrotnie, nie? Tworzy nowe zmiany, które odpowiadają cofnięciu zmian dokonanych w wybranym zatwierdzeniu. Jeśli chcesz zatwierdzić te nowe zmiany, cofniesz pracę, którą chciałeś zachować.
svaens
3

Korzystanie z git 2.9 (dokładnie 2.9.2.windows.1) git reset HEAD^monituje o więcej; nie jestem pewien, czego się tutaj oczekuje. Zobacz poniższy zrzut ekranu

wprowadź opis zdjęcia tutaj

Znaleziono inne rozwiązanie, git reset HEAD~#numberOfCommitsza pomocą którego możemy wybrać liczbę lokalnych zatwierdzeń, które chcesz zresetować, utrzymując zmiany w nienaruszonym stanie. W związku z tym mamy możliwość wyrzucenia wszystkich lokalnych zatwierdzeń, a także ograniczonej liczby lokalnych zatwierdzeń.

Zobacz poniższe zrzuty ekranu git reset HEAD~1w akcji: wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

nkharche
źródło
Prawdopodobnie musisz uciec od znaku ^ - spróbuj git zresetować „HEAD ^” lub git zresetować HEAD \ ^
Mark Fisher
Dodając do tego, git reset HEAD ^^ działa jak pojedynczy ^ jest traktowany jako nowy wiersz.
John Oss,
2

Jeszcze jeden sposób, aby to zrobić.

Dodaj zatwierdzenie na górze tymczasowego zatwierdzenia, a następnie wykonaj:

git rebase -i

Aby połączyć dwa zatwierdzenia w jeden (polecenie otworzy plik tekstowy z wyraźnymi instrukcjami, edytuj go).

Rusłan Osipow
źródło
2
Technicznie poprawne, ale nigdzie tak eleganckie, jak po prostu robienie git reset HEAD^. Baza Git ma tutaj dużo miejsca na błędy.
TheBuzzSaw