Git Reset vs Revert vs Rebase

V tomto článku se dozvíte o různých způsobech, jak si pohrát s commity v Gitu.

Jako vývojář byste se několikrát setkali s takovými situacemi, kdy byste se chtěli vrátit k jednomu ze svých předchozích odevzdání, ale nebyli byste si jisti, jak to udělat. A i když znáte příkazy Git jako reset, revert, rebase, nejste si vědomi rozdílů mezi nimi. Pojďme tedy začít a porozumět tomu, co je git reset, revert a rebase.

Git Reset

Git reset je složitý příkaz a používá se k vrácení změn.

Reset git můžete považovat za funkci vrácení zpět. Pomocí git reset můžete přeskakovat mezi různými commity. Existují tři režimy spuštění příkazu git reset: –soft, –mixed a –hard. Ve výchozím nastavení používá příkaz git reset smíšený režim. V pracovním postupu resetování git se do obrazu dostávají tři vnitřní mechanismy správy git: HEAD, pracovní oblast (index) a pracovní adresář.

Pracovní adresář je místo, kde právě pracujete, je to místo, kde jsou vaše soubory. Pomocí příkazu git status můžete vidět, jaké všechny soubory/složky se nacházejí v pracovním adresáři.

Staging Area (Index) je místo, kde git sleduje a ukládá všechny změny v souborech. Uložené změny se projeví v adresáři .git. K přidání souboru do pracovní oblasti použijte git add „filename“. A stejně jako předtím, když spustíte stav git, uvidíte, které soubory jsou přítomny v pracovní oblasti.

Aktuální větev v Gitu se označuje jako HEAD. Ukazuje na poslední potvrzení, ke kterému došlo v aktuální větvi pokladny. Je považováno za ukazatel pro jakýkoli odkaz. Jakmile provedete pokladnu v jiné pobočce, HEAD se také přesune do nové pobočky.

Dovolte mi vysvětlit, jak git reset funguje v tvrdém, měkkém a smíšeném režimu. Tvrdý režim se používá k přechodu do bodovaného odevzdání, pracovní adresář se zaplní soubory tohoto odevzdání a obnoví se pracovní oblast. Při měkkém resetu se změní pouze ukazatel na zadané potvrzení. Soubory všech odevzdání zůstanou před resetem v pracovním adresáři a pracovní oblasti. Ve smíšeném režimu (výchozí) se ukazatel i pracovní oblast resetují.

Tvrdý reset Git

Účelem hard resetu git je přesunout HEAD do zadaného potvrzení. Odebere všechna potvrzení, která se stala po zadaném potvrzení. Tento příkaz změní historii odevzdání a ukáže na zadaný odevzdání.

V tomto příkladu přidám tři nové soubory, odevzdám je a poté provedu tvrdý reset.

Jak můžete vidět z příkazu níže, právě teď není co spáchat.

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.

(use "git push" to publish your local commits)

nothing to commit, working tree clean

Nyní vytvořím 3 soubory a přidám k nim nějaký obsah.

$ vi file1.txt
$ vi file2.txt
$ vi file3.txt

Přidejte tyto soubory do stávajícího úložiště.

$ git add file*

Když znovu spustíte příkaz status, bude odrážet nové soubory, které jsem právě vytvořil.

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.

(use "git push" to publish your local commits)

Changes to be committed:

(use "git restore --staged <file>..." to unstage)

new file:
file1.txt

new file:
file2.txt

new file:
file3.txt

Před potvrzením vám dovolte, abych vám ukázal, že v současné době mám v Gitu protokol 3 commitů.

$ git log --oneline
0db602e (HEAD -> master) one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Nyní se zavazuji k úložišti.

$ git commit -m 'added 3 files'
[master d69950b] added 3 files
3 files changed, 3 insertions(+)
create mode 100644 file1.txt
create mode 100644 file2.txt
create mode 100644 file3.txt

Pokud udělám ls-files, uvidíte, že byly přidány nové soubory.

$ git ls-files
demo
dummyfile
newfile
file1.txt
file2.txt
file3.txt

Když spustím příkaz log v git, mám 4 commity a HEAD ukazuje na nejnovější commit.

