Jaka jest różnica między git reset --mixed, --soft i --hard?

740

Chcę podzielić zatwierdzenie i nie jestem pewien, której opcji resetowania użyć.

Patrzyłem na stronę Co robi „git reset”? , ale zdałem sobie sprawę, że tak naprawdę nie rozumiem, czym jest indeks git lub obszar oceny, dlatego wyjaśnienia nie pomogły.

Również przypadki użycia --mixedi --softwyglądają tak samo dla mnie w tej odpowiedzi (kiedy chcesz naprawić i ponownie uruchomić). Czy ktoś może to jeszcze bardziej rozbić? Zdaję sobie sprawę, że --mixedjest to prawdopodobnie opcja, ale chcę wiedzieć, dlaczego . Wreszcie o czym --hard?

Czy ktoś może mi podać przykładowy przepływ pracy, w jaki sposób miałaby nastąpić wybór 3 opcji?

Michael Chinen
źródło
1
Przejdę do edycji mojej odpowiedzi na to drugie pytanie, aby wyjaśnić to nieco bardziej.
Cascabel,
Odpowiedź @mkarasek jest całkiem dobra, ale można również zainteresować się tym pytaniem .
brandizzi
3
Uwaga do siebie: W ogóle , soft: stage everything, mixed: unstage everything, hard: ignore everythingaż do popełnienia mam resetowanie od.
user1164937,
Zobacz także dodatkowe komentarze Boba Kerna na ten temat
ToolmakerSteve,
kolejny dobry artykuł David Zychz jasnym wyjaśnieniem - davidzych.com/difference-between-git-reset-soft-mixed-and-hard
src3369

Odpowiedzi:

1489

Po zmodyfikowaniu pliku w repozytorium zmiana jest początkowo niestabilna. Aby go zatwierdzić, musisz go wyodrębnić - to znaczy dodać go do indeksu - za pomocą git add. Gdy dokonujesz zatwierdzenia, zatwierdzane zmiany są tymi, które zostały dodane do indeksu.

git resetzmienia się co najmniej tam, gdzie HEADwskazuje bieżąca gałąź ( ). Różnica między --mixedi --softpolega na tym, czy indeks również jest modyfikowany. Jeśli więc jesteśmy w branży masterz tą serią zatwierdzeń:

- A - B - C (master)

HEADwskazuje Ci indeks pasuje C.

Kiedy uruchamiamy git reset --soft B, master(i tym samym HEAD) teraz wskazuje na B, ale indeks nadal ma zmiany od C; git statuspokaże je jako zainscenizowane. Więc jeśli uruchomimy git commitw tym momencie, otrzymamy nowe zatwierdzenie z takimi samymi zmianami jak C.


Okej, więc zaczynając odtąd ponownie:

- A - B - C (master)

Zróbmy teraz git reset --mixed B. (Uwaga: --mixedjest opcją domyślną). Jeszcze raz masteri HEADwskaż B, ale tym razem indeks również został zmodyfikowany, aby pasował B. Jeśli uruchomimy git commitw tym momencie, nic się nie stanie, ponieważ indeks pasuje HEAD. Wciąż mamy zmiany w katalogu roboczym, ale ponieważ nie ma ich w indeksie, git statuspokazuje je jako niestacjonarne. Aby je popełnić, zrobiłbyś to, git adda następnie zrobiłeś jak zwykle.


I na koniec --hardjest taki sam jak --mixed(zmienia twój HEADi indeks), tyle że --hardmodyfikuje również katalog roboczy. Jeśli jesteśmy na stronie Ci działamy git reset --hard B, wówczas dodane zmiany C, a także wszelkie niezatwierdzone zmiany, które masz, zostaną usunięte, a pliki w kopii roboczej będą pasować do zatwierdzenia B. Ponieważ w ten sposób możesz trwale utracić zmiany, zawsze powinieneś uruchomić git statusprzed wykonaniem twardego resetu, aby upewnić się, że katalog roboczy jest czysty lub że nie masz nic przeciwko utracie niezatwierdzonych zmian.


