Przerwanie skrytki w Git

257

Wystrzeliłem skrytkę i doszło do konfliktu scalenia. W przeciwieństwie do pytania wymienionego jako duplikat, miałem już kilka niezatwierdzonych zmian w katalogu, które chciałem zachować. Nie tylko chcę, aby konflikt scalania zniknął, ale także przywrócić mój katalog do stanu sprzed pop.

Próbowałem git merge --abort, ale git twierdził, że nie trwa scalanie. Czy istnieje prosty sposób na przerwanie pop bez niszczenia zmian, które pierwotnie miałem w katalogu?

Casebash
źródło
Co do twoich niezaangażowanych zmian: czy te zmiany były już w indeksie?
jørgensen
Czy możesz opublikować wersję używanego gita.
Tinman,
2
Przyjęta odpowiedź wygląda na skomplikowaną. Myślę, że całkiem dobrą ogólną praktyką jest nigdy nie robić czegoś takiego jak próba git stash popnieczystego, pracującego reż. W takim przypadku możesz po prostu, git reset --harda twoja skrytka jest nadal nienaruszona. (To mniej więcej sugeruje powiązany link @ BradKoch)
Steven Lu
1
@StevenLu, zgadzam się, ale problem może wystąpić w czysto działającym katalogu, jeśli ukrywasz zmiany w celu przeniesienia ich do innej gałęzi. Skrytka jest w konflikcie z zatwierdzeniami, które istnieją w nowej gałęzi, która nie istniała w starej.
Jake Stevens-Haas,

Odpowiedzi:

55

Ok, myślę, że opracowałem „git stash unapply”. Jest to bardziej złożone niż git apply --reversedlatego, że potrzebujesz odwrotnego scalania na wypadek, gdyby połączenie zostało wykonane przez git stash apply.

Odwrotne scalanie wymaga, aby wszystkie bieżące zmiany zostały wprowadzone do indeksu:

  • git add -u

Następnie odwróć to, merge-recursiveco zostało zrobione przez git stash apply:

  • git merge-recursive stash@{0}: -- $(git write-tree) stash@{0}^1

Teraz pozostaną Ci tylko zmiany bez skrytki. Będą w indeksie. Możesz użyć, git resetaby cofnąć zmiany, jeśli chcesz.

Biorąc pod uwagę, że twój oryginał git stash applyzawiódł, zakładam, że sytuacja odwrotna również może się nie powieść, ponieważ niektóre rzeczy, które chce cofnąć, nie zostały wykonane.

Oto przykład pokazujący, w jaki sposób kopia robocza (via git status) znów jest czysta:

 $ git status
# On branch trunk
nothing to commit (working directory clean)
 $ git stash apply
Auto-merging foo.c
# On branch trunk
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   foo.c
#
no changes added to commit (use "git add" and/or "git commit -a")
 $ git add -u
 $ git merge-recursive stash@{0}: -- $(git write-tree) stash@{0}^1
Auto-merging foo.c
 $ git status
# On branch trunk
nothing to commit (working directory clean)
Ben Jackson
źródło
2
po zrobieniu tego, czy wcześniej ukryte zmiany zostaną utracone na zawsze, czy też są z powrotem w skrytce?
Brian H.
7
git stash applynigdy nie upuszcza skrytki, a gdy scalenie się nie powiedzie, git stash popzachowuje również skrytkę
Xerus
Złe rzeczy zdarzą się, jeśli podczas oryginalnego skrytki
wystąpi
286

Mój przypadek użycia: właśnie próbowałem wyskoczyć na niewłaściwą gałąź i dostałem konflikty. Wszystko, czego potrzebuję, to cofnąć pop, ale zachować go na liście skrytek, aby mógł wyskoczyć na właściwej gałęzi. Ja to zrobiłem:

git reset HEAD --hard
git checkout my_correct_branch
git stash pop

Łatwo.

