Git
Chapters ▾ 2nd Edition

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`.
Figure 57. Výstup příkazu 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.
Figure 58. Johnova odchylující se historie.

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`.
Figure 59. Johnův repozitář po začlenění 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`.
Figure 60. Johnova historie po odeslání revize na server 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.
Figure 61. Jessičina tématická větev.

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.
Figure 62. Historie Jessicy po vyzvednutí Johnových změn.

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.
Figure 63. Jessičina historie po začlenění Johnových změn.

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.
Figure 64. Jessičina historie po odeslání všech změn zpět na server.

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.
Figure 65. Obecná posloupnost událostí pro jednoduchý pracovní postup s více vývojáři.

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í.
Figure 66. Jessičina výchozí historie revizí.

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.
Figure 67. Jessičina historie po zapsání revizí do větve s úkolem.

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í.
Figure 68. Historie Jessicy po začlenění obou jejích tématických větví.

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.
Figure 69. Základní posloupnost událostí pracovního postupu řízeného týmu.

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 rebase -i, abyste svou práci stlačili do jediného zápisu revize, nebo budete chtít práci přeuspořádat do posloupnosti revizí, která správci usnadní zkoumání záplat. Další informace o interaktivním přeskládání najdete v části Rewriting History.

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`.
Figure 70. Počáteční historie revizí s prací na 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`.
Figure 71. Historie revizí po přeskládání práce z 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`.
Figure 72. Historie revizí po práci ve větvi 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.