I wreszcie wizualizacja: wprowadź opis zdjęcia tutaj

mkarasek
źródło
44
Innymi słowy, --soft odrzuca ostatnie zatwierdzenie, --mix odrzuca ostatnie zatwierdzenie i dodanie, --hard odrzuca ostatnie zatwierdzenie, dodanie i wszelkie zmiany dokonane w kodach, które są takie same w przypadku GIT Checkout HEAD
James Wang
11
@eventualEntropy można odzyskać zaangażowanych zmiany z reflog; niezatwierdzone zmiany, które są usuwane, reset --hardznikają na zawsze.
mkarasek
2
@Robert Ani; --mixedzmienia indeks, ale nie katalog roboczy, więc nie ma to wpływu na wszelkie modyfikacje lokalne.
mkarasek
3
Może być pomocny dla osób wizualnie korzystających z gita na terminalu z kolorem: 1.'git reset - miękki A ', a zobaczysz rzeczy B i C w kolorze zielonym (etapowe) 2.'git reset - zmieszany A' i będziesz zobacz rzeczy B i C na czerwono (bez sceny) 3.'Git reset - twarde A 'i nigdzie nie zobaczysz zmian B i C (będzie tak, jakby nigdy nie istniały)
timhc22,
2
@ user1933930 1 i 3 zostawią cię z - A - B - C′, gdzie C ′ zawiera te same zmiany co C (z innym znacznikiem czasu i ewentualnie zatwierdzeniem wiadomości). 2 i 4 zostawią cię z - A - D, gdzie D zawiera połączone zmiany B i C.
mkarasek
213

Mówiąc najprościej:

  • --soft: niezaangażowane zmiany, zmiany są przenoszone ( indeks ).
  • --mixed (domyślnie) : niezalecane + zmiany sceniczne , zmiany pozostają w działającym drzewie .
  • --hard: unommit + unstage + usuń zmiany, nic nie zostało.
Mo Ali
źródło
8
najlepsza odpowiedź, ponieważ w odpowiedzi użyto terminów technicznych, aby uzyskać kompletną odpowiedź, która jest również najbardziej zwięzła
Trevor Boyd Smith
1
Kiedy popełniłem plik (nieprzesłany) i mam nowo utworzony nieśledzony plik, to git reset --hard nic nie robi? Tylko wtedy, gdy wykonuję etap nieśledzonego pliku, usuwa go z mojego katalogu roboczego.
Michael,
1
@Nikhil Czy możesz wyjaśnić, gdzie ta odpowiedź jest błędna?
Ned Batchelder,
1
@NedBatchelder Żaden z tych argumentów nie jest poprawny: poniewaz nigdy nie zdarza się, gdy są używane te polecenia.
Nikhil
1
@Nikhil Być może chodzi ci o to, że oryginalne zatwierdzenie nadal istnieje, co jest prawdą. Ale gałąź została zmieniona, aby zatwierdzenie nie było już częścią gałęzi. Czy się z tym zgadzamy?
Ned Batchelder,
69

Należy pamiętać, że jest to uproszczone wyjaśnienie, które jest pierwszym krokiem do zrozumienia tej złożonej funkcjonalności.

Może być pomocny dla uczniów wizualnych, którzy chcą wizualizować, jak wygląda stan projektu po każdym z tych poleceń:


Dla tych, którzy używają Terminalu z włączonym kolorem (git config --global color.ui auto):

git reset --soft A a zobaczysz rzeczy B i C w kolorze zielonym (wyreżyserowane i gotowe do zatwierdzenia)

git reset --mixed A(lub git reset A), a zobaczysz rzeczy B i C na czerwono (nie zainscenizowane i gotowe do zainscenizowania (zielone), a następnie popełnione)

