Začneme se základy Mercurialu. Doporučujeme, abyste si sami zkoušeli to, co provádí Alenka a Bob. Jejich terminály jsou barevně odlišeny. Alenčin je světle okrový, Bobův petrolkově zelený a terminál Karly (přijde na scénu později) je fialkový.
Později uvedeme několik cvičení na stejné téma, které jsme předtím prováděli s Alenkou a Bobem.
Obsah
Mercurial lze použít na mnoha platformách, včetně Windows, Mac OS X a GNU/Linux:
Stačí nainstalovat program TortoiseHg, který rovněž obsahuje kompletní instalaci Mercurialu včetně řady grafických nástrojů pro práci s repozitářem.
Po instalaci ve Windows získáme kontextové menu s přístupem k jednotlivým grafickým nástrojům. Z příkazového řádku konzoly budeme moci volat přikazy hg a thg. Příkaz thg otevře grafický nástroj TortoiseHg.
Tip
V příkladech se používá několik Unixových příkazů, které můžete snadno nahradit ekvivalenty pro příkazový řádek ve Windows Prompt:
ls -> dir cat -> type echo "abc" -> echo abc
Instalujte Mercurial ze zdroje nebo pomocí správce paketů. Instalace ze zdroje je snadná: stáhnete poslední stálou verzi, rozbalite ji někde a přikážete make local. Provedete symlink skriptu hg k adresáři ve vaší cestě (PATH) a jste hotovi.
Pro instalaci TortoiseHg budete potřebovat pojítka (bindings) na PyQT. Jsou k disposici ve většině distribucí. Potom následujte tyto pokyny. Tak získáte program thg.
V našich příkladech budeme používat příkazový řádek ale někdy si stav repozitáře prohlédneme ve Verpánku (Workbench) programu TortoiseHg.
Po instalaci Mercurialu zkuste zadat hg version:
alice$ hg version Mercurial Distributed SCM (version 2.2) (see http://mercurial.selenic.com for more information) Copyright (C) 2005-2012 Matt Mackall and others This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Je zajisté vynikající, vidíte-li číslo verze větší než 2.2. Mercurial 1.4 je rovněž pro naše účely možný ale vždy doporučujeme používat nejnovější dosažitelnou verzi pro vaši platformu. Svou instalaci si můžete ověřit zadáním:
alice$ hg debuginstall Checking encoding (UTF-8)... Checking installed modules (/usr/share/pyshared/mercurial)... Checking templates (/usr/lib/python2.7/dist-packages/mercurial)... Checking commit editor... Checking username... no username supplied (see "hg help config") (specify a username in your configuration file) 1 problems detected, please check your install!
Jméno uživatele normálně po instalaci chybí. Zadáme jej vložením do konfiguračního souboru. V Unixových systémech to je $HOME/.hgrc, ve Windows se tento soubor nazývá %HOME%\Mercurial.ini (viz hg help config pro všechny lokace nebo použijte thg userconfig). Vytvořte soubor s tímto obsahem:
[ui] username = Firstname Lastname <example@example.net>
kde si však dosadíte své jméno. My použijeme nejprve Alice <alice@example.net>. Průběh hg debuginstall by nyní již měl být bezproblémový.
Nyní si stručně probereme základní příkazy. Jsou všechny podobné příkazům ze starších systémů jako je Subversion nebo CVS. Používáte-li TortoiseHg ve Windows, můžeté také nahlédnout do jeho stručného úvodu.
Vytvořte repozitář příkazem hg init:
alice$ ls alice$ hg init example alice$ ls example
Příkaz hg init vytvořil novou složku, která je kořenovým adresářem repozitáře (složky .hg). Složku example jsme také mohli vytvořit předem a v ní přikázat pouze hg init.
Složku example podrobíme inspekci:
alice$ cd example alice$ ls -a . .. .hg
Jak můžete vidět, nový repozitář je prázdný až na složku .hg. Sem Mercurial vkládá historii changesetů a několik dalších administrativních souborů.
Vytvořme soubor a zeptejme se Mercurialu na stav repozitáře:
alice$ echo "Hello World" > hello.txt alice$ hg status ? hello.txt
Odpověď nám říká, že hello.txt není Mercurialu znám, to jest, není jím zatím sledován. Soubor musíme přidat:
alice$ hg add hello.txt alice$ hg status A hello.txt
což změní statut na “A” (od slova “added” neboli přidán). Soubor zatím není uložen v historii repozitáře - provedeme to příkazem “commit”:
alice$ hg commit -m "First version of hello"
Žádná odezva znamená úspěch —– Mercurial se nevtírá, pokud nemusí.
Nově vytvořený changeset si můžeme prohlédnout příkazem hg log:
alice$ hg log changeset: 0:d312da7770f4 tag: tip user: Alice <alice@example.net> date: Wed Mar 10 20:10:05 2010 +0000 summary: First version of hello
Právě jsme úspěšně provedli první commit v Mercurialu. Upravme soubor a požádejme Mercurial aby nám ukázal, jak se soubory v pracovní kopii liší od poslední revize:
alice$ echo "Goodbye!" > hello.txt alice$ hg diff diff -r d312da7770f4 hello.txt --- a/hello.txt Wed Mar 10 20:10:05 2010 +0000 +++ b/hello.txt Mon Mar 10 20:10:10 2010 +0000 @@ -1,1 +1,1 @@ -Hello World +Goodbye!
Můžeme změnit názor a vrátit soubor do předchozího stavu:
alice$ hg revert hello.txt alice$ cat hello.txt Hello World
Proveďme jinou změnu našeho souboru:
alice$ echo >> hello.txt alice$ echo "by Alice." >> hello.txt alice$ hg commit -m "Added author."
Nyní požádáme Mercurial aby provedl anotaci souboru, to jest ukázal, kdy byl který řádek naposledy měněn:
alice$ hg annotate hello.txt 0: Hello World 1: 1: by Alice.
Zcela správně, Mercurial nám říká, že první řádek je z revize 0 a že oba následující řádky jsou z revize 1. TortoiseHg nabízí velmi pěkný interaktivní anotační nástroj, viz thg annotate.
Anotace souborů je neocenitelná pomoc při lokalizaci chyb; při zjištění chyby můžeme příkazem annotate určit, kdy byla tato chyba zavedena do souboru. Potom pomocí hg log získat příslušející komentář komitu, který může vysvětlit, o co se komitent pokoušel, když měnil řádek.
alice$ hg log -r 1 changeset: 1:2e982bdc137f tag: tip user: Alice <alice@example.net> date: Wed Mar 10 20:15:00 2010 +0000 summary: Added author.
Oba changesety v našem repozitáři tvoří posloupnou řadu. To se nejlépe uvidí pomocí standartní extenze graphlog. Povolením dostupných extenzí lze velmi rozšířit schopnosti Mercurialu. Alenka si extenzi graphlog jednoduše uvolní tak, že do svého konfiguračního souboru přidá zápis:
[extensions] graphlog =
Může potom zadat hg help graphlog, aby se o extenzi dozvěděla více. Extenze umožňuje Alence použít další příkaz:
alice$ hg glog @ changeset: 1:2e982bdc137f | tag: tip | user: Alice <alice@example.net> | date: Wed Mar 10 20:15:00 2010 +0000 | summary: Added author. | o changeset: 0:d312da7770f4 user: Alice <alice@example.net> date: Wed Mar 10 20:10:05 2010 +0000 summary: First version of hello
Ukazuje se, že changeset 2e982bdc137f je dítě changesetu d312da7770f4. Totéž lze zjistit příkazem thg log:
Soubory pracovní kopie jsou vždy synchronizovány s určitým changesetem. Aktuální changeset se v thg log pozná podle zakroužkovaného kolečka v přehledu changestů. Ve výstupu z hg glog je rodičovská revize pracovní kopie označena znakem @.
Rodičovskou revizi pracovní kopie lze změnit příkazem hg update. To se hodí, když chceme přezkoušet starší verzi svého skriptu, například když chceme otestovat starší verzi svého softwaru v novém operačním systému. Aby si prohlédla svůj starší soubor, přesune se Alenka zpět k revizi 0. Nejprve použije příkaz hg parents, aby zjistila, kde se ve větvi grafu nachází:
alice$ hg parents changeset: 1:2e982bdc137f tag: tip user: Alice <alice@example.net> date: Wed Mar 10 20:15:00 2010 +0000 summary: Added author.
a potom provede potřebný skok:
alice$ hg update 0 1 files updated, 0 files merged, 0 files removed, 0 files unresolved alice$ hg parents changeset: 0:d312da7770f4 user: Alice <alice@example.net> date: Wed Mar 10 20:10:05 2010 +0000 summary: First version of hello
Její soubor je nyní opět ve stavu staré verze:
alice$ cat hello.txt Hello World
ale ta novější verze není zapomenuta. Mercurial z ní umí snadno číst:
alice$ hg cat -r 1 hello.txt Hello World by Alice.
Příkaz hg cat je užitečný pro prohlížení starších verzí souborů místo použití hg update pro přenesení celého pracovního adresáře ke starší verzi. Přechod zpátky k poslední (tip) revizi je snadný, zadáme pouze hg update bez argumentu:
alice$ hg update 1 files updated, 0 files merged, 0 files removed, 0 files unresolved alice$ cat hello.txt Hello World by Alice.
Použijte hg init pro vytvoření repozitáře kick-start.
Vstupte do kick-start a vytvořte několik souborů. Přihlašte je k přidání příkazem hg add. Můžete také vyzkoušet přikaz hg-revert pro zrušení některého z přidaných souborů.
Použijte hg commit pro předání souborů do repozitáře. Nezadáte-li komentář komitu, otevře se textový editor pro jeho zadání.
Proveďte několik dalších komitů.
Použijte hg update pro aktualizaci pracovního adresáře ke starší revizi. Všimněte si výstupu hg parents a porovnejte jej s výstupem hg tip.
Zkuste provést komit, je-li rodičem pracovního adresáře starší revize. Dozvíte se, že vytváříte nové “čelo”. Čelo (head) je changeset bez dětí. K zobrazení čel v repozitáři použijte příkaz hg heads nebo si prohlédněte výstup z thg log.
Pamatujte si, že rodičovská revize pracovní kopie se stává rodičovskou revizí příštího changesetu — ergo pracovní kopie je potenciálně dalším changesetem.
Více čel představuje více odlišných linií vývoje. Normálně tyto linie sloučíte příkazem hg merge. Pokud se změny nepřekrývají, bude sloučení snadné. V opačném případě budete muset vyřešit konflikty - více o tom později.
Všimněte si, jak příkaz hg parents po příkazu hg merge vytiskne dva changesety. Znamená to, že příští komit bude mít dva rodiče, které posléze uvidíte v hg glog nebo v thg log.
Alenčin repozitář se v této chvíli nachází v jejím domovském adresáři. Její spolupracovník Bob chce na projektu rovněž pracovat. Jeho příkazy budeme uvádět na petrolejkovém pozadí.
V těchto příkladech necháme Alenku sdílet s Bobem společný lokální systém souborů. Může to docela dobře být systém v podnikové síti. Změny lze také sdílet prostřednictvím připojení SSH a HTTP - dokonce je lze posílat jako přílohu emailů nebo přenášet na USB nosičích. Přitom se běžně používá komprimovaný binární formát vytvořený příkazem hg bundle. V našem případě použije Bob příkaz hg clone s relativní cestou k Alenčině repozitáři:
bob$ hg clone ../alice/example destination directory: example updating to branch default 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
Bob má nyní svou vlastní nezávislou kopii Alenčina repozitáře. Takových kopií si může pořídit libovolné množství. Ve svém klonu si může vytvářet vlastní changesety bez vlivu na Alenčiny soubory. Provede několik změn:
bob$ cd example bob$ echo "Goodbye!" > goodbye.txt bob$ echo >> goodbye.txt bob$ echo "by Bob." >> goodbye.txt bob$ hg add adding goodbye.txt bob$ hg commit -m "My goodbye file." bob$ echo >> hello.txt bob$ echo "not by Bob." >> hello.txt bob$ hg commit -m "Not my file."
Jeho repozitář má nyní čtyři changesety:
Bob nemá právo zápisu do souborů v Alenčině adresáři, takže nemůže zapsat své changesety do jejího repozitáře. Může ji ale informovat, že provedl změny klonu a požádat ji, aby si changesety od něho stáhla. Alenka to provede příkazem hg pull. Předtím si ale zapíše cestu k Bobově klonu do svého souboru .hg/hgrc. To je konfigurační soubor lokálního repozitáře. Zapíše:
[paths] default = /home/bob/example
čímž se stane Bobův klon implicitním cílem při provádění hg pull a jiných příkazů souvisejících se vzdáleným repozitářem. To, co bude staženo, uvidí Alenka příkazem hg incoming:
alice$ hg incoming comparing with /home/bob/example searching for changes changeset: 2:659410363fe0 user: Bob <bob@example.net> date: Thu Mar 11 10:00:00 2010 +0000 summary: My goodbye file. changeset: 3:51b2976b01e8 tag: tip user: Bob <bob@example.net> date: Thu Mar 11 10:03:00 2010 +0000 summary: Not my file.
Všimněte si symetrie výstupu u Aleny s výstupem Bobova hg outgoing. Alenka se rozhodne, že si Bobovy změny stáhne:
alice$ hg pull pulling from /home/bob/example searching for changes adding changesets adding manifests adding file changes added 2 changesets with 2 changes to 2 files (run 'hg update' to get a working copy)
Poslední řádek (proveďte ‘hg update’ pro vytvoření pracovní kopie) napovídá Aleně, že se rodičovská revize její pracovní kopie dosud nezměnila:
Rodičovská revize je nezměněna, protože Alena mohla právě pracovat na vlastní změně a o okamžitou aktualizaci svého repozitáře nemusela mít zájem. Nyní ale zájem má:
alice$ hg update 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
Je běžné, že téměř vždy po hg pull chceme provést hg update a pro tuto potřebu existuje zkrácení: příkaz hg pull -u zařídí automaticky aktualizaci po stažení nových změn.
Alenka poněkud změní svůj soubor hello.txt:
alice$ hg diff diff -r 51b2976b01e8 hello.txt --- a/hello.txt Thu Mar 11 10:03:00 2010 +0000 +++ b/hello.txt Thu Mar 11 11:00:00 2010 +0000 @@ -1,4 +1,4 @@ -Hello World +Hello, Wonderful World! by Alice. alice$ hg commit -m "Happy, happy!"
V témže čase vytvoří Bob nový soubor:
bob$ echo "Welcome!" > welcome.txt bob$ hg add adding welcome.txt bob$ hg commit -m "Welcome file."
Jak Alena tak Bob mají nyní changeset, který je dítětem 51b2976b01e8, ale nevědí o tom, protože žádný z nich změny nikam neposílal ani odnikud nestahoval. Alenka se rozhodne, že si stáhne změny od Boba:
alice$ hg pull pulling from /home/bob/example searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) (run 'hg heads' to see heads, 'hg merge' to merge)
Co se stalo? Jak už bylo zmíněno, “čelní” changeset je changeset bez dětí. Protože Alenka a Bob provedli různé změny, založené na 51b2976b01e8, jsou nyní v repozitáři dva changesety bez potomků. Tato čela tvoří odlišné linie vývoje, jak můžeme vidět v thg log:
Tyto dvě linie vývoje sjednotíme příkazem hg merge:
alice$ hg merge 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit)
Konflikty nebyly žádné, protože Alenka a Bob editovali různé soubory. Po hg merge má pracovní kopie dva rodiče — podle toho poznáte, že merge přišlo ke slovu, pokud jste odběhli od počítače a ztratili jste nit. Sloučení zatím nebylo komitováno, takže ještě můžeme sloučený changeset případně měnit. Přesto, že nebyly provedeny konfliktní změny, mohou se vyskytnout sémantické konflikty, které Mercurial neumí poznat. Je tedy vhodné po sloučení provést ověření (suit test). V našem případě provede Alenka komit:
alice$ hg commit -m "Merge with Bob."
Obě čela jsou nyní spojena v jednom changesetu:
Sloučený changeset obsahuje změny z obou svých rodičovských revizí. Když si tento changeset Bob stáhne, přetáhne si vlastně changesety dva, které ale nemusí slučovat:
bob$ hg pull pulling from /home/alice/example searching for changes adding changesets adding manifests adding file changes added 2 changesets with 1 changes to 1 files (run 'hg update' to get a working copy)
Všimněte si rozdílného umístění stejných changesetů v Alenčině a Bobově repozitáři. Changesety si vždy uchovávají své ID (dlouhé hexadecimální číslo) ale mohou změnit číslo revize, které závisí na pořadí, ve kterém byl changeset přidán do repozitáře. Stahují-li si dva lidé změny navzájem, může snadno dojít k tomu, že stejný changeset má různá čísla revizí. Při komunikaci mimo vlastní repozitář bychom měli vždy pro určení changesetů používat jejich ID, při komunikaci uvnitř repozitáře lze bezpečně používat čísla revizí.
Vytvořte repozitář a proveďte v něm několik komitů.
Vytvořte dva klony svého repozitáře. Všimněte si, jak soubor .hg/hgrc zaznamenává umístění původního repozitáře. Příkaz hg paths vám ukáže definované cesty.
Do .hg/hgrc přidejte další cestu, například:
bob = /home/bob/test
Nyní budete moci provádět hg pull bob, hg outgoing bob, apod. Je vhodné si pro lidi, s nimiž často spolupracujeme, takové zkratky vytvořit.
Experimentujte se stahováním (pull) a posíláním (push) změn. Všimněte si, že se pracovní adresář nezmění pouhým přidáním změn do repozitáře. Rodičovská revize pracovní kopie se změní teprve po aktualizaci - hg update.
Mercurial podporuje spolupráci bez přímého spojení. Provádí se výměnou changesetů v tak zvaných svazcích. Svazek (bundle) je komprimovaná binární prezentace řady changesetů.
V jednom ze svých repozitářů vytvořte několik changesetů. Příkazem hg bundle --base X out.bundle vytvořte svazek v out.bundle, který obsahuje všechny changesety za zvoleným changesetem X. Záměrem je, abyste někde zaznamenal poslední známý changeset, sdílený vámi a cílem a použil jej jako bázi.
Přejděte do cílového repozitáře a příkazem hg unbundle importujte changesety ze svazku. V tomto příkladě máte nejspíše oba repozitáře v jednom počítači ale ve skutečnosti mohou oba repozitáře být v rozdílných počítačích, které nejsou nikdy přímo propojeny. V takovém případě můžete poslat svazky třeba emailem.
Svůj repozitář můžete publikovat také prostřednictvím HTTP:
Proveďte v repozitáři příkaz hg serve. Otevře se vám možnost přístupu k repozitářům ve webovém prohlížeči na lokální adrese http://localhost:8000. Zkuste spustit server jak pro Alenin, tak pro Bobův repozitář.
Zkuste si stáhnout (pull) changeset z repozitáře, publikovaného pomocí hg serve.
Nástroj hg serve je vhodný pro jednorázové publikování. Pro soustavné publikování je vhodnější např. Apache se skriptem hgweb.cgi, dodávaným spolu s Mercurialem.
Poznali jsme většinu základních příkazů Mercurialu a viděli jsme, jak se changesety ukládají do historie repozitáře. Důležité příkazy jsou tyto:
hg init: vytvořit nový repozitář
hg add: přihlásit soubor k přidání
hg diff: ukázat změny, které budou zahrnuty do následného komitu
hg commit: předat změny do aktuálního repozitáře
hg log: ukázat všechny změny v repozitáři
hg pull: stáhnout všechny změny z jiného repozitáře
hg push: poslat všechny vlastní změny do jiného repozitáře
hg merge: sloučit rozdílné linie vývoje
A příkaz nejdůležitější:
hg help