There are two main ways to organize branches in Mercurial: named branches and bookmarks. Bookmarks are used for short-term feature or topic branches whereas named branches are used for long-term branches.
Pojmenované větve jsou výhodné tehdy, když chcete zaznamenávat kontext, ve kterém byla každá revize vytvořena; název větve je vnořen do changesetu a můžete se na něj odkázat i po letech.
Jsou situace, kdy jsou pojmenované větve méně výhodné. Je to tehdy, když chcete jenom experimentovat nebo změnit název větve později.
V takových situacích jsou vhodnější záložky (bookmarks) Mercurialu.
Poznámka
Pro příklady v této sekci byste měl používat Mercurial 2.1 nebo novější.
Obsah
Záložky Mercurialu umožňují připojit nové jméno k changesetu. To se hodí při práci na několika tematech současně v různých ematicky zaměřených větvích.
Na rozdíl od tagů, které rovněž připojují nová jména k changesetům můžete záložky přemisťovat, přejmenovávat nebo smazat, neboť nejsou součástí historie; nevede se o nich trvalý záznam.
Představme si, že Alenka s Bobem a Karlou sestavují konverzační příručku. Budou shromažďovat fráze s různou tématikou a to někdy i ve stejném čase. Přirozeně si vytvoří několik vývojových větví, které vhodně označí záložkami.
Karla je šéf, takže začne vytvořením hlavního repozitáře projektu:
carla$ hg init phrases carla$ cd phrases carla$ echo "The Phrase Book Project" > README.txt carla$ hg add adding README.txt carla$ hg commit -m "Added a README"
Alenka s Bobem si vytvoří své vlastní klony; změny si budou vyměňovat (push/pull) s centrálním repozitářem Karly. Tento centrální repozitář může být na nějakém podnikovém serveru, zatímco jeho klony mohou být na počítačích Aleny a Boba. Pro zjednodušení ale všechny tři repozitáře umístíme v jednom souborovém systému.
Alenka s Bobem si tedy vytvoří klony repozitáře, který Karla právě vytvořila. U Alenky to vypadá nějak takto:
alice$ hg clone ../carla/phrases destination directory: phrases updating to branch default 1 files updated, 0 files merged, 0 files removed, 0 files unresolved alice$ cd phrases
Práci na frázích začnou shromažďováním frází v anglickém jazyce. Karla požádá Alenku aby připojila fráze, používané při zdravení:
alice$ echo "Hello!" > greetings.txt alice$ hg add adding greetings.txt alice$ hg commit -m "First greeting"
Karla náhle požádá Alenku, aby začala shromažďovat fráze z oblasti cestování. Alenka již pracuje na pozdravech a chce od sebe obě úlohy kvůli lepšímu přehledu oddělit. Vrátí se tedy (hg update -r1) k počáteční revizi a tam založí novou tematickou větev. Její repozitář zatím vypadá asi takto:
Revize se shromážděnými pozdravy je v pořadí druhá a nachází se na implicitní větvi default. Pro označení pracovní větve, tematicky věnované pozdravům, použije Alenka záložku (bookmark):
alice$ hg bookmark greetings
Changeset je nyní doplněn o označení greetings.
Název záložky nám poví příkaz hg bookmarks:
alice$ hg bookmarks * greetings 1:0b89bcda3dcf
Hvězdička (*) indikuje, že je záložka aktivní, což znamená, že se bude posouvat nahoru s přibývajícími revizemi a bude vždy v čele příslušné větve.
Alenka potřebuje vytvořit novou větev pro fráze z oblasti cestování. Větev by měla vyjít z revize 0, pročež Alenka tuto revizi učiní aktuální. Tím opustí větev se záložkou greetings, která se tímto deaktivuje:
alice$ hg update 0 0 files updated, 0 files merged, 1 files removed, 0 files unresolved alice$ hg bookmarks greetings 1:0b89bcda3dcf
Záložka zůstává na svém místě:
Alenka se k ní vždy může vrátit příkazem hg update greetings Nyní si ale vytvoří novou záložku traveling pro větev o cestování. Nová záložka se automaticky stává aktivní:
alice$ hg bookmark traveling alice$ hg bookmarks greetings 1:0b89bcda3dcf * traveling 0:4326a390b9b6
Aktivní záložku můžeme také vidět v TortoiseHg:
Záložka traveling bude nyní postupovat vzhůru s přibývajícími komity:
alice$ echo "When does the bus arrive?" > traveling.txt alice$ hg add traveling.txt alice$ hg commit -m "Started on traveling phrases" created new head
Nyní, když má Alenka záložky u revize 1 a 2, může používat názvy záložek všude tam, kde příkaz očekává číslo revize. Může tedy použít 1 (lokální číslo revize), 0b89bcda3dcf (globální ID changesetu) nebo greetings (název záložky):
alice$ hg log -r 1 changeset: 1:0b89bcda3dcf bookmark: greetings user: Alice <alice@example.net> date: Tue May 01 10:20:35 2012 +0000 summary: First greeting alice$ hg log -r 0b89bcda3dcf changeset: 1:0b89bcda3dcf bookmark: greetings user: Alice <alice@example.net> date: Tue May 01 10:20:35 2012 +0000 summary: First greeting alice$ hg log -r greetings changeset: 1:0b89bcda3dcf bookmark: greetings user: Alice <alice@example.net> date: Tue May 01 10:20:35 2012 +0000 summary: First greeting
Stejně jako tagy a názvy větví, pracují záložky se všemi příkazy — hg diff, hg merge etc.. Aktualizace vypadá takto:
alice$ hg update greetings 1 files updated, 0 files merged, 1 files removed, 0 files unresolved alice$ hg bookmarks * greetings 1:0b89bcda3dcf traveling 2:f1cd0e213eec alice$ hg update traveling 1 files updated, 0 files merged, 1 files removed, 0 files unresolved alice$ hg bookmarks greetings 1:0b89bcda3dcf * traveling 2:f1cd0e213eec
Všimněte si, jak se záložka aktualizované revize stává aktivní. Nový komit posune právě aktivní záložku, takže příkaz hg update traveling postačí k přípravě Alenčiny pracovní kopie pro práci ve větvi o cestování..
The distinguishing feature of bookmarks compared to named branches is that they exist outside of the history. Here, “history” means the immutable changesets that cannot be changed without changing their changeset IDs. Bookmarks are not stored in the changesets and they are not part of the computation of changeset IDs. They can therefore be deleted and renamed at will.
Představme si, že Alenka přidá novou záložku:
alice$ hg bookmark some-name alice$ hg bookmarks greetings 1:0b89bcda3dcf * some-name 2:f1cd0e213eec traveling 2:f1cd0e213eec
Nyní tuto záložku může přejmenovat:
alice$ hg bookmark --rename some-name new-name alice$ hg bookmarks greetings 1:0b89bcda3dcf * new-name 2:f1cd0e213eec traveling 2:f1cd0e213eec
Může ji také smazat:
alice$ hg bookmark --delete new-name alice$ hg bookmarks greetings 1:0b89bcda3dcf traveling 2:f1cd0e213eec
Nyní po smazané záložce nezůstala žádná stopa; nikdo nezjistí, že kdy existovala. Díky této vlastnosti jsou záložky vhodné pro sledování krátkých tematických větví, které nemusí být trvale zaznamenávány.
Po smazání záložky new-name další aktivní záložky nejsou. Jak ale začneme pracovat ve větvi traveling, stane se tato záložka aktivní:
alice$ hg update traveling 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
Zatímco Alenka pracovala, byla Karla agilní také. Aktualizovala soubor README s autory:
carla$ echo "by Alice, Bob, and Carla" >> README.txt carla$ hg commit -m "Added authors"
To znamená, že Alenčina tematická větev traveling by při posílání (push) Karle vytvořila další čelo (head). Tomu Mercurial implicitně zabrání:
alice$ hg push -r traveling pushing to /home/carla/phrases searching for changes abort: push creates new remote head f1cd0e213eec! (you should pull and merge or use push -f to force)
Aby si prověřila situaci, provede Alenka hg pull a repozitář si prohlédne v TortoiseHg:
alice$ hg pull pulling from /home/carla/phrases 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)
Posláním čela traveling by se ve vzdáleném repozitáři vytvořilo další čelo. Tím by vznikla nepřehledná situace, čemuž se Mercurial apriori brání, nicméně tento krok nevylučuje.
Při pokusu o push nám Mercurial napověděl, že můžeme vyslání vynutit opcí hg push -f. Normálně tato volba může svědčit o naší bezradnosti ale v tomto případě by Alenka měla dobrý důvod. Se svojí prací ve větvi traveling není ještě hotova a nechce, aby se změny ve větvi míchaly se změnami v hlavní vývojové lince.
Provede tedy Alenka vynucený push tematické větve do vzdáleného repozitáře:
alice$ hg push -f -r traveling pushing to /home/carla/phrases searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads)
Zde je velmi důležité, že Alenka omezí akci push jenom na větev, kterou chce poslat. Ve svém vlastním repozitáři má dvě tematické větve, které neexistují v Karlině repozitáři a opce -f říká Mercurialu, aby slepě vyslal (push) všechny odchozí changesety. Závorka (-1 heads) sděluje, že ve vzdáleném repozitáři vytvořilo jedno nové čelo.
Jak se věci mají z hlediska Karly? Ve svém repozitáři má nyní dvě anonymní čela (heads):
carla$ hg heads changeset: 2:f1cd0e213eec tag: tip parent: 0:4326a390b9b6 user: Alice <alice@example.net> date: Tue May 01 10:20:40 2012 +0000 summary: Started on traveling phrases changeset: 1:4b18431b805d user: Carla <carla@example.net> date: Tue May 01 10:20:45 2012 +0000 summary: Added authors
Říkáme anonymní, protože nejsou spojena s žádnými jmény — jsou v téže pojmenované větvi (default) bez záložek:
carla$ hg bookmarks no bookmarks set
Protože Alenka hodlá pokračovat v práci ve větvi, bylo by bývalo vhodné, kdyby svoji záložku byla poslala také. Původně (před Mercurialem 1.6) byly záložky striktně lokální ale nyní je možné i záložky vysílat a stahovat.
Záložky lze tedy mezi repozitáři vysílat a stahovat a to prostřednictvím řady protokolů, které Mercurial používá: SSH, HTTP a lokálně v souborovém systému. Možnost vysílat záložku se řídí stejnými pravidly jako při vysílání changesetů.
Alenka může snadno svůj předchozí omyl napravit. Nejprve použije hg outgoing -B (outgoing = odchozí) aby zjistila, které lokální záložky nejsou přítomné v repozitáři na serveru. Podobně by použila příkaz hg incoming -B (příchozí) aby zjistila, které ze záložek na serveru nemá ve svém repozitáři.
alice$ hg outgoing -B comparing with /home/carla/phrases searching for changed bookmarks greetings 0b89bcda3dcf traveling f1cd0e213eec
Má dvě lokální záložky, které nejsou na serveru. Při vyslání záložky musí použít opci -B místo -r:
alice$ hg push -B traveling pushing to /home/carla/phrases searching for changes no changes found exporting bookmark traveling
To přiměje Mercurial aby vyslal changeset označený jako traveling a exportoval záložku na server. Protože vlastní changeset větve již poslala, pošle se tam jenom její záložka. Karlin repozitář nyní vyhlíží mnohem úpravněji:
Stejným způsobem bude Alenka publikovat svou větev greetings, tentokrát ale řádným způsobem:
alice$ hg push -f -B greetings pushing to /home/carla/phrases searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) exporting bookmark greetings
Nyní, když Alenka zveřejnila svou záložku traveling, může pokračovat ve své práci ve větvi a pravidelně vysílat (push) své změny Karle. Tentokrát již nemusí používat opci -B:
alice$ echo "Two tickets, please!" >> traveling.txt alice$ hg commit -m "A ticket phrase" alice$ hg push pushing to /home/carla/phrases searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files updating bookmark traveling
Všimněte si sdělení updating bookmark traveling na konci výpisu. Říká Alence, že je záložka synchronizována v obou repozitářích.
Alenka může nyní pracovat společně s Bobem na tematické větvi. Nejprve si Bob stáhne větev od Karly. Záložku musí explicitně importovat při prvním stahování (pull) s použitím opce -B:
bob$ hg pull -B traveling pulling from /home/carla/phrases searching for changes adding changesets adding manifests adding file changes added 2 changesets with 2 changes to 1 files (run 'hg update' to get a working copy) importing bookmark traveling bob$ hg bookmarks traveling 2:0098cff6f6d8
Poznámka
Toto chování se změnilo v Mercurialu 2.3. Počínaje touto verzí je možné automaticky importovat vzdálené záložky spolu se stahovanými changesty. To znamená, že Bob může stáhnout od Karly všechny příchozí changesety a záložky prostým příkazem hg pull.
Bob nyní uloží stažené změny do svého repozitáře (hg update), provede nějakou změnu a pošle ji zpět do ústředního repozitáře:
bob$ hg update traveling 1 files updated, 0 files merged, 0 files removed, 0 files unresolved bob$ echo "Where is the train station?" >> traveling.txt bob$ hg commit -m "Asking for a train station" bob$ hg push pushing to /home/carla/phrases searching for changes note: unsynced remote changes! adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files updating bookmark traveling
Všimněte si, že záložka traveling byla na serveru automaticky aktualizována. Mercurial se snaží synchronizovat lokální a vzdálenou záložku při splnění jistého předpokladu: hg push provede aktualizaci vzdálené záložky, pokud je předkem lokální záložky. Pokud nejsou záložky konvergentní, je nutné je před vysláním (push) sloučit. Uvidíme to v následujícím odstavci.
Bob právě poslal (push) changeset větve traveling ale Alenka si jej ještě nestáhla. Před jeho stažením provedla komit vlastní změny:
alice$ echo "How much for a ticket?" >> traveling.txt alice$ hg commit -m "Asking for the ticket price"
O Bobově nejnovější změně se dozví při pokusu poslat svou změnu na server:
alice$ hg push pushing to /home/carla/phrases searching for changes abort: push creates new remote head e0c765eadfc4! (you should pull and merge or use push -f to force)
V této chvíli chce Alenka provést jenom pull a merge - nechce publikovat novou tematickou větev. Bylo by tedy chybné protlačit push opcí -f.
Když Alenka provede pull, obdrží changeset od Boba:
alice$ hg pull pulling from /home/carla/phrases searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) divergent bookmark traveling stored as traveling@default (run 'hg heads .' to see heads, 'hg merge' to merge)
Kromě normálního výstupu po hg pull vytiskl Mercurial sdělení o uložení divergentní záložky jako traveling@default. V TortoiseHg vypadá tato situace následovně:
Mercurial zjistil, že vzdálená záložka traveling divergovala od stejnojmenné záložky lokální. Divergencí se zde míní to, že žádná ze záložek není předkem té druhé; obě záložky jsou sourozenci (siblings). Vzdálená záložka byla přejmenována na traveling@default. Část @default vychází z implicitní (default) cesty. Pokud by Alenka měla zapsáno
[paths] carla = /home/carla/phrases
ve svém souboru .hg/hgrc, potom by byla záložka přejmenována na traveling@carla.
Alenka se nyní pokusí o normální sloučení čel příkazem hg merge, což Mercurial běžně provádí, pokud umí vybrat vhodné kandidáty. U záložek to však nechodí:
alice$ hg merge abort: branch 'default' has 4 heads - please merge with an explicit rev (run 'hg heads .' to see heads)
S divergentní záložkou se musí sloučit ručně:
alice$ hg merge "traveling@default" merging traveling.txt warning: conflicts during merge. merging traveling.txt incomplete! (edit conflicts, then use 'hg resolve --mark') 0 files updated, 0 files merged, 0 files removed, 1 files unresolved use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
Vynoří se však konflikt, protože jak Alenka tak Bob přidali své vlastní řádky. Alenka jej vyřeší vložením obou změn do jednoho souboru:
alice$ cat traveling.txt When does the bus arrive? Two tickets, please! <<<<<<< local How much for a ticket? ======= Where is the train station? >>>>>>> other alice$ hg resolve --tool internal:local traveling.txt alice$ echo "Where is the train station?" >> traveling.txt
Provede kontrolu nástrojem diff a protože je spokojená, předá (commits) sloučení do archivu repozitáře:
alice$ hg diff diff -r e0c765eadfc4 traveling.txt --- a/traveling.txt Tue May 01 10:21:00 2012 +0000 +++ b/traveling.txt Tue May 01 10:21:03 2012 +0000 @@ -1,3 +1,4 @@ When does the bus arrive? Two tickets, please! How much for a ticket? +Where is the train station? alice$ hg commit -m "Merged with Bob"
Divergentní záložka ale po sloučení nezmizela:
alice$ hg bookmarks greetings 1:0b89bcda3dcf * traveling 7:0e1a5b84f635 traveling@default 6:888ae5a9e614
Alice ji dále nepotřebuje, tudíž ji smaže:
alice$ hg bookmarks --delete "traveling@default"
Repozitář nyní vypadá takto:
Poznámka
Mercurial 2.3 přinesl zlepšení: příkaz hg merge nyní automaticky vybere divergentní záložku jako kandidáta sloučení. Poté, co je sloučení komitováno, je divergentní záložka automaticky odebrána.
Když nyní Alenka provede push ke Karle, posune se záložka traveling vpřed. Před push ukazovala na 888ae5a9e614` a protože je 0e1a5b84f635 potomek, může se záložka na serveru posunout vpřed:
alice$ hg push pushing to /home/carla/phrases searching for changes adding changesets adding manifests adding file changes added 2 changesets with 2 changes to 1 files updating bookmark traveling
Alenka s Bobem již shromáždili tři fráze o cestování a rozhodli, že větev travelling může být připojena zpět k hlavní vývojové linii.
Sloučení provede Alenka. Začne tím, že provede pull od Karly aby se ujistila, že má nejposlednější změny a potom provede aktualizaci k nepojmenovanému čelu tam, kde Karla editovala soubor README.txt.
alice$ hg pull pulling from /home/carla/phrases searching for changes no changes found alice$ hg update -r "head() and not bookmark()" 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
In the update command, Alice used a revision set to select the changeset that is a head with no bookmark. She can see in TortoiseHg that she ended up at the correct changeset:
Nyní provede jednoduché sloučení s větví traveling:
alice$ hg merge traveling 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) alice$ hg commit -m "Merged with traveling branch"
Výsledek odpovídá očekávání:
Větev byla přičleněna (merged) a záložka již není více potřebná. Alenka ji tedy může smazat:
alice$ hg bookmark --delete traveling
Může také poslat slučující changeset zpět:
alice$ hg push pushing to /home/carla/phrases searching for changes adding changesets adding manifests adding file changes added 1 changesets with 0 changes to 0 files (-1 heads)
I když smazala záložku lokálně a poslala (pushed) slučovací changeset, na serveru tato záložka stále existuje:
alice$ hg incoming -B comparing with /home/carla/phrases searching for changed bookmarks traveling 0e1a5b84f635
Aby ji smazala i na serveru, provede:
alice$ hg push -B traveling pushing to /home/carla/phrases searching for changes no changes found deleting remote bookmark traveling
Jmenování záložky, která neexistuje lokálně ale existuje na serveru, způsobí smazání této záložky na serveru. To je podobné situaci, kdy předtím Alenka záložku publikovala: hg push -B NAME ukázalo na shodné NAME na serveru jako v lokálním repozitáři — pokud NAME neexistuje lokálně, neexistuje ani na serveru.
Alenka také mohla provést push, merge a smazání záložky v jediné operaci. Ukážeme si to u připojení větve greetings:
alice$ hg merge greetings 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) alice$ hg commit -m "Merged greetings branch" alice$ hg bookmark --delete greetings alice$ hg push -B greetings -r . pushing to /home/carla/phrases searching for changes adding changesets adding manifests adding file changes added 1 changesets with 0 changes to 0 files (-1 heads) deleting remote bookmark greetings
Zde -B greetings nutí Mercurial, aby smazal vzdálenou záložku a -r . způsobí poslání (push) nově vytvořeného slučovacího changesetu; označení . je zkratkou za rodičovskou revizi pracovní kopie. Bez opce -r . by hg push pouze poslalo changeset greetings — čímž by vyloučilo slučovací changeset.
Záložky nám poskytují snadný způsob současného sledování více vývojových linií (čel) v jednom repozitáři najednou. Hlavní vlastnosti záložek jsou:
Měnitelnost: Záložky existují mimo historii changesetu a mohou být přidány, přejmenovány a smazány bez zanechání nějaké stopy. To je činí vhodným nástrojem pro sledování vlastností a pro použití v tematických větvích.
Sdílitelnost: Záložky lze přemisťovat (pull/push) mezi repozitáři. Mercurial zajišťuje synchronizaci publikovaných záložek. Publicita je ale nepovinná; uživatel repozitáře si sám rozhodne kdy a zda záložku zveřejní.
Hlavní příkazy pro práci se záložkami jsou:
hg bookmarks: Vypsat všechny záložky.
hg bookmark NAME: Vytvořit novou záložku s názvem NAME.
hg update NAME: Aktualizovat k NAME a aktivovat záložku.
hg incoming -B: Ukázat záložky, které existují ve vzdáleném, ale neexistujíá v lokálním repozitáři.
hg push -B NAME: Publikovat tematickou větev NAME a její záložku. Pokud NAME lokálně neexistuje, smazat záložku NAME ve vzdáleném repozitáři.
hg pull -B NAME: Stáhonou tematickou větev NAME spolu s její záložkou.
Nechť jedna osoba ve skupině vytvoří repozitář — ostatní si vytvoří jeho klon.
Vytvořte záložku nesoucí vaše jméno a proveďte několik komitů.
Publikujte svou záložku v centrálním repozitáři a stáhněte (pull) si do svého repozitáře větve ostatních.
See the revision set documentation and try a query like hg log -r "ancestors(NAME) - ancestors(.)" to see changesets on the branch NAME that have not yet been merged into your current revision. Revision sets also work in TortoiseHg.
Aktualizujte se k některé jiné větvi a proveďte komit. Pošlete (push) jej zpět na server a experimentujte s ošetřením divergentních záložek
Enable the rebase extension and try rebasing a feature branch on top of the main line of development. The bookmarks should follow along automatically.
První verze Mercurialu neměly ani pojmenované větve, ani záložky. Pojmenované větve byly uvedeny v Mercurialu 0.9.2 (Prosinec 2006) a záložky byly zavedeny o dva roky později. Během let záložky zkošatěly a staly se stabilnější. Zaznamenání hodné události až k Mercurialu 2.2 zahrnují:
Protože vlastnosti záložek mají dlouhou historii, je řada průvodců, které obsahují zastaralé informace. Které vlastnosti záložek nemůže průvodce vůbec znát, zjistíte snadno porovnáním datumu vydání průvodce s výše uvedeným přehledem.