git reset --hard A i nigdzie nie zobaczysz zmian B i C (będzie tak, jakby nigdy nie istniały)


Lub dla tych, którzy korzystają z programu GUI, takiego jak „Tower” lub „SourceTree”

git reset --soft A i zobaczysz rzeczy B i C w obszarze „pliki przemieszczane” gotowe do zatwierdzenia

git reset --mixed A(lub git reset A), a zobaczysz rzeczy B i C w obszarze „pliki niestacjonarne” gotowe do przeniesienia na etapowe, a następnie zatwierdzone

git reset --hard A i nigdzie nie zobaczysz zmian B i C (będzie tak, jakby nigdy nie istniały)

timhc22
źródło
1
Jest to w najlepszym razie mylące: twoja odpowiedź brzmi tak, jakby git resettylko zmieniała wygląd git statuswyników.
jub0bs
3
Rozumiem twój punkt widzenia, ale nie zgadzam się, ponieważ jako wizualny uczeń, zobaczenie, jak mój projekt „wyglądał” po użyciu 3 poleceń, w końcu pomogło mi zrozumieć, co robili!
timhc22
Widziałem to raczej pomysł na „gówno dla manekinów”, aby pomóc ludziom zrozumieć, co się właściwie dzieje. Czy potrafisz wymyślić, jak można to poprawić, aby nie wprowadzać w błąd
timhc22
8
Nie, nie musimy zmieniać tej odpowiedzi. Zapewnia przydatny „ściągawka”. Pomyśl o tym: miękki = zielony, mieszany = czerwony, twardy = nic (oznacza zniknięcie)! Jak łatwo zapamiętać! Dla tych początkujących, którzy nawet nie rozumieją, co naprawdę oznaczają te kolory, wiedzą zbyt mało o git, i tak i tak będą brać ciężkie lekcje po drodze, a to NIE jest wina @unegma! BTW, właśnie głosowałem tę odpowiedź, aby przeciwdziałać temu poprzedniemu głosowaniu. Dobra robota, @unegma!
RayLuo
5
Służyło to jako świetne uzupełnienie, aby lepiej zrozumieć wewnętrzne funkcjonowanie, gdy czytam je gdzie indziej. Dziękuję Ci!
spex
24

Wszystkie inne odpowiedzi są świetne, ale uważam, że najlepiej je zrozumieć poprzez rozbicie plików na trzy kategorie: unstaged, staged, commit:

  • --hard powinno być łatwe do zrozumienia, przywraca wszystko
  • --mixed (domyślnie) :
    1. unstagedpliki: nie zmieniaj
    2. staged pliki: przejdź do unstaged
    3. commit pliki: przejdź do unstaged
  • --soft:
    1. unstagedpliki: nie zmieniaj
    2. stagedpliki: nie zmieniaj
    3. commit pliki: przejdź do staged

W podsumowaniu:

  • --softopcja przeniesie wszystko (oprócz unstagedplików) dostaging area
  • --mixed opcja przeniesie wszystko do unstaged area
Hansen W
źródło
22

Oto podstawowe wyjaśnienie dla użytkowników TortoiseGit:

git reset --softi --mixedzostaw swoje pliki nietknięte.

git reset --hardfaktycznie zmień swoje pliki, aby pasowały do ​​zatwierdzonego resetu.

W TortoiseGit, koncepcja indeksu jest bardzo ukryta przez GUI. Po zmodyfikowaniu pliku nie trzeba uruchamiać, git addaby dodać zmianę do obszaru / indeksu pomostowego. Jeśli chodzi o modyfikacje istniejących plików, które nie zmieniają nazw plików git reset --softi --mixedsą takie same! Różnicę zauważysz tylko wtedy, gdy dodasz nowe pliki lub zmienisz ich nazwy. W takim przypadku, jeśli uruchomisz git reset --mixed, będziesz musiał ponownie dodać swój plik (i) z listy plików bez wersji .