$ git log --oneline
d69950b (HEAD -> master) added 3 files
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Pokud ručně odstraním soubor1.txt a udělám stav git, zobrazí se zpráva, že změny nejsou připraveny pro odevzdání.

$ git status
On branch master
Your branch is ahead of 'origin/master' by 3 commits.

(use "git push" to publish your local commits)

Changes not staged for commit:

(use "git add/rm <file>..." to update what will be committed)

(use "git restore <file>..." to discard changes in working directory)

deleted:
file1.txt

no changes added to commit (use "git add" and/or "git commit -a")

Nyní spustím příkaz pro tvrdý reset.

$ git reset --hard
HEAD is now at d69950b added 3 files

Pokud znovu zkontroluji stav, zjistím, že není co odevzdávat, a soubor, který jsem smazal, se vrátil do úložiště. K rollbacku došlo, protože po smazání souboru jsem neprovedl potvrzení, takže po tvrdém resetu se vrátil do předchozího stavu.

$ git status
On branch master
Your branch is ahead of 'origin/master' by 3 commits.

(use "git push" to publish your local commits)

nothing to commit, working tree clean

Když zkontroluji protokol git, bude to vypadat takto.

$ git log
commit d69950b7ea406a97499e07f9b28082db9db0b387 (HEAD -> master)
Author: mrgeek <[email protected]>
Date:
Mon May 17 19:53:31 2020 +0530

added 3 files

commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14
Author: mrgeek <[email protected]>
Date:
Mon May 17 01:04:13 2020 +0530

one more commit

commit 59c86c96a82589bad5ecba7668ad38aa684ab323
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:54:53 2020 +0530

new commit

commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD)
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:16:33 2020 +0530

test

Účelem tvrdého resetu je ukázat na zadaný odevzdání a aktualizovat pracovní adresář a pracovní oblast. Dovolte mi ukázat vám ještě jeden příklad. V současné době vypadá vizualizace mých commitů takto:

  Jak zkontrolovat stav reklamace FedEx

Zde spustím příkaz s HEAD^, což znamená, že se chci vrátit k předchozímu odevzdání (o jedno potvrzení zpět).

$ git reset --hard HEAD^
HEAD is now at 0db602e one more commit

Můžete vidět, že ukazatel hlavy se nyní změnil na 0db602e z d69950b.

$ git log --oneline
0db602e (HEAD -> master) one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Pokud zkontrolujete protokol, odevzdání d69950b je pryč a hlava nyní ukazuje na 0db602e SHA.

$ git log
commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 (HEAD -> master)
Author: mrgeek <[email protected]>
Date:
Mon May 17 01:04:13 2020 +0530

one more commit

commit 59c86c96a82589bad5ecba7668ad38aa684ab323
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:54:53 2020 +0530

new commit

commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD)
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:16:33 2020 +0530

Test

Pokud spustíte ls-files, uvidíte, že soubor1.txt, soubor2.txt a soubory3.txt již nejsou v úložišti, protože tento odevzdání a jeho soubor byly po tvrdém resetu odstraněny.

$ git ls-files
demo
dummyfile
newfile

Git Soft Reset

Podobně vám nyní ukážu příklad měkkého resetu. Zvažte, znovu jsem přidal 3 soubory, jak je uvedeno výše, a svěřil jsem je. Zobrazí se protokol git, jak je znázorněno níže. Můžete vidět ‚soft reset‘ je můj nejnovější commit a HEAD na to také poukazuje.

$ git log --oneline
aa40085 (HEAD -> master) soft reset
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Podrobnosti o odevzdání v protokolu lze zobrazit pomocí příkazu níže.

$ git log
commit aa400858aab3927e79116941c715749780a59fc9 (HEAD -> master)
Author: mrgeek <[email protected]>
Date:
Mon May 17 21:01:36 2020 +0530

soft reset

commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14
Author: mrgeek <[email protected]>
Date:
Mon May 17 01:04:13 2020 +0530

one more commit

commit 59c86c96a82589bad5ecba7668ad38aa684ab323
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:54:53 2020 +0530

new commit

commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD)
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:16:33 2020 +0530

test