jmoz
źródło
22
Więc skrytka, którą chcesz, pozostaje na liście skrytek, dopóki nie uda ci się pop?
Ryan Clark
52
To nie jest odpowiedź na oryginalne pytanie, ponieważ zniszczyłoby lokalne zmiany, które nie zostały popełnione.
Dmitry
13
@RyanClark Zobacz odpowiedź Davida poniżej. Zasadniczo tak, pozostaje na liście skrytek.
fkorsa
1
Myślę, że zdecydowana większość użytkowników, którzy znajdą się w takiej sytuacji, uzna to rozwiązanie za idealne. Nie mogę sobie wyobrazić sytuacji, w której dokonałem niezatwierdzonych zmian w moim katalogu roboczym przed próbą stash popwejścia do niego. To brzmi jak przepis na katastrofę.
Shadoninja,
2
Miły. Po przeczytaniu tego spojrzałem na git stash pop docs i mówi: „Zastosowanie stanu może się nie powieść w przypadku konfliktów; w tym przypadku nie jest usuwane z listy ukrytych”. Dlatego właśnie skrytkę można ponownie otworzyć po resecie / kasie.
Brady Holt
48

Edycja: Z git help stashdokumentacji w sekcji pop:

Stosowanie stanu może zakończyć się niepowodzeniem w przypadku konfliktów; w takim przypadku nie jest usuwany z listy skrytek. Musisz rozwiązać konflikty ręcznie i później ręcznie wywołać zrzut git.

Jeśli używana jest opcja --index, wówczas próbuje przywrócić nie tylko zmiany działającego drzewa, ale także zmiany indeksu. Może się to jednak nie powieść, gdy wystąpią konflikty (które są przechowywane w indeksie, w związku z czym nie można już stosować zmian w takim stanie, w jakim były pierwotnie).

Spróbuj wydrukować całe swoje repozytorium do nowego katalogu (więc masz jego kopię) i uruchom:

git stash show i zapisz gdzieś to wyjście, jeśli ci na tym zależy.

następnie: git stash dropaby usunąć sprzeczną skrytkę, a następnie:git reset HEAD

To powinno pozostawić twoje repo w stanie, w jakim było wcześniej (mam nadzieję, że nadal nie byłem w stanie naprawić twojego problemu)

===

Próbuję zareagować na twój problem, ale wszystko, co dostaję, gdy używamy git stash popto:

error: Your local changes to the following files would be overwritten by merge:
...
Please, commit your changes or stash them before you can merge.
Aborting

W czystym reż:

git init
echo hello world > a
git add a & git commit -m "a"
echo hallo welt >> a
echo hello world > b
git add b & git commit -m "b"
echo hallo welt >> b
git stash
echo hola mundo >> a
git stash pop

Nie widzę, żeby git próbował scalić moje zmiany, po prostu się nie udaje. Czy masz jakieś kroki repro, które możemy wykonać, aby Ci pomóc?

DavidG
źródło
Spróbuj ukryć zmiany w innym pliku.
asmeurer
Próba ukrycia się w innym pliku nie działała (zobacz nowe kroki repro). Po prostu nie mogę powtórzyć problemu ...
DavidG,
1
To rozwiązanie nie działa, jeśli w katalogu roboczym występują niezatwierdzone zmiany.
tutaj
@there To nie jest prawdziwe rozwiązanie, to tylko po to, aby pokazać, że nie mogę odtworzyć problemu OP i że nie podał żadnych kroków, aby dotrzeć do miejsca, w którym się znajduje.
DavidG
1
Rzeczywisty scenariusz to: 1) Zmień plik A. 2) Zmień skrytkę 3) Dokonaj sprzecznej zmiany w pliku A i zatwierdź (np. Zmień tę samą linię) 4) Zmień plik B 5) Wykonaj „git stash pop”. Teraz masz konflikt i lokalne zmiany. Zasadniczo będą znajdować się w różnych plikach, ale nigdy nie wiadomo, które zmodyfikowane pliki nie powodują konfliktu ze skrytki, a które lokalne zmiany niestacjonarne.
Dmitry
16