James Lawruk
źródło
Ta odpowiedź jest bardzo niejasna, jeśli chodzi o różnicę między miękkim a mieszanym. i nawet lekceważy to stwierdzając. Poniższa odpowiedź jest bardziej zrozumiała. stackoverflow.com/questions/2530060/…
barlop
2
Jako użytkownik Github Desktop, który również zachowuje się tak samo, ta odpowiedź wyjaśnia mi, dlaczego wciąż jestem zdezorientowany --mixedi --soft.
Chen Li Yong,
20

W takich przypadkach podoba mi się obraz, który, mam nadzieję, może to wyjaśnić:

git reset --[hard/mixed/soft] :

wprowadź opis zdjęcia tutaj

Każdy efekt ma inny zakres

  1. Hard => WorkingDir + Index + HEAD
  2. Mieszane => Indeks + GŁOWA
  3. Soft => tylko HEAD (indeks i katalog roboczy bez zmian).
Tomer Ben David
źródło
15

Trzy rodzaje żalu

Wiele istniejących odpowiedzi wydaje się nie odpowiadać na rzeczywiste pytanie. Chodzi o to, co robią polecenia, a nie o to, czego chcesz (użytkownik) - przypadek użycia . Ale o to pytał OP!

Przydałoby się opisanie w kategoriach tego, czego dokładnie żałujesz w momencie wydawania git resetpolecenia. Powiedzmy, że mamy to:

A - B - C - D <- HEAD

Oto niektóre możliwe żałowania i co z nimi zrobić:

1. Żałuję, że B, C i D nie są jednym zatwierdzeniem.

git reset --soft A. Mogę teraz natychmiast zatwierdzić i presto, wszystkie zmiany od A jednym zatwierdzeniem.

2. Żałuję, że B, C i D nie są dziesięcioma zobowiązaniami.

git reset --mixed A. Zatwierdzenia zniknęły, a indeks wrócił do A, ale obszar roboczy nadal wygląda tak jak po D. Więc teraz mogę dodawać i zatwierdzać w zupełnie innej grupie.

3. Żałuję, że B, C i D zdarzyły się na tej gałęzi ; Żałuję, że nie rozgałęziłem się po A i zdarzyły się one w tej drugiej gałęzi.

Utwórz nowy oddział otherbranch, a następnie git reset --hard A. Obecna gałąź kończy się teraz na A, z której otherbranchpochodzi.

(Oczywiście możesz również użyć twardego resetu, ponieważ chciałbyś, żeby B, C i D nigdy się nie zdarzyły).

matowy
źródło
5

Nie musisz zmuszać się do zapamiętywania różnic między nimi. Pomyśl o tym, jak faktycznie dokonałeś zatwierdzenia.

1. Dokonaj zmian.

2. dodaj.

3.gc -m „Zrobiłem coś”

Miękki, mieszany i twardy to sposób na rezygnację z operacji, które wykonałeś od 3 do 1.

Miękkie „udawanie”, by nigdy nie widzieć, że zrobiłeś „gc -m”.

Mieszane „udawane”, by nigdy nie widzieć, że „dodałeś git”.

Trudno „udawać”, że nigdy nie widziałeś, żebyś dokonał zmian w plikach.

qinmu2127
źródło
4

Przed przejściem do tych trzech opcji należy zrozumieć 3 rzeczy.

1) Historia / GŁOWA

2) Etap / indeks

3) Katalog roboczy

reset --soft: Historia zmieniona, HEAD zmieniony, Katalog roboczy nie został zmieniony.

reset --mixed: Historia zmieniona, HEAD zmieniony, Katalog roboczy zmieniony z danymi niestacjonarnymi.

reset --hard: Historia zmieniona, HEAD zmieniony, Katalog roboczy został zmieniony z utraconymi danymi.

Korzystanie z Git - soft jest zawsze bezpieczne. W złożonych wymaganiach należy użyć innej opcji.

