Moje odkazy
Obsah článku:
vydáno: 24. 11. 2011 21:58, aktualizováno: 10. 8. 2020 10:42
Předchozí díl tohoto seriálu byl věnovaný teorii distribuovaných verzovacích systémů. Dnes se konečně podíváme prakticky na jeden z nich – Mercurial (zkráceně Hg). Kromě úplných základů (přidávání souborů pod správu verzí, mazání, přejmenovávání…) si také ukážeme, jak se vracet ke starším verzím a jak si verze pro větší přehlednost označovat pomocí štítků.
Instalace programu je v GNU/Linuxových distribucích jednoduchá, použijeme balíčkovací systém a Mercurial si nainstalujeme např. jedním z těchto příkazů:
aptitude install mercurial # Debian/Ubuntu yum install mercurial # Fedora/RedHat
Příkaz pro práci s Mercurialem se jmenuje hg
a mělo
by nám fungovat doplňování a napovídání tabulátorem.
Např. napíšeme hg com
a stiskneme tabulátor a doplní se na hg commit
.
Globální nastavení Mercurialu se nachází v souboru /etc/mercurial/hgrc
.
Zde nic měnit nemusíme.
Jediné, co bychom si měli nastavit, je jméno v našem uživatelském konfiguračním souboru v ~/.hgrc
– sem zadáme něco jako:
[ui] username = Jméno Příjmení <jmeno@example.com>
Při commitování změn se pak toto použije jako jméno autora.
Kromě globálního a uživatelského konfiguračního souboru existuje i nastavení pro jednotlivá úložiště
– nachází se uvnitř nich v souboru .hg/hgrc
.
Dejme tomu, že píšeme článek, diplomku, nějaký svůj program/skript… přestože to nepotřebujeme nikde vystavovat nebo s někým sdílet, může se nám hodit verzovací systém. Možná se vám už taky někdy stalo, že jste něco přepsali a pak jste si vzpomněli, že formulace, kterou jste tam měli ještě před pár dny byla lepší. Nebo třeba jen chcete sledovat, jak vám jde práce hezky od ruky – vidět kolik textu kdy přibylo. Místo toho, abychom si každý týden/den kopírovali vytvořené soubory do nějak pojmenovaného adresáře je začneme verzovat – je to snadné, takže se nemusíme bát použít verzování i v banálních případech – není to pověstný kanón na vrabce – je to naopak jednodušší a můžeme u toho udělat méně chyb, než kdybychom soubory zálohovali/verzovali ručně kopírováním.
Otevřeme si terminál a vstoupíme do složky se soubory, které chceme verzovat, a zadáme příkaz:
$ hg init
Příkaz nic nevypíše (to je v pořádku) a vytvoří nové mercurialové úložiště
– vznikne adresář .hg
.
Úložiště je zatím prázdné.
Následujícím příkazem zjistíme aktuální stav:
$ hg status ? index.xhtml ? soubor.txt ? video.ogv
Otazník před názvem souborů znamená, že tyto soubory nejsou zatím verzované. Soubory si tedy přidáme pod správu mercurialu:
$ hg addremove adding index.xhtml adding soubor.txt adding video.ogv $ hg status A index.xhtml A soubor.txt A video.ogv
U souborů je teď příznak A
a to znamená, že jsou čerstvě přidané
a když zadáme hg commit
uložíme je trvale do mercurialu jako novou verzi.
Jenže teď si uvědomíme, že video verzovat nechceme – naštěstí se nic nestalo a můžeme se vrátit zpět:
$ hg revert --all forgetting index.xhtml forgetting soubor.txt forgetting video.ogv $ hg status ? index.xhtml ? soubor.txt ? video.ogv
A přidáme soubory raději jednotlivě – jen ty, které skutečně chceme verzovat:
$ hg add soubor.txt index.xhtml $ hg status A index.xhtml A soubor.txt ? video.ogv
Abychom na to nemuseli pokaždé myslet, vytvoříme si seznam souborů, které se nemají verzovat:
$ echo "video.ogv" >> .hgignore $ hg add .hgignore $ hg status A .hgignore A index.xhtml A soubor.txt
Nežádoucí soubory nám už nepřekáží ve výpisu hg status
a nestane se nám, že bychom je omylem postoupili do úložiště
(to bychom museli udělat záměrně: hg add video.ogv
, ale hromadné přidání/odebrání pomocí hg addremove
je bude ignorovat).
Samotný soubor .hgignore
je rovněž verzovaný – to je dobré jednak proto, že seznam se může v čase měnit
(např. soubory, které jsme chtěli ignorovat v prvních verzích teď chceme verzovat a naopak),
a jednak proto, že tento seznam se bude šířit společně s ostatními soubory – když uděláme push
nebo pull
, seznam ignorovaných souborů bude všude stejný.
Soubor .hgignore
může obsahovat jak regulární výrazy, tak obyčejné vzory jako v shellu – např. *~
.
Vždy jeden vzor na jednom řádku.
Obvykle budeme ignorovat celé adresáře jako dist/*
, build/*
a záložní soubory.
Ještě jednou si pro jistotu vypíšeme hg status
a když jsme s výsledkem spokojeni, můžeme vytvořit svoji první verzi:
$ hg commit
Otevře se nám textový editor, do něj napíšeme popis změn, které daná verze přináší – první řádka by měla být výstižná a stručná (zobrazuje se v krátkých výpisech historie) a na dalších řádcích se můžeme rozepsat klidně trochu víc. Hodně záleží co a pro koho verzujeme – když si verzujeme jen tak pro sebe, asi tam nebudeme psát moc dlouhé romány, ale když pracujeme v týmu, je dobré napsat srozumitelný popis změn (ne každý chce číst zdrojový kód a dělat si diff), nějaké vzkazy pro člověka, který bude dělat revizi naší práce, a pokud používáme systém na správu požadavků/chyb, měli bychom zadat i čísla úkolů, které jsme zde řešili (obvykle se před číslo dává # a systémy jako Trac z toho pak umí udělat odkaz na daný úkol). Platí zde podobné zásady jako pro komentáře v kódu: neměli bychom psát až tak, co jsme udělali (např. „do metody xyz() jsem přidal a = b + 10;“ – to je vidět z diffu, tudíž zbytečná informace), ale proč jsme to udělali a jaký to má smysl (např. „oprava chyby: zdvojování teček v protokolu SMTP“ a ideálně i její #číslo).
Zprávu nemusíme psát do editoru a můžeme ji uvést na příkazové řádce:
$ hg commit -m "Popis změn…"
Také můžeme postoupit jen konkrétní soubory, přestože jich bylo přidáno či změněno více:
hg commit index.xhtml soubor.txt
A když si to úplně na poslední chvíli rozmyslíme, stačí ukončit editor a žádnou zprávu nepsat – Mercurial to pochopí:
abort: empty commit message
a žádnou verzi nevytvoří.
Kromě zdrojových kódů nebo různých vlastních souborů můžeme tímto způsobem verzovat i konfiguraci našeho počítače v /etc
:
# cd /etc # hg init # chown root:root .hg/ # chmod 700 .hg/ # hg addremove # hg commit -m "prvotní verze konfigurace"
Na chmod
a chown
nesmíme zapomenout
– jinak by se mohlo stát, že se k souborům,
které může normálně číst jen superuživatel, dostane přes verzovací systém i někdo jiný.
Verze pak můžeme vytvářet při každé ruční změně konfiguračních souborů,
případně si tento proces automatizujeme programem etckeeper
(verzuje /etc
v pravidelných intervalech a při aktualizacích balíčků).
Zatím jsme se pohybovali pouze mezi pracovní kopií (normální soubory v adresáři, které upravujeme)
a místním úložištěm (uvnitř adresáře .hg
, spravované Mercurialem, zde nic ručně neupravujeme, maximálně konfigurační soubor .hg/hgrc
)
Pro připomenutí jeden obrázek z minula:
Jelikož se obvykle verzují důležité a pracně vytvořené věci, bylo by škoda o ně přijít třeba při havárii pevného disku. Proto je potřeba zálohovat – s verzovacím systémem to jde velice snadno.
Přepneme se do adresáře na externím nebo třeba síťovém disku a naklonujeme sem svoje dosavadní úložiště:
$ cd /mnt/externí-disk/ $ hg clone ~/diplomka/ destination directory: diplomka updating to branch default 3 files updated, 0 files merged, 0 files removed, 0 files unresolved $ cd diplomka
Máme teď kompletní kopii náší práce včetně všech předchozích verzí. Vazba na původní úložiště (odkud jsme klonovali) zůstává zachována – najdeme ji v konfiguračním souboru:
$ cat .hg/hgrc [paths] default = /home/franta/diplomka
Díky tomu nám budou fungovat příkazy hg push
a hg pull
,
pomocí kterých můžeme „synchronizovat“ obě úložiště.
Můžeme tedy pracovat střídavě v obou úložištích a třeba i na více počítačích současně (nosit si externí disk s sebou).
A zároveň máme ve věcech pořádek – vždy víme, která verze je aktuální, a nestane se nám, že bychom si omylem soubory přepsali nějakou špatnou verzí
– i kdyby se nám to stalo, vždy se můžeme vrátit zpět a chybu napravit.
Před stažením změn z druhého úložiště (hg pull
) se můžeme podívat, jaké změny čekají na druhé straně:
$ hg incoming comparing with /home/franta/diplomka searching for changes changeset: 1:87df11cb506b tag: tip user: František Kučera <franta-hg@example.com> date: Thu Nov 24 15:18:08 2011 +0100 summary: Druhá verze
Vidíme, že provedením hg pull
si stáhneme jednu sadu změn, víme kdo ji vytvořil, kdy a vidíme její popis.
Pustíme tedy hg pull
a následně hg update
(tím aktualizujeme pracovní kopii na poslední verzi).
Obdobným způsobem fungují příkazy hg push
a hg outgoing
,
akorát v opačném směru.
Příkazem hg log
si vypíšeme stručnou historii celého úložiště:
$ hg log changeset: 1:87df11cb506b tag: tip user: František Kučera <franta-hg@example.com> date: Thu Nov 24 15:18:08 2011 +0100 summary: Druhá verze changeset: 0:01c1e32a34f5 user: František Kučera <franta-hg@example.com> date: Thu Nov 24 14:13:04 2011 +0100 summary: Moje první verze
A příkazem hg annotate index.xhtml
zjistíme, z jaké verze pocházejí jednotlivé řádky daného souboru
– každý řádek začíná krátkým číslem verze (v našem případě by to byly 0 a 1, protože máme zatím dvě verze a čísluje se od nuly).
Díky tomu víme, jak je který řádek starý a který autor ho má na svědomí.
Užitečné je to zvlášť při vývoji softwaru – když nějaké části kódu nerozumíme, můžeme jít přímo za konkrétním kolegou a zeptat se, co tím myslel.
Ostatně v Subversionu se tato funkce jmenuje svn blame
(obvinit).
Pro podrobnější výpis použijeme hg annotate
s volbami --changeset
a --user
.
Práce na příkazové řádce je sice fajn, ale některé věci jsou přeci jen lépe vidět v GUI. K pokročilejším externím nástrojům se dostaneme v příštím díle – zatím si vystačíme se samotným Mercurialem. Ten disponuje vestavěným HTTP serverem, který zpřístupní dané úložiště.
$ cat ~/bin/hg-náhled #!/bin/bash konqueror http://localhost:8000/?style=gitweb & hg serve -p 8000
Pomocí tohoto skriptu si spustíme www prohlížeč a HTTP server a podíváme se, co je v daném úložišti. Přehledně vidíme všechny verze, můžeme si rozkliknout jednotlivé změny a rozdíly v souborech. K dispozici je i vyhledávání, stromový pohled a další funkce.
Tím můžeme zkoumat změny, které byly již postoupené do úložiště. Pro porovnání pracovní kopie s úložištěm se nám bude hodit následující skript:
$ cat ~/bin/hg-porovnej #!/bin/bash hg diff | kompare -o -
Tento příkaz je dobré spustit před hg commit
a podívat se, co přesně se chystáme odeslat – vidíme nové řádky (modře), změněné (červeně) a smazané (zeleně).
Při přesouvání souborů je lepší použít příkazů Mercurialu – místo:
$ mv soubor.txt Soubor.TXT $ hg status ! soubor.txt ? Soubor.TXT
použijeme raději:
$ hg mv soubor.txt Soubor.TXT $ hg status A Soubor.TXT R soubor.txt
a soubor se nejen přesune/přejmenuje, ale zároveň se i správně připraví na postoupení do úložiště:
původní soubor je ve stavu R
(ke smazání) a nový ve stavu A
(přidání).
Mercuril si navíc pamatuje, že Soubor.TXT
vznikl ze soubor.txt
(Soubor.TXT
má nastaveno parent 1 soubor.txt@87df11cb506b
),
takže je historie zachována a lze sledovat změny (např. pomocí hg annotate
) zpětně i do dob,
kdy se soubor ještě jmenoval jinak.
Což by nemuselo fungovat, kdybychom jeden soubor jen smazali a druhý vytvořili bez vědomí Mercurialu
(pak by to byly dva úplně jiné nezávislé soubory).
Naštěstí Mercurial je celkem inteligentní, takže se dokáže vypořádat i s tím,
když soubor přejmenujeme bez něj a pak dáme hg addremove
:
$ hg addremove removing soubor.txt adding Soubor.TXT recording removal of soubor.txt as rename to Soubor.TXT (100% similar)
Nicméně jistější je ten první způsob (hg mv
),
protože pak můžeme soubor v jednom kroku přejmenovat i měnit jeho obsah
a přesto se neztratí informace, že jde o tentýž soubor.
Pokud soubor smažeme obyčejně pomocí rm
, Mercurial si všimne, že chybí:
$ rm Soubor.TXT $ hg status ! Soubor.TXT
Ale při následujícím hg commit
se toto smazání nijak neprojeví.
Je to stejné, jako když vytvoříme nový soubor a nepřidáme ho pod správu verzí pomocí hg add
.
Aby se smazání uložilo v rámci dané verze, musíme dát hg remove
(místo obyčejného smazání, nebo dodatečně po něm):
$ hg rm Soubor.TXT $ hg status R Soubor.TXT
Při postoupení změn se tento soubor v úložišti smaže (staré verze tam samozřejmě zůstanou a můžeme se k nim kdykoli vrátit).
Jedním z důvodů, proč verzovací systémy používáme, je cestování v čase – můžeme se vracet ke starým verzím souborů.
Pomocí hg up -r ?
se vrátíme k vybrané verzi (její číslo zjistíme z hg log
):
$ hg up -r 0 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
A pomocí hg up
bez parametru aktualizujeme pracovní kopii na poslední verzi, která je v úložišti.
Pro označení verzí můžeme samozřejmě používat i hashe:
$ hg up -r 7f906c7d62cc 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
Následujícím příkazem pak zjistíme, kde v čase se právě nacházíme:
$ hg identify 7f906c7d62cc
Odkazovat se na verze pomocí podivných čísel je dost nepohodlné.
Proto existují tzv. štítky, kterými si můžeme jednotlivé verze označit.
Dejme tomu, že verze 4:7f906c7d62cc
je nějakým způsobem zajímavá (např. program v této verzi jde nejen zkompilovat, ale i spustit),
tak si ji pojmenujeme:
hg tag --rev 4 "tohle funguje"
A pak se na ni můžeme odkazovat tímto jménem místo čísla:
hg up "tohle funguje"
Všechny štítky si vypíšeme příkazem:
$ hg tags tip 8:89a449ab3cb0 tohle funguje 4:7f906c7d62cc třetí verze 3:07708ca1e12f
A kromě toho je i přehledně vidíme ve webovém rozhraní.
Dnešní úvod do Mercurialu byl pro některé možná příliš podrobný a zdlouhavý, ale článek by měl sloužit i těm, kteří zatím žádný verzovací systém nepoužívali a chtěli by s tím začít. Příště se budeme věnovat více týmové práci (stahování ze vzdálených úložišť i zpřístupnění vlastního úložiště pomocí různých protokolů ostatním) a práci s větvemi. Také si ukážeme některé GUI programy, protože příkazová řádka není všechno.
Témata: [verzovací systémy] [softwarové inženýrství]
Tento článek zatím nikdo nekomentoval