Rewriting history

Rewriting history

Intro

Tento kurz pokrývá různé metody přepisování a upravování historie Git. Git k záznamu změn používá několik různých metod. Probereme silné a slabé stránky jednotlivých metod a uvedeme příklady, jak s nimi pracovat. Tento kurz popisuje běžné důvody pro přepisování snapshotů, u nichž byl proveden commit, a ukazuje, jak se vyhnout nástrahám, které vás při tom čekají.

Hlavním úkolem Gitu je zajistit, abyste nikdy nepřišly o změnu, u níž byl proveden commit. Je však také navržen tak, abyste nad svým vývojovým workflow měli absolutní kontrolu. V rámci toho můžete přesně definovat, jak bude historie vašeho projektu vypadat. Vzniká tím však riziko, že některé commity ztratíte. Git poskytuje příkazy pro přepis historie s upozorněním, že jejich nesprávné použití může způsobit ztrátu obsahu.

Git má několik mechanismů ukládání historie a změn. Patří mezi ně příkazy commit --amend, git rebase a git reflog. Tyto příkazy poskytují mocné možnosti přizpůsobení workflow. Až dokončíte tento kurz, budete obeznámeni s příkazy, které vám umožní restruktualizovat vaše commity Git, a budete schopni vyhnout se nástrahám, které na běžné uživatele při přepisování hostorie čekají.

Změna posledního commitu: git commit --amend

Příkaz git commit --amend je pohodlný způsob, jak změnit nejnovější commit. Umožňuje vám kombinovat provedené změny s předchozím commitem, místo abyste museli vytvářet úplně nový commit. Lze jej také použít k jednoduché úpravě zprávy předchozího commitu bez změny jeho snapshotu. Možnost amend však nemění jen nejnovější commit. Zcela jej nahrazuje, což znamená, že commit, u něhož byl proveden amend, bude novou entitou s vlastním odkazem ref. Systému Git se bude jevit jako úplně nový commit, který jsme v diagramu níže označili hvězdičkou (*). Příkaz git commit --amend se používá v několika běžných scénářích. Příklady užití popíšeme v následujících částech.

Git commit amend

Změna zprávy nejnovějšího commitu Git

git commit --amend

Řekněme, že jste právě provedli commit, ale udělali jste chybu v jeho zprávě protokolu. Když tento příkaz spustíte ve chvíli, kdy nejsou připraveny žádné změny, můžete upravit zprávu předchozího commitu, aniž byste měnili jeho snapshot.

Předčasné odevzdání commitu není při každodenním vývoji nic neobvyklého. Snadno se stane, že zapomenete připravit soubor nebo špatně naformátujete zprávu commitu. Příznak --amend je pohodlný způsob, jak tyto malé změny opravit.

git commit --amend -m "an updated commit message"

Přidání možnosti -m vám umožní předat novou zprávu prostřednictvím příkazového řádku, aniž byste byli vyzváni k otevření editoru.

Změna souborů, u kterých byl proveden commit

V následujícím příkladu je ukázán scénář, který při vývoji v Gitu běžně nastává. Řekněme, že jsme upravili několik souborů, u kterých chceme provést commit v podobě jediného snapshotu, ale zapomněli jsme jeden z nich přidat. Tuto chybu opravíme jednoduchým připravením zapomenutého souboru a provedením commitu s příznakem --amend:

# Edit hello.py and main.py git add hello.py git commit
# Realize you forgot to add the changes from main.py git add main.py
git commit --amend --no-edit

Příznak --no-edit umožňuje přidat amend ke commitu, aniž byste měnili jeho zprávu. Výsledný commit nahradí ten nekompletní a bude to vypadat tak, jako bychom u změn souborů hello.py a main.py provedli commit v jediném snapshotu.

Neprovádějte amend veřejných commitů

Commity, u kterých provedete amend, jsou vlastně zcela novými commity, což znamená, že předchozí commit už na aktuální větvi nebude. Má to stejné následky jako resetování veřejného snapshotu. Vyhněte se přidávání amendu u commitu, na kterém jiný vývojáři založili svou práci. Vznikla by tím matoucí situace, která by se složitě odstraňovala.

Rekapitulace

Příkaz git commit --amend vám umožní vzít poslední commit a přidat k němu nově připravené změny. Změny, které chcete použít u příkazu --amend commitu, můžete přidávat nebo odebírat v oblasti stagingu Git. Pokud nejsou připraveny žádné změny, zobrazí se po spuštění příkazu --amend výzva k úpravě zprávy posledního commitu. Při úpravě commitů, které sdílíte s ostatními členy týmu, pomocí příkazu --amend buďte opatrní. Když provedete amend u commitu, který sdílíte s jiným uživatelem, může dojít ke konfliktu, který budete muset zdlouhavě řešit.

Změna starších nebo více commitů

Chcete-li upravit starší commit nebo změnit více commitů, můžete pomocí příkazu git rebase zkombinovat posloupnost commitů do jednoho základního. Ve standardním režimu vám příkaz git rebase umožní doslova přepsat historii – commity ve vaší aktuální pracovní větvi se automaticky použijí u předané hlavy větve. Vzhledem k tomu, že nové commity nahradí ty staré, je důležité nepoužívat příkaz git rebase u commitů, které byly učiněny veřejnými. Pokud se tak stane, bude to vypadat, jako by historie vašeho projektu zmizela.

U těchto nebo podobných instancí, kde je důležité zachovat čistou historii projektu, můžete přidat možnost -i, a spustit tak příkaz git rebase v režimu rebase interactive. To vám poskytne možnost upravit během procesu jednotlivé commity místo toho, abyste je přesunuli všechny najednou. Další informace o interaktivním rebasingu a dalších příkazech založených na rebase naleznete na stránce git rebase.