Suresh Sharma
źródło
3

Istnieje wiele odpowiedzi z błędnym przekonaniem na temat git reset --soft. Chociaż istnieje określony warunek, w którym git reset --softzmieni się tylko HEAD(zaczynając od odłączonego stanu głowy), zwykle (i zgodnie z przeznaczeniem), przenosi odniesienie do gałęzi, które obecnie sprawdziłeś. Oczywiście nie może tego zrobić, jeśli nie masz wyewidencjonowanego oddziału (stąd szczególny warunek, w którym git reset --softsię tylko zmieni HEAD).

Uważam, że jest to najlepszy sposób myślenia git reset. Nie tylko się poruszasz HEAD( wszystko to robi ), ale także przenosisz ref gałąź , np master. Jest to podobne do tego, co dzieje się po uruchomieniu git commit(bieżąca gałąź porusza się wraz z HEAD), z tym wyjątkiem, że zamiast tworzyć (i przechodzić do) nowego zatwierdzenia, przechodzisz do wcześniejszego zatwierdzenia.

Chodzi o to reset, żeby zmienić gałąź na coś innego niż nowy zatwierdzenie, nie zmieniać HEAD. Możesz to zobaczyć w przykładzie dokumentacji:

Cofnij zatwierdzenie, czyniąc go gałęzią tematów

          $ git branch topic/wip     (1)
          $ git reset --hard HEAD~3  (2)
          $ git checkout topic/wip   (3)
  1. Podjęliście pewne zobowiązania, ale zdajcie sobie sprawę, że przedwcześnie byli w gałęzi „master”. Chcesz kontynuować polerowanie ich w gałęzi tematów, więc utwórz gałąź „topic / wip” z bieżącego HEAD.
  2. Przewiń gałąź master, aby pozbyć się tych trzech zmian.
  3. Przejdź do gałęzi „topic / wip” i kontynuuj pracę.

Jaki jest sens tej serii poleceń? Chcesz przenieść gałąź tutaj master, więc kiedy się masterwymeldowałeś, biegniesz git reset.

Najczęściej głosowana odpowiedź tutaj jest ogólnie dobra, ale pomyślałem, że dodam to, aby poprawić kilka odpowiedzi z błędnymi przekonaniami.

Zmień swój oddział

git reset --soft <ref>: Resetuje wskaźnik oddział dla aktualnie wyrejestrowany oddziału do zatwierdzenia w określonym odniesienia, <ref>. Pliki w katalogu roboczym i indeksie nie są zmieniane. Przyjęcie z tego etapu zabierze Cię z powrotem do miejsca, w którym byłeś przed git resetpoleceniem.

Zmień też swój indeks

git reset --mixed <ref>

lub równoważnie

git reset <ref>:

Robi to, co --softrobi i również resetuje wskaźnik do meczu commit w określonym odniesienia. Chociaż git reset --soft HEADnic nie robi (ponieważ mówi o przeniesieniu gałęzi wyrejestrowanej do gałęzi wyrejestrowanej), git reset --mixed HEADlub równoważnie git reset HEAD, jest popularnym i użytecznym poleceniem, ponieważ resetuje indeks do stanu ostatniego zatwierdzenia.

Zmień również katalog roboczy

git reset --hard <ref>: Robi to, co --mixedrobi i również nadpisuje katalog roboczy. Ta komenda jest podobna do tej git checkout <ref>, z tą różnicą, że (i to jest najważniejszy punkt reset) wszystkie formy git resetruchu, na które HEADwskazuje gałąź odniesienia .

Uwaga na temat „takie i takie polecenie porusza GŁOWĘ”:

Nie jest użyteczne stwierdzenie, że polecenie porusza HEAD. Każde polecenie, które zmienia się w historii zatwierdzeń, przenosi HEAD. Właśnie o to HEAD chodzi , wskaźnik do gdziekolwiek jesteś. HEADto ty , i tak się przeprowadzisz, kiedy to zrobisz.