Nyní pomocí měkkého resetu chci přejít na jeden ze starších commitů s SHA 0db602e085a4d59cfa9393abac41ff5fd7afcb14

  Jak přidat podmíněné formátování do buněk v Tabulkách Google

K tomu spustím níže uvedený příkaz. Musíte předat více než 6 počátečních znaků SHA, úplný SHA není vyžadován.

$ git reset --soft 0db602e085a4

Nyní, když spustím protokol git, vidím, že HEAD byl resetován na zadaný odevzdání.

$ git log
commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 (HEAD -> master)
Author: mrgeek <[email protected]>
Date:
Mon May 17 01:04:13 2020 +0530

one more commit

commit 59c86c96a82589bad5ecba7668ad38aa684ab323
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:54:53 2020 +0530

new commit

commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD)
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:16:33 2020 +0530

test

Rozdíl je ale v tom, že soubory odevzdání (aa400858aab3927e79116941c715749780a59fc9), kam jsem přidal 3 soubory, jsou stále v mém pracovním adresáři. Nebyly smazány. To je důvod, proč byste měli používat měkký reset spíše než tvrdý reset. V měkkém režimu nehrozí ztráta souborů.

$ git ls-files
demo
dummyfile
file1.txt
file2.txt
file3.txt
newfile

Git Revert

V Gitu se příkaz revert používá k provedení operace vrácení, tj. k vrácení některých změn. Je to podobné příkazu reset, ale jediný rozdíl je v tom, že provedete nové potvrzení, abyste se vrátili ke konkrétnímu potvrzení. Stručně řečeno, je fér říci, že příkaz git revert je potvrzení.

Příkaz Git revert při provádění operace vrácení nevymaže žádná data.

Řekněme, že přidávám 3 soubory a provádím operaci potvrzení git pro příklad návratu.

$ git commit -m 'add 3 files again'
[master 812335d] add 3 files again
3 files changed, 3 insertions(+)
create mode 100644 file1.txt
create mode 100644 file2.txt
create mode 100644 file3.txt

Protokol zobrazí nové potvrzení.

$ git log --oneline
812335d (HEAD -> master) add 3 files again
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Nyní bych se rád vrátil k jednomu z mých minulých commitů, řekněme – „59c86c9 new commit“. Spustil bych příkaz níže.

$ git revert 59c86c9

Tím se otevře soubor, najdete podrobnosti o odevzdání, ke kterému se pokoušíte vrátit, a zde můžete pojmenovat svůj nový odevzdání a poté soubor uložit a zavřít.

Revert "new commit"

This reverts commit 59c86c96a82589bad5ecba7668ad38aa684ab323.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
# Your branch is ahead of 'origin/master' by 4 commits.
# (use "git push" to publish your local commits)
#
# Changes to be committed:
# modified: dummyfile

Po uložení a zavření souboru získáte výstup.

$ git revert 59c86c9
[master af72b7a] Revert "new commit"
1 file changed, 1 insertion(+), 1 deletion(-)

Nyní, aby bylo možné provést potřebné změny, na rozdíl od resetu provedl revert ještě jeden nový odevzdání. Pokud znovu zkontrolujete protokol, najdete nové potvrzení kvůli operaci vrácení.

$ git log --oneline
af72b7a (HEAD -> master) Revert "new commit"
812335d add 3 files again
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Protokol Git bude obsahovat celou historii odevzdání. Pokud chcete odstranit odevzdání z historie, pak návrat není dobrá volba, ale pokud chcete zachovat změny odevzdání v historii, pak je vhodný příkaz vrátit místo resetu.

Git Rebase

V Gitu je rebase způsob přesouvání nebo kombinování odevzdání jedné větve přes větev jinou. Jako vývojář bych nevytvářel své funkce na hlavní větvi ve scénáři reálného světa. Pracoval bych na své vlastní větvi (‚feature branch‘) a až budu mít ve své feature větvi několik commitů s přidanou funkcí, chtěl bych ji přesunout do hlavní větve.

Rebase může být někdy trochu matoucí k pochopení, protože je velmi podobná sloučení. Cílem sloučení a rebasingu obou je převzít odevzdání z mé větve funkcí a umístit je do hlavní větve nebo jakékoli jiné větve. Zvažte, mám graf, který vypadá takto:

  USB 4 přinese rychlosti Thunderboltu za méně peněz