Zawsze używałem

git reset --merge

Nie pamiętam, żeby kiedykolwiek zawiodła.

Kenn Sebesta
źródło
@GauravPaliwal, spojrzę na to następnym razem git reset. Czy wiesz, czy jest funkcjonalnie identyczny git reset --merge?
Kenn Sebesta
5

Jeśli nie musisz się martwić o jakiekolwiek inne zmiany, które wprowadziłeś i chcesz po prostu wrócić do ostatniego zatwierdzenia, możesz:

git reset .
git checkout .
git clean -f
Hugo der Hungrige
źródło
4

OK, myślę, że udało mi się znaleźć przepływ pracy, który doprowadzi cię z powrotem do miejsca, w którym musisz być (tak jakbyś nie zrobił popu).

ZAPRASZAMY WCZEŚNIEJ! Nie wiem, czy to zadziała, więc skopiuj całe repozytorium na wypadek, gdyby nie zadziałało.

1) Napraw problemy z łączeniem i napraw cały konflikt, wybierając wszystkie zmiany, które pochodzą z łatki (w żółwiku pokazuje się to jako jeden. REMOETE (ich)).

git mergetool

2) Zatwierdź te zmiany (zostaną one już dodane za pomocą polecenia scaletool). Przekaż mu komunikat zatwierdzenia „scalenia” lub coś, co pamiętasz.

git commit -m "merge"

3) Teraz nadal będziesz mieć lokalne nieskalowane zmiany, które zacząłeś pierwotnie, z nowym zatwierdzeniem z łatki (możemy się tego później pozbyć). Teraz zatwierdź swoje nieetapowe zmiany

git add .
git add -u .
git commit -m "local changes"

4) Odwróć łatkę. Można to zrobić za pomocą następującego polecenia:

git stash show -p | git apply -R

5) Zatwierdź następujące zmiany:

git commit -a -m "reversed patch"

6) Pozbądź się patch / unpatch commits

git rebase -i HEAD^^^

z tego usuń dwie linie z „scaleniem” i „odwróconą łatką”.

7) Odzyskaj niezmienione zmiany z powrotem i cofnij zatwierdzenie „zmiany lokalne”

git reset HEAD^

Przeanalizowałem go z prostym przykładem i pozwala wrócić do miejsca, w którym chcesz być - bezpośrednio przed wyskakowaniem skrytki, z lokalnymi zmianami i skrytką nadal dostępną.

agentgonzo
źródło
Jeśli to nie zadziała, mam nadzieję, że dostaniesz się tam przez większość drogi! :-)
agentgonzo
git stash show -p | git apply -Rto nie działa, jeśli git stash applyfaktycznie się scaliło. Zobacz moją odpowiedź ...
Ben Jackson
3

Rozwiązałem to w nieco inny sposób. Oto co się stało.

Najpierw wpadłem na niewłaściwą gałąź i dostałem konflikty. Skrytka pozostała nienaruszona, ale indeks był w trakcie rozwiązywania konfliktów, blokując wiele poleceń.

Prosty git reset HEADprzerwał rozwiązywanie konfliktu i pozostawił niezaangażowane (i NIEZALEŻNE ) zmiany.

Kilka git co <filename>przywróciło indeks do stanu początkowego. W końcu zmieniłem gałąź git co <branch-name>i uruchomiłem nową git stash pop, która rozwiązała się bez konfliktów.

pid
źródło
2

Jakieś pomysły:

  • Służy git mergetooldo dzielenia plików scalania na oryginalne i nowe części. Mam nadzieję, że jednym z nich jest plik ze zmianami nieprzechowującymi.

  • Zastosuj różnicę skrytki w odwrotnej kolejności, aby cofnąć tylko te zmiany. Prawdopodobnie będziesz musiał ręcznie rozdzielić pliki z konfliktami scalania (co, mam nadzieję, zadziała powyższa sztuczka).