Změna souborů, u kterých byl proveden commit

Při úpravě nebo použití příkazu e během rebasingu se pozastaví přehrání rebase daného commitu a vy budete moci provést dodatečné změny pomocí příkazu git commit --amend. Git přeruší přehrání a zobrazí zprávu:

Stopped at 5d025d1... formatting
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue

Více zpráv

Všechny běžné commity Git mají zprávu, která vysvětluje, co se v commitu stalo. Tyto zprávy slouží jako cenné informace ohledně historie projektu. Během rebasingu můžete u commitů spustit několik příkazů, a upravit tak jejich zprávy.

  • Příkaz reward nebo 'r' zastaví přehrávání rebasingu a umožní vám přepsat zprávu jednoho commitu.
  • Příkaz squash nebo 's' během přehrávání rebasingu pozastaví všechny commity se značkou s a vyzve vás k úpravě oddělených zpráv commitů do jedné kombinované zprávy. Další informace naleznete v části o příkazu squash commit níže.
  • Příkaz fixup nebo 'f' má podobný kombinační efekt jako squash. Na rozdíl od příkazu squash příkaz fixup nepřeruší přehrávání rebasingu otevřením editoru pro zkombinování zpráv commitů. Zprávy commitů označených jako 'f' budou zahozeny a nahrazeny zprávou předchozího commitu.

Vyčištění historie commitů pomocí příkazu squash

Příkaz s (squash) odhaluje, jak je rebasing užitečný. Příkaz squash vám umožňuje určit, u jakých commitů chcete provést merge do předchozích commitů. Právě to umožňuje „čistou historii“. Během přehrávání rebasingu Git spustí určený příkaz rebase pro každý commit. U commitů se squash Git otevře nakonfigurovaný textový editor a vyzve vás ke zkombinování určených zpráv commitů. Celý proces je znázorněn na následujícím obrázku:

Kurz Gitu: příklad příkazu git rebase -i

Všimněte si, že commity upravené pomocí příkazu rebase mají jiné ID než jakýkoli z původních commitů. Pokud byly předchozí commity přepsány, mají commity označené vybráním nové ID.

Moderní řešení hostingu Git, jako je Bitbucket, nyní nabízí funkce „automatického squashe“ po provedení merge. Tyto funkce za vás během využití uživatelského rozhraní hostovaného řešení automaticky provedou příkazy rebase a squash u commitů větve. Další informace naleznete v části Provádění squash u commitů při provádění merge větve Gitu s Bitbucket.

Rekapitulace

Příkaz git rebase vám umožní upravit historii. Díky interaktivnímu rebasingu při tom po sobě nezanecháte „nepořádek“. Tím získáte svobodu dělat a opravovat chyby a vylepšovat svou práci, aniž byste museli obětovat čistou a lineární historii projektu.

Záchranná síť: git reflog

Referenční protokoly (reflogy) jsou mechanismus, který Git používá k zaznamenávání aktualizací provedených u špiček větví, a daších referencí commitů. Reflog vám umožňuje přejít zpět i na commity, na které neodkazuje žádná větev ani štítek. Když přepíšete historii, uchovají se v reflogu informace o starém stavu větví a reflog vám umožní se do tohoto stavu v případě potřeby vrátit. Pokaždé, když se z jakéhokoli důvodu aktualizuje špička větve (kvůli přepnutí větví, provedení pull pro nové změny, přepsání historie nebo obyčejnému přidání nových commitů), přidá se do reflogu nový záznam. V této části se podrobně zaměříme na příkaz git reflog a prozkoumáme běžné způsoby jeho použití.

Usage

git reflog

Tento příkaz zobrazí reflog místního repozitáře.

git reflog --relative-date

Tento příkaz zobrazí reflog podle data (například před 2 týdny).

Example

To understand git reflog, let's run through an example.

0a2e358 HEAD@{0}: reset: moving to HEAD~2 0254ea7 HEAD@{1}: checkout: moving from 2.2 to master c10f740 HEAD@{2}: checkout: moving from master to 2.2

The reflog above shows a checkout from master to the 2.2 branch and back. From there, there's a hard reset to an older commit. The latest activity is represented at the top labeled HEAD@{0}.

Pokud se ukáže, že jste se omylem přesunuli zpět, naleznete v reflogu master commitu, který ukazuje na hodnotu (0254ea7), která platila předtím, než jste omylem zahodili dva commity.

git reset --hard 0254ea7

Pomocí příkazu git reset nyní můžete změnit master zpět na commit, který platil předtím. Díky tomu získáte záchrannou síť pro případ, že omylem změníte historii.

Je důležité si uvědomit, že reflog poskytuje záchrannou síť pouze tehdy, pokud byl u změn proveden commit v místním repozitáři, a že sleduje pouze změny špičky větve repozitáře. Záznamy v reflogu mají navíc datum vypršení platnosti. Platnost záznamů v reflogu vyprší ve výchozím nastavení po 90 dnech.

Další informace naleznete na stránce git reflog

Souhrn

V článku jsme probrali několik metod změny historie Git a vracení změn Git. Podrobně jsme prošli procesem příkazu git rebase. Některé klíčové poznatky:

  • V gitu lze přepisovat historii mnoha způsoby.
  • Pomocí příkazu git commit --amend můžete změnit poslední zprávu protokolu.
  • Pomocí příkazu git commit --amend můžete upravit poslední commit.
  • Pomocí příkazu git rebase můžete zkombinovat commity a upravit historii větve.
  • Příkaz git rebase -i poskytuje podrobnější kontrolu nad úpravami historie než standardní příkaz git rebase.

Další informace o jednotlivých příkazech jsme popsali na samostatných stránkách: