-
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
5.2 Distribuovaný Git - Přispívání do projektu
Přispívání do projektu
Hlavní potíž při popisu způsobu přispívání do projektu spočívá v tom, že existuje obrovské množství variací, jak se to dá udělat. Git je velmi pružný, lidé mohou spolupracovat různými způsoby (a taky to tak dělají), takže není snadné popsat, jakým způsobem byste měli přispívat. Každý projekt je trochu jiný. Mezi proměnné v tomto procesu patří počet aktivních přispěvatelů, zvolený pracovní postup, vaše oprávnění pro odesílání revizí a případně i metoda externího přispívání.
První proměnnou je počet aktivních přispěvatelů. Kolik uživatelů aktivně přispívá svým kódem do projektu a jak často? V mnoha případech budete mít dva nebo tři vývojáře přispívající několika málo revizemi denně. U spících projektů to bude i méně. U větších společností nebo u větších projektů může počet vývojářů dosahovat tisíců — při stovkách nebo tisících zápisů revizí (commit) denně. Počet přispěvatelů je důležitý, protože s rostoucím počtem vývojářů narůstají i potíže se zajištěním toho, aby byl váš kód aplikován čistě, nebo aby ho bylo možné snadno začlenit (merge). Vámi odeslané změny se mohou ukázat jako zastaralé nebo vážně narušené pracemi, které byly do projektu začleněny během vaší práce, nebo v době, kdy vaše změny čekaly na schválení či aplikaci. Jak lze dosáhnout neustálé aktuálnosti vašeho kódu a platnosti vašich revizí?
Další proměnnou je pracovní postup, který se u projektu využívá. Probíhá vývoj centralizovaně s tím, že každý vývojář má stejné oprávnění pro zápis do hlavní linie kódu? Má projekt svého správce nebo integračního manažera, který kontroluje všechny záplaty? Jsou všechny záplaty odborně posuzovány a schvalovány? Jste součástí tohoto procesu? Jsou součástí systému poručíci a musíte všechnu svou práci odesílat nejprve jim?
Další otázkou je vaše oprávnění k zapisování revizí. Pracovní postup při přispívání do projektu se velmi liší podle toho, zda máte, či nemáte oprávnění k zápisu do projektu. Pokud oprávnění k zápisu nemáte, jaké metodě se dává přednost při přijímání příspěvků? Je nějaká strategie vůbec určena? Kolik práce představuje jeden váš příspěvek? A jak často přispíváte?
Všechny tyto otázky mohou mít vliv na efektivní přispívání do projektu a určují, jaký pracovní postup je vůbec možný a který bude upřednostněn. Každou z těchto otázek si probereme na sérii praktických případů, postupně od jednodušších po složitější. Z uvedených příkladů byste si měli být schopni odvodit vlastní pracovní postup, který budete v praxi využívat.
Pravidla pro zápis revizí
Než se podíváme na konkrétní případy, uveďme rychlou poznámku o zprávách k revizím.
Pokud stanovíte dobrá pravidla pro vytváření revizí (commit) a pokud se jich budete držet, bude práce s Gitem a spolupráce s ostatními mnohem jednodušší.
Samotný projekt Git obsahuje dokument, v němž je navržena celá řada dobrých tipů pro vytváření revizí, z kterých se vytvářejí záplaty. Najdete ho ve zdrojovém kódu systému Git, v souboru Documentation/SubmittingPatches
.
Především nechcete odesílat chybně použité prázdné znaky (whitespace).
Git nabízí snadný způsob, jak tyto chyby zkontrolovat. Před zapsáním revize spusťte git diff --check
, který zkontroluje prázdné znaky a zobrazí vám je.
![Výstup příkazu `git diff --check`.](png/git-diff-check.png)
git diff --check
.Spustíte-li tento příkaz před zapsáním revize, můžete zjistit, zda se budou zapisovat i problematické prázdné znaky, které by mohly ostatní vývojáře obtěžovat.
Dále se snažte zapisovat každou revizi (commit) jako logicky oddělený soubor změn.
Pokud je to možné, snažte se, aby byly vaše změny stravitelné. Není právě ideální pracovat celý víkend na pěti různých problémech a v pondělí je všechny najednou odeslat formou jedné velké revize.
Když už během víkendu nebudete zapisovat revize, využijte v pondělí oblasti připravených změn pro rozdělení práce alespoň do stejného počtu revizí, kolik je řešených problémů, a přidejte k nim vysvětlující zprávy.
Pokud některé změny upravují stejný soubor, zkuste použít příkaz git add --patch
a připravit soubory k zapsání po částech (podrobnosti jsou popsány v kapitole Interactive Staging).
Snímek projektu na vrcholu větve bude stejný, ať zapíšete jednu revizi, nebo pět (za předpokladu, že vložíte všechny změny). Snažte se proto usnadnit práci svým kolegům, kteří možná někdy budou vaše změny revidovat.
Takový přístup současně usnadňuje stahování změn (pull) nebo vracení jedné sady změn do původního stavu — bude-li to později třeba.
Podkapitola Rewriting History popisuje několik užitečných triků, jak v systému Git přepsat historii a jak interaktivně připravovat soubory k zapsání. Než svou práci odešlete ostatním, použijte tyto nástroje k udržení čisté a srozumitelné historie.
Poslední věcí, na kterou byste měli myslet, jsou zprávy k revizím. Pokud si zvyknete připisovat k revizím kvalitní zprávy, bude pro vás práce s Gitem a spolupráce s ostatními mnohem jednodušší. Základním pravidlem je, že by vaše zprávy měly začínat jedním řádkem, který nemá víc než asi 50 znaků a který popisuje sadu provedených změn. Za ním následuje prázdný řádek a potom podrobnější vysvětlení. Projekt Git vyžaduje, aby podrobnější popis revize zahrnoval i vaši motivaci ke změnám a aby uvedl srovnání nové implementace s původním chováním. Tuto zásadu je dobré dodržovat. Doporučuje se také, aby se pro zápis zpráv používal rozkazovací způsob. Jinými slovy, formulujte je jako příkazy. Místo „Přidal jsem testy pro“ nebo „Přidány testy pro“ použijte „Přidej testy pro“. Zde je originální (přeložená) šablona, kterou napsal Tim Pope:
Krátké shrnutí změn (do 50 znaků)
Podrobnější vysvětlující text, pokud je třeba. Zalamujte řádky
přibližně na 72 znaků. Někdy se první řádek používá jako předmět
emailu a zbytek textu jako tělo dopisu. Prázdný řádek, který
odděluje shrnutí od těla je nezbytně nutný (pokud tělo nevynecháte
úplně); nástroje jako rebase mohou být zmatené, pokud tyto části
neoddělíte.
Další odstavce se oddělují prázdným řádkem.
- Můžete používat i odrážky.
- Pro odrážku se typicky používá pomlčka nebo hvězdička,
před kterou se uvádí jedna mezera. Mezi odrážky se vkládají
prázdné řádky, ale tady se zvyklosti mohou lišit.
Pokud budou všechny vaše zprávy k revizím vypadat takto, usnadníte tím práci sobě i svým spolupracovníkům.
Projekt Git obsahuje kvalitně naformátované zprávy k revizím. Zkuste spustit git log --no-merges
, abyste se podívali, jak vypadá pěkně naformátovaná historie revizí projektu.
V následujících příkladech — a ve většině ukázek v knize — se takto pěkně naformátované zprávy nepoužívají kvůli stručnosti. Místo toho budeme používat volbu -m
za příkazem git commit
.
Dělejte to, jak říkám, a ne jak to dělám.
Malý soukromý tým
Nejjednodušší situaci, s kterou se asi setkáte, představuje soukromý projekt s jedním nebo pár dalšími vývojáři. „Soukromý“ v této souvislosti znamená s uzavřeným zdrojovým kódem — okolní svět k němu nemá přístup. Vy a vaši ostatní vývojáři máte všichni oprávnění odesílat změny do repozitáře (push).
V takovém prostředí můžete uplatnit podobný pracovní postup, na jaký jste možná zvyklí ze systému Subversion nebo z jiného centralizovaného systému. Přesto získáte výhody v takových ohledech, jako je zapisování revizí offline a podstatně snazší větvení a slučování. Pracovní postup však bude velmi podobný. Hlavním rozdílem je to, že slučování probíhá na straně klienta, a ne během zapisování revize na straně serveru. Podívejme se, jak to může vypadat, když dva vývojáři začnou spolupracovat na projektu se sdíleným repozitářem. První vývojář, John, naklonuje repozitář, provede změny a zapíše lokální revizi. (V následujících příkladech byly zprávy protokolů nahrazeny třemi tečkami, abych je trochu zkrátil.)
# Johnův počítač
$ git clone john@githost:simplegit.git
Cloning into 'simplegit'...
...
$ cd simplegit/
$ vim lib/simplegit.rb
$ git commit -am 'removed invalid default value'
[master 738ee87] removed invalid default value
1 files changed, 1 insertions(+), 1 deletions(-)
Druhá vývojářka, Jessica, učiní totéž — naklonuje repozitář a zapíše provedenou změnu:
# Jessičin počítač
$ git clone jessica@githost:simplegit.git
Cloning into 'simplegit'...
...
$ cd simplegit/
$ vim TODO
$ git commit -am 'add reset task'
[master fbff5bc] add reset task
1 files changed, 1 insertions(+), 0 deletions(-)
Jessica teď odešle (push) svou práci na server:
# Jessičin počítač
$ git push origin master
...
To jessica@githost:simplegit.git
1edee6b..fbff5bc master -> master
Také John se pokusí odeslat své změny na server:
# Johnův počítač
$ git push origin master
To john@githost:simplegit.git
! [rejected] master -> master (non-fast forward)
error: failed to push some refs to 'john@githost:simplegit.git'
John nemá povoleno odeslat změny, protože mezitím odeslala své změny Jessica. Pochopit to je obzvlášť důležité v případě, kdy jste zvyklí na Subversion. Můžete si totiž všimnout, že oba vývojáři neupravovali stejný soubor. Pokud byly upraveny různé soubory, provádí Subversion takové sloučení na serveru automaticky. Ale v Gitu musíte provést sloučení revizí (merge) lokálně. John musí vyzvednout (fetch) změny, které provedla Jessica, a začlenit je (merge) do své práce. Teprve potom mu bude umožněno je odeslat (push):
$ git fetch origin
...
From john@githost:simplegit
+ 049d078...fbff5bc master -> origin/master
V tomto okamžiku vypadá Johnův lokální repozitář nějak takto:
![Johnova odchylující se historie.](png/small-team-1.png)
John má k dispozici odkaz na změny, které odeslala Jessica, ale než bude moci sám odeslat svá data, bude muset začlenit její práci:
$ git merge origin/master
Merge made by recursive.
TODO | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
Sloučení probíhá hladce — Johnova historie revizí teď vypadá takto:
![Johnův repozitář po začlenění `origin/master`.](png/small-team-2.png)
origin/master
.Teď může John svůj kód otestovat, aby se ujistil, že stále pracuje správně, a pak může odeslat svou novou sloučenou práci na server:
$ git push origin master
...
To john@githost:simplegit.git
fbff5bc..72bbc59 master -> master
Johnova historie revizí nakonec vypadá takto:
![Johnova historie po odeslání revize na server `origin`.](png/small-team-3.png)
origin
.Jessica mezitím pracovala na tématické větvi.
Vytvořila tématickou větev s názvem issue54
a zapsala do ní tři revize.
Zatím ještě nevyzvedla Johnovy změny, takže její historie revizí vypadá nějak takto:
![Jessičina tématická větev.](png/small-team-4.png)
Jessica chce synchronizovat svou práci s Johnem, a proto vyzvedne jeho data:
# Jessičin počítač
$ git fetch origin
...
From jessica@githost:simplegit
fbff5bc..72bbc59 master -> origin/master
Tím stáhne práci, kterou mezitím odeslal John. Historie revizí Jessicy teď vypadá takto:
![Historie Jessicy po vyzvednutí Johnových změn.](png/small-team-5.png)
Jessica považuje svou tématickou větev za dokončenou, ale chce zjistit, co by měla do své práce začlenit, aby ji mohla odeslat.
Spustí proto příkaz git log
:
$ git log --no-merges issue54..origin/master
commit 738ee872852dfaa9d6634e0dea7a324040193016
Author: John Smith <jsmith@example.com>
Date: Fri May 29 16:01:27 2009 -0700
removed invalid default value
Zápis issue54..origin/master
představuje filtr příkazu, kterým se Gitu říká, aby zobrazil seznam jen těch objektů revize, které se nacházejí v druhé z větví (zde origin/master
), ale které se nenacházejí v první větvi (zde issue54
).
Podrobně se budeme touto syntaxí zabývat v části Commit Ranges.
Z výstupu vidíme, že existuje jediná revize, kterou zapsal John a kterou Jessica nesloučila se svou prací.
Pokud začlení origin/master
, bude to jediná revize, která změní její lokální práci.
Teď může Jessica začlenit svou tématickou větev do své větve master
(merge), pak začlenit Johnovu práci (origin/master
) rovněž do své větve master
a potom může vše znovu odeslat na server (push).
Nejdříve se přepne zpět na svou větev master
, aby do ní mohla vše integrovat:
$ git checkout master
Switched to branch 'master'
Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.
Buď může nejdříve začlenit origin/master
nebo issue54
. Obě revize jsou následníky té aktuální, takže na pořadí nezáleží.
Konečný snímek bude stejný, ať zvolí jakékoli pořadí. Trochu se bude lišit jen historie revizí.
Jessica se rozhodne začlenit nejdříve issue54
:
$ git merge issue54
Updating fbff5bc..4af4298
Fast forward
README | 1 +
lib/simplegit.rb | 6 +++++-
2 files changed, 6 insertions(+), 1 deletions(-)
Nevyskytly se žádné problémy. Jak vidíte, šlo o jednoduchý posun „rychle vpřed“.
Nyní Jessica začlení Johnovu práci (origin/master
):
$ git merge origin/master
Auto-merging lib/simplegit.rb
Merge made by recursive.
lib/simplegit.rb | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
Začlenění proběhne čistě a Jessičina historie bude vypadat následovně:
![Jessičina historie po začlenění Johnových změn.](png/small-team-6.png)
Větev origin/master
je dosažitelná z Jessičiny větve master
, takže by měla být schopná práci úspěšně odeslat (za předpokladu, že John mezitím neodeslal další revizi):
$ git push origin master
...
To jessica@githost:simplegit.git
72bbc59..8059c15 master -> master
Každý z nich provedl několik zápisů (commit) a úspěšně začlenil práci toho druhého (merge).
![Jessičina historie po odeslání všech změn zpět na server.](png/small-team-7.png)
Toto je jeden z nejjednodušších pracovních postupů.
Po určitou dobu pracujete, obvykle na nějaké tématické větvi, a když je připravena k integraci, začleníte ji do své větve master
.
Chcete-li tuto práci sdílet, pak vyzvednete (fetch) a začleníte (merge) případné změny z origin/master
do vaší větve master
. Nakonec odešlete všechna data do větve master
na serveru (push).
Obvyklá posloupnost událostí vypadá takto:
![Obecná posloupnost událostí pro jednoduchý pracovní postup s více vývojáři.](png/small-team-flow.png)
Soukromý řízený tým
V následujícím scénáři se podíváme na role přispěvatelů ve větší soukromé skupině. Naučíte se, jak pracovat v prostředí, v němž na jednotlivých úkolech spolupracují malé skupiny a tyto týmové příspěvky jsou poté integrovány jinou skupinou.
Řekněme, že John a Jessica spolupracují na jednom úkolu, zatímco Jessica a Josie pracují na jiném.
Společnost v tomto případě používá typ pracovního postupu s integračním manažerem, kdy práci jednotlivých skupin integrují pouze někteří inženýři a větev master
hlavního repozitáře mohou aktualizovat pouze oni.
V tomto scénáři se veškerá práce provádí ve větvích jednotlivých týmů a později je spojována integrátory.
Sledujme pracovní postup Jessicy pracující na dvou úkolech a spolupracující v tomto prostředí paralelně s dvěma různými vývojáři.
Protože už má naklonovaný repozitář, rozhodne se pracovat nejprve na úkolu featureA
.
Vytvoří si pro něj novou větev a udělá v ní kus práce:
# Jessičin počítač
$ git checkout -b featureA
Switched to a new branch 'featureA'
$ vim lib/simplegit.rb
$ git commit -am 'add limit to log function'
[featureA 3300904] add limit to log function
1 files changed, 1 insertions(+), 1 deletions(-)
V tomto okamžiku potřebuje sdílet svou práci s Johnem, a tak odešle revize své větve featureA
na server.
Jessica nemá oprávnění pro odesílání dat do větve master
(ten mají pouze integrátoři), takže aby mohla s Johnem spolupracovat, musí své revize odeslat do jiné větve:
$ git push -u origin featureA
...
To jessica@githost:simplegit.git
* [new branch] featureA -> featureA
Jessica e-mailem Johnovi sdělí, že odeslala svou práci do větve pojmenované featureA
a že se na ni může podívat.
Zatímco čeká na zpětnou vazbu od Johna, rozhodne se, že začne pracovat spolu s Josie na úkolu featureB
.
Začne tím, že založí novou větev, která vychází ze serverové větve master
:
# Jessičin počítač
$ git fetch origin
$ git checkout -b featureB origin/master
Switched to a new branch 'featureB'
Jessica nyní vytvoří několik revizí ve větvi featureB
:
$ vim lib/simplegit.rb
$ git commit -am 'made the ls-tree function recursive'
[featureB e5b0fdc] made the ls-tree function recursive
1 files changed, 1 insertions(+), 1 deletions(-)
$ vim lib/simplegit.rb
$ git commit -am 'add ls-files'
[featureB 8512791] add ls-files
1 files changed, 5 insertions(+), 0 deletions(-)
Jessičin repozitář vypadá nějak takto:
![Jessičina výchozí historie revizí.](png/managed-team-1.png)
Je připravena odeslat svou práci, ale dostane e-mail od Josie, že již na server odeslala větev featureBee
, v níž už je část práce hotová.
Než bude Jessica moci odeslat svou práci na server, musí do ní nejprve začlenit práci Josie.
Změny, které Josie provedla, vyzvedne příkazem git fetch
:
$ git fetch origin
...
From jessica@githost:simplegit
* [new branch] featureBee -> origin/featureBee
Nyní může Jessica začlenit tyto změny do své práce příkazem git merge
:
$ git merge origin/featureBee
Auto-merging lib/simplegit.rb
Merge made by recursive.
lib/simplegit.rb | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
Tady nastává drobný problém. Musí odeslat sloučenou práci ze své větve featureB
do větve featureBee
na serveru.
Může tak učinit příkazem git push
s určením lokální větve, za níž bude následovat dvojtečka (:) a za ní vzdálená větev:
$ git push -u origin featureB:featureBee
...
To jessica@githost:simplegit.git
fba9af8..cd685d1 featureB -> featureBee
Říká se tomu refspec.
Podrobnější diskusi o specifikaci gitových referencí a o různých možnostech práce s nimi najdete v kapitole The Refspec.
Všimněte si též příznaku -u
. Jde o zkratku pro volbu --set-upstream
, která se používá k nastavení větví pro pozdější zjednodušené odesílání (push) a stahování (pull).
John poté pošle Jessice e-mail, aby jí sdělil, že odeslal několik změn do větve featureA
, a poprosí ji, aby je ověřila.
Jessica je stáhne spuštěním příkazu git fetch
:
$ git fetch origin
...
From jessica@githost:simplegit
3300904..aad881d featureA -> origin/featureA
Poté si může příkazem git log
prohlédnout, co všechno bylo změněno:
$ git log featureA..origin/featureA
commit aad881d154acdaeb2b6b18ea0e827ed8a6d671e6
Author: John Smith <jsmith@example.com>
Date: Fri May 29 19:57:33 2009 -0700
changed log output to 30 from 25
Nakonec začlení Johnovu práci do své vlastní větve featureA
:
$ git checkout featureA
Switched to branch 'featureA'
$ git merge origin/featureA
Updating 3300904..aad881d
Fast forward
lib/simplegit.rb | 10 +++++++++-
1 files changed, 9 insertions(+), 1 deletions(-)
Jessica by ráda něco vylepšila, a proto vytvoří novou revizi a odešle ji zpět na server:
$ git commit -am 'small tweak'
[featureA 774b3ed] small tweak
1 files changed, 1 insertions(+), 1 deletions(-)
$ git push
...
To jessica@githost:simplegit.git
3300904..774b3ed featureA -> featureA
Historie revizí Jessicy bude nyní vypadat takto:
![Jessičina historie po zapsání revizí do větve s úkolem.](png/managed-team-2.png)
Jessica, Josie a John pošlou zprávu integrátorům, že větve featureA
a featureBee
jsou na serveru připraveny k integraci do hlavní linie.
Poté, co integrátoři uvedené větve začlení do hlavní linie, bude možné vyzvednout (fetch) nový bod sloučení (merge commit) a historie revizí bude vypadat takto:
![Historie Jessicy po začlenění obou jejích tématických větví.](png/managed-team-3.png)
Mnoho skupin přechází na Git právě kvůli této možnosti paralelní spolupráce několika týmů a možnosti slučování různých linií práce až v pozdějších fázích procesu. Možnost spolupráce menších podskupin týmu prostřednictvím vzdálených větví — aniž by si práce vyžádala účast celého týmu nebo aby bránila ostatním v jiné práci — patří k obrovským výhodám Gitu. Posloupnost událostí ve zde popsaném pracovním postupu vypadá takto:
![Základní posloupnost událostí pracovního postupu řízeného týmu.](png/managed-team-flow.png)
Odštěpený veřejný projekt
Přispívání do veřejných projektů je trochu odlišné. Protože nemáte oprávnění k tomu, abyste mohli aktualizovat větve projektu přímo, musíte svou práci správcům doručit nějakým jiným způsobem. První příklad popisuje, jak se přispívá s využitím odštěpení (fork) na gitovských hostitelských serverech, které podporují snadné vytváření odštěpených projektů. Tento mechanismus podporuje řada hostitelských serverů (včetně GitHub, BitBucket, Google Code, repo.or.cz a dalších) a řada správců projektů takový styl přispívání očekává. Další část pojednává o projektech, které upřednostňují přijímání záplat posílaných e-mailem.
Nejdříve pravděpodobně naklonujete hlavní repozitář, vytvoříte tématickou větev — pro záplatu nebo pro posloupnost záplat, kterými chcete přispět — a zde vaši práci zrealizujete. Posloupnost příkazů vypadá v podstatě takto:
$ git clone (url)
$ cd project
$ git checkout -b featureA
# (něco uděláte)
$ git commit
# (něco uděláte)
$ git commit
Note
|
Možná budete chtít použít |
Až budete s prací ve větvi hotovi a budete ji chtít poslat zpět správcům, přejděte na původní stránku projektu a klikněte na tlačítko „Fork“, čímž vytvoříte vlastní odštěpený projekt, do kterého budete moci zapisovat.
Poté musíte URL adresu tohoto nového repozitáře přidat jako druhý vzdálený repozitář, v tomto případě pojmenovaný myfork
:
$ git remote add myfork (url)
Do něj teď musíte odeslat svou práci (push).
Lepší bude, když do repozitáře odešlete svou tématickou větev (v které pracujete) než abyste výsledek začlenili do své větve master
(merge) a odesílali tuto větev.
Důvod je ten, že pokud nebude vaše práce přijata, nebo pokud z ní budou převzaty jen některé revize, nebudete muset svou větev master
vracet zpět.
Pokud správci provedou sloučení (merge), přeskládání (rebase) nebo částečné převzetí vaší práce (cherry-pick), budete stejně muset stáhnout změny z jejich repozitáře (pull):
$ git push -u myfork featureA
Pokud jste práci odeslali do odštěpeného repozitáře, musíte to správci oznámit.
Často se tomu říká „pull request“, čili požadavek na stažení. Můžete ho vytvořit prostřednictvím webové stránky — GitHub má svůj vlastní mechanismus Pull Request, kterým se budeme zabývat v kapitole GitHub — nebo můžete spustit příkaz git request-pull
a správci projektu ručně zaslat výstup příkazu e-mailem.
Příkazu request-pull
se zadává základní větev, do které chcete nechat vaši tématickou větev vtáhnout, a URL adresa gitového repozitáře, z kterého se má změna stahovat. Příkaz vypíše shrnutí všech změn, které by měly být vtaženy.
Pokud chce například Jessica poslat Johnovi požadavek na stažení a vytvořila předtím dvě revize v tématické větvi, kterou právě odeslala, může zadat tento příkaz:
$ git request-pull origin/master myfork
The following changes since commit 1edee6b1d61823a2de3b09c160d7080b8d1b3a40:
John Smith (1):
added a new function
are available in the git repository at:
git://githost/simplegit.git featureA
Jessica Smith (2):
add limit to log function
change log output to 30 from 25
lib/simplegit.rb | 10 +++++++++-
1 files changed, 9 insertions(+), 1 deletions(-)
Výstup příkazu lze odeslat správci. Říká mu, odkud daná větev vychází, podá mu přehled o revizích a řekne mu, odkud lze práci stáhnout.
U projektů, kde nevystupujete jako správce, je většinou jednodušší, aby vaše větev master
stále sledovala větev origin/master
a abyste práci prováděli v tématických větvích, které můžete v případě odmítnutí snadno odstranit.
Izolováním tématických úkolů do tématických větví si také usnadníte přeskládání vaší práce v případě, kdy se vrchol v hlavním repozitáři mezi tím posunul a vaše revize se už nedají čistě aplikovat.
Pokud například chcete do projektu odeslat druhé pracovní téma, nepokračujte v práci v tématické větvi, kterou jste právě odeslali. Začněte znovu od začátku z větve master
hlavního repozitáře:
$ git checkout -b featureB origin/master
# (nějaká práce)
$ git commit
$ git push myfork featureB
# (odeslání e-mailu správci)
$ git fetch origin
Teď je každé z vašich témat obsaženo v samostatném zásobníku — podobá se frontě záplat — a můžete je přepsat, přeskládat a upravit, aniž by se obě témata navzájem ovlivňovala nebo aby se mezi nimi vytvářela vzájemná závislost, viz obrázek:
![Počáteční historie revizí s prací na `featureB`.](png/public-small-1.png)
featureB
.Řekněme, že správce projektu vtáhl do projektu několik jiných záplat a vyzkoušel vaši první větev, ale ta už se nedá začlenit čistě.
V takovém případě můžete zkusit přeskládat tuto větev na vrchol větve origin/master
, vyřešit za správce vzniklé konflikty a poté své změny ještě jednou odeslat:
$ git checkout featureA
$ git rebase origin/master
$ git push -f myfork featureA
Tím se vaše historie přepíše a bude vypadat jako na obrázku Historie revizí po přeskládání práce z featureA
..
![Historie revizí po přeskládání práce z `featureA`.](png/public-small-2.png)
featureA
.Protože jste větev přeskládali, musíte u příkazu pro odesílání (push) přidat -f
, abyste mohli serverové větvi featureA
podsunout revizi, která není jejím potomkem.
Druhou možností by bylo odeslání nové práce na server do jiné větve (nazvané třeba featureAv2
).
Podívejme se ještě na jeden možný scénář: Správce se podíval na práci ve vaší druhé větvi a tento koncept se mu líbí, ale rád by, abyste změnili jeden implementační detail.
Této příležitosti využijete i k tomu, abyste za základ své práce vzali aktuální stav projektu ve větvi master
.
Začnete vytvořením nové větve z aktuální větve origin/master
, nacpete (squash) do ní změny z featureB
, vyřešíte případné konflikty, provedete požadovanou úpravu implementace a vše odešlete jako novou větev:
$ git checkout -b featureBv2 origin/master
$ git merge --squash featureB
# (změníte implementaci)
$ git commit
$ git push myfork featureBv2
Volba --squash
(stlačit) zajistí převzetí všech změn ze začleňované větve a stlačí je do podoby jedné sady změn, která vede ke stejnému stavu repozitáře, jako kdyby se provedlo opravdové sloučení (merge), ale nevytvoří se při tom bod sloučení[16] (merge commit).
To znamená, že váš budoucí objekt revize bude mít jen jednoho rodiče a přitom vám umožní vnést všechny změny z jiné větve a poté provést další úpravy ještě před tím, než se nová revize zapíše.
V případě základního procesu slučování (merge) může být užitečná i volba --no-commit
, která oddálí vytvoření bodu sloučení.
Teď už můžete správci zaslat zprávu, že jste provedli požadované změny a že je najde ve vaší větvi featureBv2
.
![Historie revizí po práci ve větvi `featureBv2`.](png/public-small-3.png)
featureBv2
.Veřejný projekt využívající elektronickou poštu
Mnoho projektů si vytvořilo vlastní procedury pro přijímání záplat. Konkrétní pravidla si u každého projektu budete muset zjistit, protože budou odlišná. Protože existuje několik starších, větších projektů, kde se záplaty přijímají prostřednictvím vývojářské poštovní konference[17], projdeme si teď podobný příklad.
Pracovní postup je podobný jako v předchozím případě. Pro každou sérii záplat, na níž pracujete, vytvoříte samostatnou tématickou větev. Odlišnost spočívá ve způsobu doručování změn do projektu. Místo vytváření odštěpeného projektu a odesílání změn (push) do vlastní zapisovatelné verze, vygenerujete e-mailovou verzi každé série revizí a pošlete je e-mailem do poštovní konference vývojářů:
$ git checkout -b topicA
# (práce)
$ git commit
# (práce)
$ git commit
Teď máte dvě revize, které chcete odeslat do poštovní konference.
Pro vygenerování souborů v podobě vhodné pro zaslání poštou použijete příkaz git format-patch
. Každá revize se tím přetransformuje na e-mailovou zprávu, jejíž předmět bude tvořit první řádek zprávy k revizi a tělo e-mailu bude tvořeno zbytkem zprávy a samotnou záplatou.
Výhodou tohoto postupu je, že se při aplikaci záplaty z e-mailu, který byl vygenerován příkazem format-patch
, korektně převezmou všechny informace o revizi.
$ git format-patch -M origin/master
0001-add-limit-to-log-function.patch
0002-changed-log-output-to-30-from-25.patch
Příkaz format-patch
vypíše názvy souborů záplaty, kterou vytváří.
Přepínač -M
řekne systému Git, aby zkontroloval případné přejmenování.
Soubory nakonec vypadají takto:
$ cat 0001-add-limit-to-log-function.patch
From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001
From: Jessica Smith <jessica@example.com>
Date: Sun, 6 Apr 2008 10:17:23 -0700
Subject: [PATCH 1/2] add limit to log function
Limit log functionality to the first 20
---
lib/simplegit.rb | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index 76f47bc..f9815f1 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -14,7 +14,7 @@ class SimpleGit
end
def log(treeish = 'master')
- command("git log #{treeish}")
+ command("git log -n 20 #{treeish}")
end
def ls_tree(treeish = 'master')
--
2.1.0
Do souborů se záplatami můžete dodatečně připsat další informace, které jsou určeny pro poštovní konferenci, ale které nechcete vkládat do zprávy k revizi.
Pokud mezi řádek ---
a začátek záplaty (řádek lib/simplegit.rb
) přidáte nějaký text, mohou si ho vývojáři přečíst; ale při aplikaci záplaty se nepoužije.
Při odesílání souboru do poštovní konference můžete soubor buď vložit do svého poštovního programu, nebo ho můžete odeslat z příkazového řádku.
Vkládání textu často způsobuje problémy s formátováním, zvlášť v případě některých „chytřejších“ klientů, kteří správně nezachovávají zalamování řádků a prázdné znaky (whitespace).
Git naštěstí nabízí nástroj, který vám pomůže odeslat správně formátované záplaty protokolem IMAP, což pro vás může být jednodušší.
Ukážeme si, jak se dá záplata odeslat přes Gmail, což je nástroj pro poštu, který známe nejlépe. Podrobné pokyny pro používání celé řady poštovních programů najdete na konci dříve zmíněného souboru Documentation/SubmittingPatches
ve zdrojovém kódu systému Git.
Nejdříve si musíte ve vašem souboru ~/.gitconfig
nastavit sekci „imap“.
Každou hodnotu můžete nastavit zvlášť pomocí série příkazů git config
, nebo můžete vložit hodnoty ručně. Na závěr by ale měl váš konfigurační soubor vypadat nějak takto:
[imap]
folder = "[Gmail]/Drafts"
host = imaps://imap.gmail.com
user = user@gmail.com
pass = p4ssw0rd
port = 993
sslverify = false
Pokud váš server IMAP nepoužívá SSL, nebudou zřejmě dva poslední řádky vůbec třeba a hodnota hostitele bude imap://
, a nikoli imaps://
.
Až toto nastavení dokončíte, můžete použít příkaz git imap-send
, jímž umístíte sérii záplat (patch) do složky Drafts zadaného serveru IMAP:
$ cat *.patch |git imap-send
Resolving imap.gmail.com... ok
Connecting to [74.125.142.109]:993... ok
Logging in...
sending 2 messages
100% (2/2) done
V tomto okamžiku byste měli být schopni přejít do své složky Drafts, změnit pole To na adresu poštovní konference, do které záplatu posíláte, případně pole CC na správce nebo na osobu odpovědnou za tuto část, a odeslat.
Záplaty můžete odesílat i přes SMTP server.
Můžete rovněž nastavit každou hodnotu zvlášť sérií příkazů git config
, nebo je můžete vložit ručně do sekce sendemail vašeho souboru ~/.gitconfig
:
[sendemail]
smtpencryption = tls
smtpserver = smtp.gmail.com
smtpuser = user@gmail.com
smtpserverport = 587
Jakmile je to hotové, můžete záplaty odeslat příkazem git send-email
:
$ git send-email *.patch
0001-added-limit-to-log-function.patch
0002-changed-log-output-to-30-from-25.patch
Who should the emails appear to be from? [Jessica Smith <jessica@example.com>]
Emails will be sent from: Jessica Smith <jessica@example.com>
Who should the emails be sent to? jessica@example.com
Message-ID to be used as In-Reply-To for the first email? y
Git poté vytvoří log s určitými informacemi, který bude pro každou odesílanou záplatu vypadat asi takto:
(mbox) Adding cc: Jessica Smith <jessica@example.com> from
\line 'From: Jessica Smith <jessica@example.com>'
OK. Log says:
Sendmail: /usr/sbin/sendmail -i jessica@example.com
From: Jessica Smith <jessica@example.com>
To: jessica@example.com
Subject: [PATCH 1/2] added limit to log function
Date: Sat, 30 May 2009 13:29:15 -0700
Message-Id: <1243715356-61726-1-git-send-email-jessica@example.com>
X-Mailer: git-send-email 1.6.2.rc1.20.g8c5b.dirty
In-Reply-To: <y>
References: <y>
Result: OK
Shrnutí
Tato část se zabývala obvyklými pracovními postupy, které týkají několika velmi odlišných typů gitových projektů, s kterými se asi setkáte. Uvedla pár nových nástrojů, které vám pomohou celý proces zvládnout. V další části uvidíte, jak se pracuje na druhé straně — jak se spravuje gitový projekt. Dozvíte se, jak být benevolentním diktátorem nebo integračním manažerem.