-
1. Úvod
- 1.1 Správa verzí
- 1.2 Stručná historie systému Git
- 1.3 Základy systému Git
- 1.4 Příkazový řádek
- 1.5 Instalace systému Git
- 1.6 První nastavení systému Git
- 1.7 Získání nápovědy
- 1.8 Shrnutí
-
2. Základy práce se systémem Git
-
3. Větve v systému Git
- 3.1 Větve v kostce
- 3.2 Základy větvení a slučování
- 3.3 Správa větví
- 3.4 Postupy při práci s větvemi
- 3.5 Vzdálené větve
- 3.6 Přeskládání
- 3.7 Shrnutí
-
4. Git na serveru
- 4.1 Protokoly
- 4.2 Zprovoznění Gitu na serveru
- 4.3 Generování veřejného klíče SSH
- 4.4 Nastavení serveru
- 4.5 Démon Git
- 4.6 Chytrý HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Možnosti hostování u třetí strany
- 4.10 Shrnutí
-
5. Distribuovaný Git
- 5.1 Distribuované pracovní postupy
- 5.2 Přispívání do projektu
- 5.3 Správa projektu
- 5.4 Shrnutí
-
6. GitHub
-
7. Git Tools
- 7.1 Revision Selection
- 7.2 Interactive Staging
- 7.3 Stashing and Cleaning
- 7.4 Signing Your Work
- 7.5 Searching
- 7.6 Rewriting History
- 7.7 Reset Demystified
- 7.8 Advanced Merging
- 7.9 Rerere
- 7.10 Ladění v systému Git
- 7.11 Submodules
- 7.12 Bundling
- 7.13 Replace
- 7.14 Credential Storage
- 7.15 Shrnutí
-
8. Customizing Git
- 8.1 Git Configuration
- 8.2 Atributy Git
- 8.3 Git Hooks
- 8.4 An Example Git-Enforced Policy
- 8.5 Shrnutí
-
9. Git a ostatní systémy
- 9.1 Git as a Client
- 9.2 Migrating to Git
- 9.3 Shrnutí
-
10. Git Internals
- 10.1 Plumbing and Porcelain
- 10.2 Git Objects
- 10.3 Git References
- 10.4 Balíčkové soubory
- 10.5 The Refspec
- 10.6 Přenosové protokoly
- 10.7 Správa a obnova dat
- 10.8 Environment Variables
- 10.9 Shrnutí
-
A1. Appendix A: Git in Other Environments
- A1.1 Graphical Interfaces
- A1.2 Git in Visual Studio
- A1.3 Git in Eclipse
- A1.4 Git in Bash
- A1.5 Git in Zsh
- A1.6 Git in Powershell
- A1.7 Shrnutí
-
A2. Appendix B: Embedding Git in your Applications
- A2.1 Command-line Git
- A2.2 Libgit2
- A2.3 JGit
-
A3. Appendix C: Git Commands
- A3.1 Setup and Config
- A3.2 Getting and Creating Projects
- A3.3 Basic Snapshotting
- A3.4 Branching and Merging
- A3.5 Sharing and Updating Projects
- A3.6 Inspection and Comparison
- A3.7 Debugging
- A3.8 Patching
- A3.9 Email
- A3.10 External Systems
- A3.11 Administration
- A3.12 Plumbing Commands
3.6 Větve v systému Git - Přeskládání
Přeskládání
V Gitu existují dva základní způsoby, jak integrovat změny z jedné větve do druhé: merge
(sloučení, začlenění) a rebase
(přeskládání).
V této podkapitole se dozvíte, co to přeskládání je, jak se provádí, proč je to docela úžasný nástroj a v jakých případech ho raději nepoužívat.
Základní přeskládání
Pokud se vrátíte k dřívějšímu příkladu v Základní slučování, vidíte, že jste se při práci odchýlili a vznikly objekty revize ve dvou různých větvích.
![Jednoduché rozvětvení historie.](png/basic-rebase-1.png)
Jak jsme si již ukázali, nejjednodušší způsob spojení větví spočívá v použití příkazu merge
.
Ten provede třícestné sloučení mezi dvěma posledními snímky (C3 a C4) a jejich nejmladším společným předkem (C2), přičemž vytvoří nový snímek (a novou revizi).
![Spojení rozvětvené historie sloučením.](png/basic-rebase-2.png)
Existuje však ještě jiný způsob. Můžete vzít záplatu se změnou, která vznikla v revizi C4
, a aplikovat ji na revizi C3
.
V Gitu se to nazývá přeskládáním (rebasing).
Příkazem rebase
vezmete všechny změny, které byly zapsány na jedné větvi, a necháte je přehrát na jinou větev.
U tohoto příkladu byste provedli následující:
$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command
Funguje to tak, že se přejde na společného předka obou větví (větve, na níž se nacházíte, a větve, na kterou přeskládáváte), zjistí se rozdíly pro každý objekt revize ve větvi, na které se nacházíte, zjištěné rozdíly se uloží do dočasných souborů, aktuální větev se nastaví na stejný bod jako větev, na kterou přeskládáváte, a nakonec postupně aplikují všechny změny.
![Přeskládání změn vzniklých v `C4` na `C3`.](png/basic-rebase-3.png)
C4
na C3
V tomto okamžiku můžete přejít zpět na větev master
a provést sloučení typu „rychle vpřed“.
$ git checkout master
$ git merge experiment
![Posunutí větve `master` rychle vpřed.](png/basic-rebase-4.png)
master
rychle vpředSnímek, na který odkazuje C4'
, je v tomto okamžiku přesně stejný, jako ten, na který u příkladu využívajícího slučování odkazoval C5
.
Ve výsledcích integrace není žádný rozdíl, výsledkem přeskládání je však čistší historie.
Pokud si prohlížíte log přeskládané větve, vypadá jako lineární historie. Zdá se, jako kdyby veškeré práce probíhaly postupně, i když původně proběhly paralelně.
Tuto metodu budete často používat v situaci, kdy chcete mít jistotu, že byly na vzdálenou větev vaše změny aplikovány čistě — například u projektu, do kterého chcete přispět, ale který nespravujete.
V takovém případě budete pracovat ve své větvi, a až budete mít připraveny záplaty k odeslání do hlavního projektu, přeskládáte svou práci na větev origin/master
.
Správce v tomto případě nemusí provádět žádnou integraci, provede pouze posun „rychle vpřed“ nebo čistou aplikaci.
Všimněte si, že snímek, na který ukazuje poslední objekt revize — ať už se jedná o poslední revizi vzniklou přeskládáním, nebo poslední bod sloučení po provedení merge
--, je shodný. Liší se pouze historie.
Přeskládání provede změny z jedné řady prací na jinou, a to v pořadí, v jakém byly provedeny. Naproti tomu sloučení vezme koncové body větví a sloučí je dohromady.
Zajímavější možnosti přeskládání
Posloupnost změn můžete aplikovat i na něco jiného než na cílovou větev pro přeskládání.
Vezměme si například historii na obrázku Historie s tématickou větví vytvořenou z jiné tématické větve.
Vytvořili jste novou tématickou větev (server
), abyste v ní do projektu přidali novou funkčnost na straně serveru, a zapsali jste revizi.
Poté jste se odvětvili, abyste učinili změny na straně klienta (client
), a provedli jste několik zápisů.
Nakonec jste se vrátili na větev server
a zapsali dalších pár objektů revize.
![Historie s tématickou větví vytvořenou z jiné tématické větve.](png/interesting-rebase-1.png)
Předpokládejme, že nyní chcete změny straně klienta začlenit do své hlavní linie k vydání, ale prozatím chcete počkat se změnami na straně serveru, dokud nebudou víc otestovány.
Můžete vzít změny na větvi client
, které nejsou na větvi server
(C8
a C9
) a aplikovat je na větev master
příkazem git rebase
s volbou --onto
:
$ git rebase --onto master server client
V podstatě tím říkáte: „Přepni se (checkout) na větev client
, zjisti záplaty od společného předka větví client
a server
a znovu je aplikuj na větev master
.“ Je to možná trochu složitější, ale výsledek stojí za to.
![Přeskládání tématické větve vzniklé odvětvením z jiné tématické větve.](png/interesting-rebase-2.png)
Teď můžete větev master
posunout „rychle vpřed“ (viz obrázek):
$ git checkout master
$ git merge client
![Přidání změn z větve `client` posunutím větve `master` „rychle vpřed“.](png/interesting-rebase-3.png)
client
posunutím větve master
„rychle vpřed“Řekněme, že se později rozhodnete vtáhnout i větev server
.
Větev server
můžete přeskládat na větev master
— aniž byste se na ni nejdříve museli nejdříve přepnout — tak, že provedete git rebase [základna] [tématická větev]
— provede se tím přepnutí na tématickou větev (checkout, v tomto případě na větev server
) a přeskládá její změny na základnu (angl. base branch, v tomto případě master
):
$ git rebase master server
Příkaz přehraje práci z větve server
k práci ve větvi master
— viz obrázek.
![Přeskládání větve `server` na vrchol větve `master`.](png/interesting-rebase-4.png)
server
na vrchol větve master
Poté můžete bázovou větev (master
) přesunout „rychle vpřed“:
$ git checkout master
$ git merge server
Větve client
a server
můžete smazat, protože všechna práce z nich je integrována a tyto větve už nebudete potřebovat. Historie celého procesu bude vypadat jako na obrázku Historie poslední revize:
$ git branch -d client
$ git branch -d server
![Historie poslední revize.](png/interesting-rebase-5.png)
Rizika spojená s přeskládáním
Ale slastné přeskládání má i stinné stránky, které se dají shrnout do jednoho řádku:
Neprovádějte přeskládání pro revize, které existují mimo váš repozitář.
Pokud se budete touto zásadou řídit, budete v pohodě. Pokud ne, lidi vás budou nenávidět, přátelé a rodina vámi budou opovrhovat.
Pokud provedete přeskládání, zahazujete existující objekty revize (commits) a vytváříte nové, které jsou podobné, ale přesto jiné.
Pokud objekty revize odešlete (push) a ostatní si je stáhnou (pull) a založí na nich svou práci a vy potom tyto revize přepíšete příkazem git rebase
a znovu je odešlete, vaši spolupracovníci budou muset znovu znovu začleňovat svou práci (merge) a ve věcech zavládne chaos v okamžiku, kdy se pokusíte vtáhnout jejich práci zpět do své.
Podívejme se na příklad toho, jaké problémy může přeskládání již zveřejněných dat způsobit. Předpokládejme, že jste naklonovali z centrálního serveru a provedli jste několik změn. Historie revizí bude vypadat nějak takto:
![Naklonování repozitáře a provedení změn.](png/perils-of-rebasing-1.png)
Někdo jiný teď provede další úpravy, jejichž součástí bude i sloučení (merge), a odešle svou práci na centrální server. Vy tyto změny vyzvednete a začleníte novou vzdálenou větev do své práce — vaše historie teď vypadá nějak takto:
![Vyzvednutí (fetch) bodů zápisu a jejich začlenění (merge) do vaší práce.](png/perils-of-rebasing-2.png)
Jenže osoba, která odeslala výsledek sloučení, se rozhodne vrátit zpět a svou práci raději přeskládat. Provede příkaz git push --force
a přepíše historii na serveru.
Vy poté znovu vyzvednete data ze serveru (fetch), čímž získáte nové objekty revizí.
![Někdo odešle přeskládané objekty revize a zahodí ty, z kterých jste při své práci vycházeli.](png/perils-of-rebasing-3.png)
Teď jste oba v loji.
Pokud provedete git pull
, vytvoříte bod sloučení (merge commit), který zahrnuje obě historické linie. Váš repozitář bude vypadat takto:
![Opakované začlenění stejné práce do nového bodu sloučení.](png/perils-of-rebasing-4.png)
Pokud s takovouto historií spustíte příkaz git log
, nastane zmatečná situace, kdy se zobrazí dvě revize se stejným autorem, datem a zprávou k revizi.
Když navíc odešlete takovou historii zpět na server, znovu na centrálním serveru vzniknou všechny objekty revizí, které vznikly přeskládáním, což vede k dalšímu zmatení lidí okolo.
Dá se předpokládat, že druhý vývojář nechtěl, aby byly C4
a C6
součástí historie. To je důvodem, proč přeskládání vůbec prováděl.
Přeskládejte po přeskládání
Když už se do takové situace dostanete, má Git v zásobě trochu magie, která vám z toho může pomoci ven. Pokud někdo ve vašem týmu vynutí odeslání změn, které přepíší práci z které vycházíte, pak potřebujete zjistit, co zůstalo vaše a co bylo přepsáno.
Věc se má tak, že Git pro objekt revize kromě kontrolního součtu (SHA-1) počítá také kontrolní součet, který vychází pouze ze záplat, které s revizí vznikají. Jmenuje se to „patch-id“ (doslova „identifikace záplaty“).
Pokud si stáhnete práci, která byla přepsána a nad těmito novými objekty revize od spolupracovníka provedete přeskládání, umí často Git úspěšně zjistit, co pochází určitě od vás a aplikovat to na vrchol nové větve.
Tak například, pokud se při předchozím scénáři nacházíme v situaci na obrázku Někdo odešle přeskládané objekty revize a zahodí ty, z kterých jste při své práci vycházeli a místo provedení sloučení (merge) spustíme git rebase teamone/master
, provede Git následující:
-
Určí, jaká práce je pro naši větev jedinečná (C2, C3, C4, C6, C7).
-
Určí, co z toho nejsou body sloučení (C2, C3, C4).
-
Určí, které z nich nebyly do cílové větve zapsány znovu (jsou to jen C2 a C3, protože C4 představuje stejnou záplatu jako C4').
-
Na
teamone/master
aplikuje jen určené revize.
Takže místo výsledku z obrázku Opakované začlenění stejné práce do nového bodu sloučení, skončíme s něčím, co odpovídá spíše obrázku Přeskládání nad vynuceně odeslaným výsledkem přeskládání.
![Přeskládání nad vynuceně odeslaným výsledkem přeskládání.](png/perils-of-rebasing-5.png)
Funguje to jen tehdy, když C4 a C4', které vytvořil váš partner, představují téměř shodné záplaty.
V opačném případě nebude příkaz rebase
schopen zjistit, že se jedná o duplikát, a přidá další záplatu, která se podobá té z C4 (kterou se ale pravděpodobně nepodaří čistě aplikovat, protože tam budou alespoň nějaké změny).
Můžete si to trochu zjednodušit spuštěním git pull --rebase
místo běžného git pull
.
Nebo to v tomto případě můžete provést ručně příkazem git fetch
, následovaným příkazem git rebase teamone/master
.
Pokud používáte git pull
a chcete, aby se z --rebase
stala výchozí volba, můžete nastavit konfigurační hodnotu pull.rebase
příkazem jako je git config --global pull.rebase true
.
Pokud přeskládání chápete jako způsob úklidu a práce s revizemi před tím, než je odešlete, a pokud přeskládáváte jen revize, které nikdy nebyly veřejně dostupné, pak budete v pohodě. Pokud přeskládáte revize, které již byly odeslány do veřejného prostoru a ostatní na těchto revizích (commit) mohli založit další práce, pak se můžete dostat do nepříjemných potíží a stát se předmětem opovržení týmových kolegů.
Pokud to vy nebo váš partner v určité chvíli považujete za nezbytnost, ujistěte se, že všichni umí spustit git pull --rebase
, aby poté své utrpení učinili o něco snesitelnějším.
Přeskládání vs. slučování
Když jste teď přeskládání a slučování viděli v akci, možná by vás zajímalo, které z nich je lepší. Než na to budeme moci odpovědět, podívejme se na to s odstupem a proberme si, co rozumíme historií.
Jeden pohled na věc považuje historii zápisů do repozitáře za záznam toho, co se skutečně stalo. Je to historický dokument, který je cenný sám o sobě, a neměli bychom s ním manipulovat. Z tohoto pohledu jsou úpravy historie zápisů revizí téměř rouhačstvím; lžete o tom, co se skutečně stalo. Jenže co když se vyskytly nějaké zamuchlané posloupnosti bodů sloučení? Takhle se to stalo a repozitář by to měl zachovat pro další generace.
Z opačného pohledu je historie zápisu revizí příběhem o tom, jak váš projek vznikal. U knihy byste také nepublikovali pracovní verzi a příručka o tom, jak udržovat udržovat váš vlastní software vyžaduje pečlivé úpravy. Zastánci tohoto tábora používají nástroje jako přeskládání a filtrování větví k tomu, aby příběh sdělili způsobem, který je nejlepší z hlediska budoucích čtenářů.
A teď zpět k otázce, zda je lepší slučování nebo přeskládání. Vidíte, doufám, že to není tak jednoduché. Git je mocný nástroj a dovoluje vám s historií provádět mnoho věcí. Ale každý tým a každý projekt se liší. Když teď víte, jak oba přístupy fungují, je jen na vás, který z nich ve vaší konkrétní situaci zvolíte jako nejlepší.
Obecně můžete nejlepší z obou světů získat tím, že použijete přeskládání vašich lokálních změn, které jste dosud nezveřejnili, abyste příběh vyčistili, než jej zveřejníte. Ale nikdy neprovádějte přeskládání toho, co už bylo někam odesláno.