Nie testowałem żadnego z nich, więc nie wiem na pewno, czy będą działać.

asmeurer
źródło
1

Mógłbym odtworzyć czysty git stash popkatalog „brudny” z niezatwierdzonymi zmianami, ale jeszcze nie pop, który generuje konflikt scalania.

Jeśli podczas konfliktu scalenia skrytka, którą próbujesz zastosować, nie zniknęła, możesz spróbować zbadać git show stash@{0}(opcjonalnie z --ourslub --theirs) i porównać z git statisi git diff HEAD. Powinieneś być w stanie zobaczyć, które zmiany pochodzą z zastosowania skrytki.

Jakub Narębski
źródło
1

Jeśli DavidG ma rację, że nie wyskoczył z ukrycia z powodu konfliktu scalenia, wystarczy wyczyścić katalog roboczy. Szybko git commitwszystko, na czym Ci zależy. (Możesz zrobić resetlub squashzatwierdzić później, jeśli nie skończyłeś.) Następnie ze wszystkim, co zależy Ci na bezpieczeństwie, git resetwszystko inne, co git stash popzrzuciło do twojego katalogu roboczego.

Robert
źródło
1

Jeśli przed git stash pop, jak w pytaniu, nie było zmian etapowych , wówczas powinny działać następujące dwa polecenia.

git diff --name-only --cached | xargs git checkout --ours HEAD
git ls-tree stash@{0}^3 --name-only | xargs rm

Pierwszy odwraca wszelkie scalenia ze skrytki, udane lub nie. Drugi usuwa wszelkie nieśledzone pliki wprowadzone przez skrytkę.

Od man git stash: The working directory must match the index. co wskazuje @DavidG, stash popzakończy się niepowodzeniem, jeśli wystąpią jakiekolwiek konflikty aktualnie zmodyfikowanych plików. W związku z tym nie powinniśmy martwić się rozwiązywaniem konfliktów scalania poza powrotem do nich HEAD. Wszelkie pozostałe zmodyfikowane pliki nie są następnie powiązane ze skrytką i zostały zmodyfikowane przedstash pop

Jeśli wystąpiły zmiany etapowe, nie jestem pewien, czy możemy polegać na tych samych poleceniach i możesz spróbować techniki @Ben Jackson. Sugestie docenione ..

Oto konfiguracja testowania dla wszystkich różnych przypadków https://gist.github.com/here/4f3af6dafdb4ca15e804

# Result:
# Merge succeeded in m (theirs)
# Conflict in b
# Unstaged in a
# Untracked in c and d

# Goal:
# Reverse changes to successful merge m
# Keep our version in merge conflict b
# Keep our unstaged a
# Keep our untracked d
# Delete stashed untracked c
tutaj
źródło
Myślę, że to jak dotąd najbardziej poprawna odpowiedź. Bardzo dobrze przemyślane.
Agent piątek
0

Służy git reflogdo wyświetlania wszystkich zmian dokonanych w historii git. Skopiuj identyfikator akcji i wpiszgit reset ACTION_ID

Fatih Acet
źródło
2
Git nie tworzy wpisu dziennika przed
wyskakowaniem
-1

Publikuję tutaj w nadziei, że moja odpowiedź będzie dla innych pomocna. Miałem podobny problem, kiedy próbowałem zrobić skrytkę na innej gałęzi niż ta, z której się ukryłem. W moim przypadku nie miałem żadnych plików, które nie zostały zatwierdzone ani nie były w indeksie, ale nadal dostałem się do sprawy dotyczącej konfliktów scalania (taka sama jak @pid). Jak inni zauważyli wcześniej, nieudany pop git stash rzeczywiście zachował moją skrytkę, a następnie szybki reset gita HEAD plus powrót do mojej oryginalnej gałęzi i zrobienie skrytki stamtąd rozwiązało mój problem.

Victor Camacho
źródło