Práce s historií je díky distribuované nátuře Gitu trochu jiná než s centralizovanými systémy. Zde si ukážeme, jak s ní pracovat.
Důsledkem toho, že Git je distribuovaný, je to, že historie může být lehce změněna. Ale když si zahráváte s minulostí, dávejte si pozor: měňte pouze tu část, kterou vy sami ovládáte. Stejně jako se národy hádají, který z nich udělal jaká zvěrstva, pokud bude mít někdo jiný klon, jehož historie se bude lišit od té vaší, bude těžké přinutit vaše dva stromy, aby se domluvily.
Samozřejmě že pokud máte kontrolu i nad všemi stromy ostatních, tak žádný problém nenastává, protože je můžete jednoduše přepsat.
Někteří vývojáři si silně stojí za tím, že historie by měla být neměnná, bez příkras. Jiní si zase myslí, že stromy by měly být udělány prezentovatelné, než budou vypuštěny na veřejnost. Git podporuje oba úhly pohledu. Stejně jako klonování, větvení a slučování, je přepisování historie prostě jen další síla, kterou vám Git dává. Je na vás ji používat správně.
Mám to správně
Právě jste provedli commit, ale přejete si, aby se k němu pojila jiná zpráva? Pak spusťte:
$ git commit --ammend
ke změně poslední zprávy. Uvědomili jste si, že jste zapomněli přidat
soubor? Pak použijte git add
k jeho přidání a opět proveďte
příkaz výše.
Chcete přidat ještě pár dalších úprav do poslední commitu? Udělejte tyto úpravy a pak:
$ git commit --ammend -a
…ale potom
Představme si, že předchozí problém je ještě desetkrát horší. Pracovali jste docela dlouho a provedli jste pěknou řádku commitů. Ale nejste zcela spokojeni s tím, jak jsou zorganizovány, a některé zprávy by byly vhodné přepsání. Pak napište:
$ git rebase -i HEAD~10
a posledních 10 commitů se objeví ve vašem oblíbeném
$EDITOR
u. Ukázkový výňatek:
pick 5c6eb73 Přidán odkaz na repo.or.cz pick a311a64 Přerovnány analogie v "Pracujte jak chcete" pick 100834f Přidán push cíl do Makefile
A potom:
- Odstraňte commity vymazáním řádek.
- Proházejte commity prohozením řádek.
- Změňte
pick
naedit
pro úpravy commitu. - Změňte
pick
nasquash
ke sloučení commitu s předchozím.
Pokud jste označili commit k úpravám, spusťte:
$ git commit --amend
Jinak:
$ git rebase --continue
Provádějte commity brzo a často: můžete je jednoduše pročistit později s rebase.
Lokální změny zůstávají
Pracujete na nějakém aktivním projektu. Časem provedete pár lokálních commitů a potom se synchronizujete s oficiálním stromem pomocí merge. Toto se opakuje několikrát za sebou, než jste připraveni nahrát změny na centrální server.
Ale teď je váš lokální klon repozitáře neuspořádaná míchanice vašich a oficiálních změn. Radši byste viděli všechny vaše změny souvisle za sebou a potom všechny oficiální změny.
Toto je právě pro git rebase
, jako je popsáno výše. V mnoha
případech můžete použít přepínač --onto
a vyhnout se tak
vzájemnému působení.
Viz také git help rebase
pro podrobné ukázky na tento
úžasný příklad. Můžete commity rozdělit, můžete dokonce
přeuspořádat větve stromu.
Přepisování historie
Příležitostně narazíte na to, že potřebujete vymazávat lidi z oficiálních fotek, z historie vůbec, ve stylu Stalina. Například si představte, že hodláme uvolnit projekt, který ale zahrnuje soubor, který by měl zůstat soukromý z nějakého důvodu. Možná jsem nechal číslo mé kreditní karty v textovém souboru a omylem ho přidal do toho projektu. Vymazat soubor je nedostatečné, protože může být získán ze starších commitů. Musíme soubor odstranit ze všech commitů:
$ git filter-branch --tree-filter 'rm prisne/tajny/soubor' HEAD
Viz git help filter-branch
, kde se můžete dočíst o tomto
příkladě více a je tam i rychlejší metoda. Obecně vám
filter-branch umožňuje změnit velké kusy historie jedním
příkazem.
Nakonec vyměňte klony projektu s touto upravenou verzí, pokud s nimi chcete interagovat později.
Dělání historie
Chcete zmigrovat projekt na Git? Pokud je spravován jedním z dobře známých systémů, je šance, že někdo již napsal skript, který vyexportuje celou historii do Gitu.
Jinak se ale podívejte na git fast-import
, který čte textový
vstup v určitém formátu k vytvoření nového Gitího repozitáře. Typicky je
skript používající tento příkaz udělán narychlo, proběhne jednou a
přestěhuje celý projekt najednou.
Jako příklad vložte následující skript do dočasného souboru, třebas
jako /tmp/historie
:
---------------------------------- commit refs/heads/master committer Alice <alice@example.com> Thu, 01 Jan 1970 00:00:00 +0000 data <<EOT První commit. EOT M 100644 inline hello.c data <<EOT #include <stdio.h> int main() { printf("Hello, world!\n"); return 0; } EOT commit refs/heads/master committer Bob <bob@example.com> Tue, 14 Mar 2000 01:59:26 -0800 data <<EOT Nahrazení printf() za write(). EOT M 100644 inline hello.c data <<EOT #include <unistd.h> int main() { write(1, "Hello, world!\n", 14); return 0; } EOT ----------------------------------
Poté vytvořte Gití repozitář z tohoto souboru napsáním:
$ mkdir projekt; cd projekt; git init $ git fast-import < /tmp/historie
Poslední verzi projektu můžete získat pomocí:
$ git checkout master .
Příkaz git fast-export
změní jakýkoli Gití repozitář na
skript pro git fast-import
, jehož formát si můžete nastudovat
pro napsání exportérů, stejně jako pro transport Gitího repozitáře do
člověkem čitelného formátu. Díky těmto příkazům můžete posílat
repozitáře složené z textových souborů skrz textově založené
kanály.
Kdy se to všecko pokazilo?
Právě jste objevili rozbitou vlastnost ve vašem programu, o které víte, že se musela pokazit někdy před pár měsíci. Kruci! Odkud se tu ta chyba vzala? Jo, kdybyste tak testovali tu vlastnost hned, jak jste ji vyvinuli…
Na to je už ale pozdě. Ale pokud jste změny ukládali často, Git může určit, kde je problém:
$ git bisect start $ git bisect bad SHA1_SPATNE_VERZE $ git bisect good SHA1_DOBRE_VERZE
Git do pracovního adresáře umístí stav repozitáře uprostřed mezi těmito body. Otestujte vlastnost, a pokud je stále rozbitá:
$ git bisect bad
Pokud není, nahraďte bad
za good
. Git vás opět
přenese do stavu na půli cesty mezi dobrou a špatnou verzí při zúžených možnostech.
Po pár opakováních vás toto binární vyhledávání zavede na
commit, který způsobil tyto trable. Když skončíte s
pátráním, vraťte se do původního stavu napsáním:
$ git bisect reset
Místo toho, abyste testovali každou změnu ručně, můžete zautomatizovat vyhledávání:
$ git bisect run PRIKAZ
Git k rozhodnutí, jestli je ta která verze dobrá/špatná, používá návratovou hodnotu příkazu: příkaz by měl skončit s návratovým kódem 0, když je verze dobrá; 125, když by měla být přeskočena; a skončí-li jakýmkoli jiným kódem mezi 1 a 127, verze je špatná. Záporné návratové hodnoty zruší hledání.
Můžete dělat mnohem víc: stránka s nápovědou vysvětluje, jak vizualizovat bisect, prozkoumávat nebo přehrávat jeho záznam, a vyřadit nevinné změny pro rychlejší hledání.
Kdo to pokazil?
Jako mnoho ostatních verzovacích systémů má i Git příkaz blame:
$ git blame SOUBOR
který označí každou řádku v souboru, kdo a kdy ji naposledy změnil. Narozdíl od mnoha ostatních verzovacích systémů, tato operace pracuje offline čtouc pouze z lokální kopie na disku.
Osobní zkušenost
V centralizovaných verzovacích systémech je změna historie těžká a mohou ji dělat pouze administrátoři. Klonování, větvení a slučování, ani jedno z toho není možné bez připojení k síti. Stejně tak jsou na tom takové základní operace jako prohlížení si historie, nebo nahrávání změny. V některých systémech musejí uživatelé mít připojení k síti jen pro to, aby si mohli prohlédnout jejich vlastní změny nebo začít upravovat soubor.
Centralizované systémy znemožňují pracovat offline a jsou náročnější na síťovou infrastrukturu, obzvláště roste-li počet vývojářů. Nejdůležitější je, že všechny operace jsou do nějakého bodu pomalejší. Obvykle do bodu, kdy se uživatelé vyhýbají pokročilejším příkazům, pokud to není nezbytné. V těch nejhorších případech je toto pravda i pro tu nejzákladnější sadu příkazů. Poněvadž uživatelé musí spouštět pomalé příkazy, produktivita klesá kvůli přerušování práce.
Tohle mám z první ruky. Git byl prvním verzovacím systémem, který jsem používal. Velmi rychle jsem na něj přivykl a bral většinu funkcí jako samozřejmost. Prostě jsem si myslel, že ostatní systémy jsou podobné: vybrat si verzovací systém by nemělo být o moc jinačí než zvolit textový editor nebo webový prohlížeč.
Později jsem byl šokován, když jsem byl přinucen používat centralizovaný systém. S Gitem na mém často padavém internetovém připojení záleželo jen málo, ale vývoj se stal neúnosným, když muselo být stejně spolehlivé jako můj lokální disk. Navíc jsem se přistihl při tom, jak se vyhýbám jistým příkazům kvůli latencím s nimi spojeným, které mi bránily pracovat tak, jak jsem zvyklý.
Když jsem musel spustit pomalý příkaz, přerušení toku myšlenek způsobilo neúměrnou škodu mé práci. Když jsem čekal na server, než komunikace s ním skončí, dělal jsem něco jiného, abych zabil čas, jako napříkad kontrolu e-mailů nebo psané dokumentace. Až jsem se časem vrátil ke své původní úloze, příkaz byl již dávno hotov a musel jsem mrhat časem na to, abych si vzpomněl, co jsem to vlastně dělal. Lidem moc nejde měnit kontext (ang. context switching).
Objevil se taky zajímavý efekt tragédie obecní pastviny: tuše zahlcení sítě si jednotlivci vzali na různé operace větší šířku pásma, než byla potřeba, aby minimalizovali budoucí prodlevy. Kombinací těchto snah si jednotlivci zabírali ještě větší šířku pásma, aby zabránili budoucím ještě delším prodlevám.
Tento článek je překladem páté kapitoly – Lessons of History – z GitMagic od Bena Lynna. V další kapitole budou popsány různé triky, které se vám v Gitu mohou hodit.