Předpokládejme, že pracujete v týmu s dalšími vývojáři. V takovém případě si dokážete představit, že by to mohlo být opravdu složité, pokud máte spoustu dalších vývojářů pracujících na různých větvích funkcí a tito slučovali několik změn. Sledování se stává matoucím.

Takže tady pomůže rebase. Tentokrát místo začleňování git udělám rebase, kde chci vzít své dvě revize funkce a přesunout je do hlavní větve. Rebase převezme všechny mé odevzdání z větve feature a přesune je na vrchní odevzdání hlavní větve. Takže v zákulisí git duplikuje potvrzení větve feature na hlavní větev.

Tento přístup vám poskytne čistý přímý graf se všemi odevzdáními v řadě.

Usnadňuje sledování toho, kam se odevzdaly. Dokážete si představit, že pokud jste v týmu s mnoha vývojáři, všechny commity jsou stále v řadě. Takže je opravdu snadné sledovat, i když na stejném projektu pracuje mnoho lidí současně.

Dovolte mi, abych vám to ukázal prakticky.

Takto aktuálně vypadá moje hlavní větev. Má 4 commity.

$ git log --oneline
812335d (HEAD -> master) add 3 files again
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Spustím níže uvedený příkaz pro vytvoření a přepnutí na novou větev s názvem feature a tato větev bude vytvořena od 2. odevzdání, tj. 59c86c9

(master)
$ git checkout -b feature 59c86c9
Switched to a new branch 'feature'

Pokud zkontrolujete protokol ve větvi feature, má pouze 2 commity pocházející z hlavní (hlavní řady).

(feature)
$ git log --oneline
59c86c9 (HEAD -> feature) new commit
e2f44fc (origin/master, origin/HEAD) test

Vytvořím prvek 1 a odevzdám jej do větve prvku.

(feature)
$ vi feature1.txt

(feature)
$ git add .
The file will have its original line endings in your working directory

(feature)
$ git commit -m 'feature 1'
[feature c639e1b] feature 1
1 file changed, 1 insertion(+)
create mode 100644 feature1.txt

Vytvořím ještě jeden prvek, tj. prvek 2, ve větvi feature a odevzdám ho.

(feature)
$ vi feature2.txt

(feature)
$ git add .
The file will have its original line endings in your working directory

(feature)
$ git commit -m 'feature 2'
[feature 0f4db49] feature 2
1 file changed, 1 insertion(+)
create mode 100644 feature2.txt

Nyní, když zkontrolujete protokol větve feature, má dva nové odevzdání, které jsem provedl výše.

(feature)
$ git log --oneline
0f4db49 (HEAD -> feature) feature 2
c639e1b feature 1
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Nyní chci přidat tyto dvě nové funkce do hlavní větve. K tomu použiji příkaz rebase. Z větve feature budu rebase proti větvi master. Co to udělá, je, že to znovu ukotví mou větev funkcí proti nejnovějším změnám.

(feature)
$ git rebase master
Successfully rebased and updated refs/heads/feature.

Nyní jdu do toho a zkontroluji hlavní pobočku.

(feature)
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 3 commits.

(use "git push" to publish your local commits)

A nakonec předělejte hlavní větev proti mé hlavní větvi. Tím se převezmou tyto dva nové odevzdání na mé hlavní větvi a přehrají se na mé hlavní větvi.

(master)
$ git rebase feature
Successfully rebased and updated refs/heads/master.

Nyní, když zkontroluji protokol na hlavní větvi, vidím, že dvě revize mé větve features byly úspěšně přidány do mé hlavní větve.

(master)
$ git log --oneline
766c996 (HEAD -> master, feature) feature 2
c036a11 feature 1
812335d add 3 files again
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

To bylo vše o resetu, vrácení a obnovení příkazů v Gitu.

Závěr

To bylo vše o resetu, vrácení a obnovení příkazů v Gitu. Doufám, že vám tento návod krok za krokem pomohl. Nyní víte, jak si pohrát se svými commity podle potřeby pomocí příkazů uvedených v článku.