De Novo
źródło
2
„przeniesienie gałęzi ref”: dobra uwaga. Musiałem zaktualizować stackoverflow.com/a/5203843/6309 .
VC
1

Krótka odpowiedź w jakim kontekście używane są 3 opcje:

Aby zachować bieżące zmiany w kodzie, ale przepisać historię zatwierdzeń:

  • soft: Możesz zatwierdzić wszystko naraz i utworzyć nowy zatwierdzenie z nowym opisem (jeśli używasz Torotise git lub dowolnego innego GUI, to jest to, którego możesz użyć, ponieważ nadal możesz zaznaczyć, które pliki chcesz w zatwierdzeniu i zrobić wiele zatwierdza w ten sposób różne pliki. W Sourcetree wszystkie pliki byłyby przemieszczane do zatwierdzenia.)
  • mixed: Będziesz musiał ponownie dodać poszczególne pliki do indeksu przed dokonaniem zatwierdzeń (w Sourcetree wszystkie zmienione pliki byłyby nieetapowane)

Aby faktycznie utracić również zmiany w kodzie:

  • hard: nie tylko przepisujesz historię, ale także tracisz wszystkie zmiany aż do momentu zresetowania
Nickpick
źródło
W tym przypadku nie jestem miękki i mieszany. Jeśli musisz popełnić, co zostało cofnięte? zatwierdzasz przywrócenie lub ponownie wprowadzasz zmiany (więc wracasz do stanu pierwotnego?)
John Little
Polecanie zmian. Nie będzie zatwierdzania wstecznego.
Nickpick
1

Podstawowe różnice między różnymi opcjami polecenia git reset są następujące.

  • --soft: resetuje HEAD tylko do wybranego zatwierdzenia. Działa zasadniczo tak samo jak git Checkout, ale nie tworzy odłączonego stanu głowy.
  • --mixed (opcja domyślna): resetuje HEAD do zatwierdzenia wybranego w historii i cofa zmiany w indeksie.
  • --hard: resetuje HEAD do zatwierdzenia wybranego w historii, cofa zmiany w indeksie i cofa zmiany w katalogu roboczym.
Vishwas Abhyankar
źródło
1

--soft: Mówi Gitowi, aby zresetował HEAD do innego zatwierdzenia, więc indeks i katalog roboczy nie zostaną w żaden sposób zmienione. Wszystkie pliki zmienione między oryginalną HEAD a zatwierdzeniem zostaną wyreżyserowane.

--mixed: Podobnie jak soft, zresetuje HEAD do innego zatwierdzenia. Zresetuje również indeks, aby go dopasować, podczas gdy katalog roboczy nie zostanie zmieniony. Wszystkie zmiany pozostaną w katalogu roboczym i pojawią się jako zmodyfikowane, ale nie etapowe.

--hard: Resetuje wszystko - resetuje HEAD z powrotem do innego zatwierdzenia, resetuje indeks, aby dopasować go, i resetuje katalog roboczy, aby go dopasować.

Główną różnicą pomiędzy --mixedi --softjest to, czy twój indeks jest również modyfikowany. Sprawdź więcej na ten temat tutaj .

Nesha Zoric
źródło
0

Odpowiedź mkarasek jest świetna, w prostych słowach możemy powiedzieć ...

  • git reset --soft: ustaw HEADplanowane zatwierdzenie, ale zachowaj zmiany od ostatnich zatwierdzeń
  • git reset --mixed: jest taki sam, git reset --softale jedyną różnicą jest to, że nie wprowadzasz zmian od ostatnich zatwierdzeń
  • git reset --hard: ustaw swoje HEADzatwierdzenie, które określisz i zresetuj wszystkie zmiany od ostatnich zatwierdzeń, w tym niezatwierdzone zmiany.
Vivek Maru
źródło