aragost Trifork: Mercurial Kick Start Exercises


Základy Mercurialu

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

Instalace a konfigurace

Mercurial lze použít na mnoha platformách, včetně Windows, Mac OS X a GNU/Linux:

Windows:

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
Linux:

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.

Mac OS X:
Doporučujeme instalovat MacHg, čímž získáte dobrou, rychlou a nativní klientskou klientskou aplikaci s vlastním Mercurialem. Pokud již používáte TortoiseHg ve Windows, tedy vězte, že jej nyní také můžete instalovat na Mac OS X .

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ý.

Základní příkazy

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ření repozitáře

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ření changesetu

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í.

Prohlížení historie

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:

alice-log.png

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 @.

Putování časem

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.

Cvičení

  1. Použijte hg init pro vytvoření repozitáře kick-start.

  2. 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ů.

  3. 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í.

  4. Proveďte několik dalších komitů.

  5. 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.

  6. 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.

  7. 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.

Spolupráce s ostatními

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í.

Klonování

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-log.png

Porovnávání klonů

Klon ví odkud pochází a Bob se může ptát, zda nejsou v Alenčině kopii changesety, které dosud nemá:

bob$ hg incoming
comparing with /home/alice/example
searching for changes
no changes found

Může se také zeptat Mercurialu, které jeho changesety Alenka nemá:

bob$ hg outgoing
comparing with /home/alice/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.

To dává smysl: Bob vytvořil ve svém klonu dva nové changesety a Alena je zatím nemá.

Přenášení changesetů

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:

alice-pull.png

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.

Odlišné linie vývoje

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:

alice-heads.png

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:

alice-merged.png

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)
bob-pull.png

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í.

Cvičení

  1. Vytvořte repozitář a proveďte v něm několik komitů.

  2. 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.

  3. 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.

  4. 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ů.

  1. 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.

  2. 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:

  1. 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ář.

  2. 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.

Shrnutí

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:

A příkaz nejdůležitější: