Hlavní navigace

Výlet do říše verzí: RCS - mergování

Petr Baudiš

Po delší prodlevě nastal opět čas podívat se do světa RCS. Dnes se dozvíte, co je a k čemu slouží mergování (a hlavně, jakými všemi možnými způsoby se dá provádět). Budeme si také vyprávět, jak vznikají konflikty, proč vznikají a jak se s nimi vypořádat.

Jak na slučování větví

Často provedeme změny v jedné větvi, načež chceme podobné změny provést i ve větvích jiných (například se jedná o opravu nějaké chyby). Proto by bylo vhodné mít možnost slučovat některé změny s jinými větvemi. Takové akci se učeně říká merge a můžeme takto řešit například i situaci, kdy nám někdo poslal patch proti nějaké starší revizi souboru. Dokonce takovéto věci můžeme provádět hned několika způsoby. Musím ale upozornit, že žádný z těchto způsobů výsledné změny automaticky nezaregistruje! Po sloučení změn tedy musíte sami ještě zavolat ci.

Samozřejmě můžeme příkazem rcsdiff vygenerovat diff mezi revizí před změnou a revizí po změně a pak použít příkaz patch, ovšem rozložení na několik kroků může být nepohodlné a hlavně je dosti pracné řešit případné konflikty (o nich si povíme za chvíli — ono je to občas pracné i tak, ale méně). Proto je tu příkaz rcsmerge. Ten vezme rozdíly mezi dvěmi revizemi souboru a zahrne je do jeho pracovní kopie. Pokud tak do revize 1.4.2.3 chceme zahrnout rozdíly mezi revizí 1.6 a 1.7 (protože jsme v revizi 1.7 opravili vážnou chybu), použijeme (na souboru obsahujícím revizi 1.4.2.3) rcsmerge -r1.6 -r1.7. Pokud naopak například zjistíme, že úpravy, které jsme provedli v revizi 1.5, se nám úplně nepovedly, a chceme tedy vrátit zpět změny mezi revizí 1.4 a 1.5 (tzv. je revertnout), použijeme rcsmerge -r1.5 -r1.4 (pozor na pořadí parametrů!). Jako poslední příklad si ukážeme mergování patche proti starší verzi souboru — pokud máme patch na verzi 1.3, vrátíme se k ní odpovídajícím příkazem co, aplikujeme patch a následně sloučíme všechny změny až k poslední revizi (dejme tomu 1.7) pomocí rcsmerge -r1.3 -r1.7 — máme nyní opět revizi 1.7, ovšem včetně onoho patche. Výhoda je opět ta, že RCS umí v případných konfliktech chodit mnohem lépe než klasická utilita patch.

Téměř ekvivalentní k rcsmerge je parametr -j (od slova join) příkazu co, pracuje však přímo s revizí daného souboru (tou aktuální nebo předanou jiným parametrem, třeba -r). Často je proto méně flexibilní, na druhou stranu se však tato vlastnost může občas hodit a použití této metody může být také rychlejší a elegantnější; krom toho v CVS se používá prakticky výhradně tato metoda. Samotné použití je jednoduché, jako hodnotu parametru napíšeme rozsah revizí, které chceme sloučit s tou revizí, kterou právě checkoutujeme. Pokud chceme tedy připojit k revizi 1.4.2.3 rozdíly mezi revizí 1.6 a 1.7, použijeme co -r1.4.2.3 -j1.6:1.7. Pokud chceme vrátit zpět změny mezi revizí 1.4 a 1.5, stačí co -j1.5:1.4.

Při mergování patche proti starší verzi souboru si musíme pomoci, protože co nám přepíše aktuální verzi souboru — nejdříve si tedy u revize 1.3 vytvoříme pomocnou větev, do které si uložíme onen patch příkazem ci -r 1.3.1; nyní již stačí sloučit změny v této větvi s poslední revizí souboru příkazem co -j1.3:1.3.1. Na první pohled se tento přístup může zdát podivný a neohrabaný, vyzařuje však určitou vnitřní krásu a eleganci ;-). Způsob, jakým RCS a CVS pracuje s větvemi, není úplně nejšťastnější a některé nedostatky do značné míry uživatele od širšího používání větví odrazují. Ovšem pokročilejší systémy (o kterých si povíme po CVS) často větvení a slučování ovládají mnohem lépe a uživatele se v častém větvení snaží dokonce podporovat. Proto se v některých z nich často setkáme s podobným přístupem, jehož odlesk vidíme v této metodě mergování patchů.

Metoda parametru -j je zvláště vhodná také při slučování celých větví, například pokud chceme sloučit všechny změny v hlavní větvi do pomocné větve, ve které vyvíjíme nějakou zvláště nestabilní funkci. Pokud vynecháme počátek rozsahu revizí, automaticky se doplní poslední revize ještě společná pro obě větve. Tedy například pokud chceme do hlavní větve (1) zahrnout všechny změny provedené ve větvi 1.3.1, zavoláme ve větvi 1 příkaz co -j1.3.1.4, automaticky tak přimergujeme změny mezi 1.3 a 1.3.1.4. Ale nebylo by to RCS, kdyby v tom nebyl nějaký háček. Bohužel totiž nemůžeme tak elegantně mergovat změny opakovaně, poněvadž některé změny by se nám přimergovaly několikrát, což není pěkná představa. Pokud bychom totiž zaregistrovali revize 1.3.1.5 a 1.3.1.6 a chtěli je přimergovat, co -j1.3.1.6 nám kromě toho zkusí opět sloučit do aktuální revize i revize 1.3.1.1 až 1.3.1.4. Proto nám nezbývá, než si při opakovaném slučování pamatovat, co všechno už jsme mergnuli, a psát co -j1.3.1.5:1.3.1.6.

Pozn.: rcsmerge a -j používají ve skutečnosti jako backend příkaz merge, který je sice také součástí distribuce RCS, ovšem lze používat zcela nezávisle. Více viz manuálová stránka merge(1). A merge zase používá jako backend příkaz diff3, o kterém se teoreticky blíže dočtete v diff3(1). Já tuto manuálovou stránku z nějakého důvodu nemám, dokumentace je však součástí i info manuálu k diffu.

O bytostech podsvětních aneb konflikty

Občas slučování změn není zase tak jednoduché. Problém totiž nastane, když nezávisle na sobě ve dvou větvích změníme třeba jeden řádek a teď chceme větve sloučit:

Tabulka č. 398
1.1 1.1.1.1 1.2
Ahoj
Zdravím
Dobrý den
Ahoj
Zdravim
Dobry den
Ahoj
Zdravíme
Dobrý den

Nastává ovšem problém, druhý řádek jsme změnili v obou větvích. Má se tedy při sloučení dát přednost „Zdravim“ nebo „Zdravíme“? To ovšem RCS neuhodne, tedy se zbaběle vzdá, vygeneruje konflikt a nechá problém vyřešit nás. Na konflikt ovšem alespoň patřičně upozorní, jak ve formě chybové hlášky vygenerované rcsmerge či co -j, tak i poměrně výrazným označením ve výsledném souboru (dejme tomu že mergujeme 1.1.1.1 a 1.2 do 1.3):

Ahoj
<<<<<<< 1.2
Zdravíme
=======
Zdravim
>>>>>>>> /tmp/T3PHcpoy
Dobry den

<<<<<<< označuje počátek této nemilé události, následuje ho číslo revize a původní fragment. Ten je ukončen ======= a po něm přichází fragment, který pochází z 1.1.1.1. Všechno uzavírá >>>>>>> a jakési podivné jméno pracovního souboru, na kterém RCS pouštěl merge. Při řešení konfliktů tedy vybereme ten vyhovující fragment (případně je sloučíme nějak inteligentně, třeba do „Zdravime“) a zbylý ASCII art eliminujeme.

Pozn.: údaje za <<.. a >>.. jsou pouze informativní a můžou být libovolně proházené podle toho, jak šíleně zrovna mergujeme; například u CVS bude naopak obvykle jako první jméno souboru (CVS je pokročilejší, takže tentokráte již smysluplné ;-) a teprve na konci číslo mergované revize.

Další pozn.: velmi oblíbeným zdrojem konfliktů bývají keywords neboli klíčová slova. Co jsou zač a jak se vypínají, se dozvíme … po reklamě.


Příště se s RCS rozloučíme povídáním o mocném nástroji jménem klíčová slova. Ta nám umožní se uvnitř souboru dozvědět, že vlastně prošel RCS, a náležitě toho využít. Na závěr pak ještě utrousíme několik pochmurných slov o obludce jménem SCCS.

Našli jste v článku chybu?