Hlavní navigace

Nástroje pro porovnání obsahu tří různých verzí textových souborů

1. 2. 2018
Doba čtení: 26 minut

Sdílet

Dnes se seznámíme s nástroji sloužícími pro porovnání obsahu tří různých verzí textových souborů i pro takzvané třícestné sloučení změn. Mezi tyto nástroje patří diff3 a grafické Meld, KDiff3 a xxdiff.

Obsah

1. Nástroje pro porovnání obsahu tří různých verzí textových souborů

2. Ukázka vzniku dvou nových variant souborů ze společné předlohy

3. Situace, v níž již porovnání dvou souborů nedostačuje

4. Použití utility diff3 ovládané z příkazového řádku

5. Jak číst výstup z nástroje diff3

6. Sloučení obou nových variant souboru

7. Druhý příklad: změny provedené lokálně i vzdáleně na totožném řádku

8. Vznik konfliktu při pokusu o sloučení změn

9. Třetí příklad: totožné změny provedené lokálně i vzdáleně na totožném řádku

10. Čtvrtý příklad: přidání nových řádků

11. Sloučení obou nových variant souboru

12. Pátý příklad: vymazání řádků

13. Sloučení obou nových variant souboru

14. Poslední příklad: prohození řádků

15. Sloučení obou nových variant souboru

16. Nástroje s grafickým uživatelským rozhraním, které dokážou provést porovnání a třícestné sloučení

17. Utilita xxdiff (stručné připomenutí)

18. Užitečný nástroj KDiff3

19. Nástroj Meld

20. Odkazy na Internetu

1. Nástroje pro porovnání obsahu tří různých verzí textových souborů

Na předchozí článek, v němž jsme se zabývali popisem nástrojů určených pro porovnání obsahu dvou textových souborů, dnes navážeme. Ukážeme si totiž, jakými utilitami je možné porovnat tři soubory. K nutnosti porovnání tří souborů, resp. většinou tří variant jednoho souboru, dochází kupodivu velmi často, a to ve chvíli, kdy jsou tyto soubory upravovány větším množstvím autorů, ať již se jedná o vývojáře, administrátory či autory článků, popř. i knížek. Kromě porovnání je většinou nutné všechny tři verze souborů slučovat dohromady, což většina dále popsaných nástrojů taktéž umožňuje. V první části článku si popíšeme možnosti utility diff3 spouštěné z příkazového řádku a v části druhé se budeme zabývat vybranými nástroji s plnohodnotným grafickým uživatelským rozhraním. Mezi takové nástroje patří především Meld, KDiff3 a již minule zmíněný nástroj xxdiff.

xxx

Obrázek 1: Porovnání dvou verzí testovacího souboru nástrojem TkDiff, který jsme si popsali minule.

2. Ukázka vzniku dvou nových variant souborů ze společné předlohy

V mnoha případech, typicky ve chvíli, kdy na nějakém projektu pracuje větší množství autorů, se může stát, že dva autoři nezávisle na sobě změní obsah stejného souboru. Pro jednoduchost předpokládejme, že se jedná o prostý textový soubor. Podívejme se nyní na jednotlivé verze testovacího textového souboru. Původní verze, která je na disku uložená pod jménem old.txt, obsahuje pouze jedenáct řádků a vypadá přesně takto:

První řádek původního souboru
Druhý řádek původního souboru
Třetí řádek původního souboru
Čtvrtý řádek původního souboru
Pátý řádek původního souboru
Šestý řádek původního souboru
Sedmý řádek původního souboru
Osmý řádek původního souboru
Devátý řádek původního souboru
Desátý řádek původního souboru
Konec původního souboru

Novější, lokálně upravená varianta, která je pro jednoduchost pojmenovaná new_mine.txt, se od verze původní liší pouze v jediném řádku. Pro větší názornost je tento řádek zvýrazněn:

První řádek původního souboru
Druhý řádek původního souboru
Tento řádek jsem změnil já
Čtvrtý řádek původního souboru
Pátý řádek původního souboru
Šestý řádek původního souboru
Sedmý řádek původního souboru
Osmý řádek původního souboru
Devátý řádek původního souboru
Desátý řádek původního souboru
Konec původního souboru

Jiný autor mezitím, tj. nezávisle na lokálních změnách provedených v souboru new_mine.txt, taktéž upravil původní soubor do podoby zobrazené pod tímto odstavcem. Pro jednoduchost máme tuto verzi uloženou v souboru pojmenovaném new_someone_else.txt (domyslete si prosím chybějící apostrof). Opět byl změněn jen jediný (odlišný!) řádek, který je zvýrazněn:

První řádek původního souboru
Druhý řádek původního souboru
Třetí řádek původního souboru
Čtvrtý řádek původního souboru
Pátý řádek původního souboru
Šestý řádek původního souboru
Sedmý řádek původního souboru
Osmý řádek původního souboru
Tento řádek změnil někdo jiný
Desátý řádek původního souboru
Konec původního souboru

Celá situace je zobrazena na diagramu:

Obrázek 2: Vznik dvou novějších variant odvozených od jednoho původního souboru.

Poznámka: povšimněte si, že provedené změny se týkají odlišných řádků, které navíc spolu ani nesousedí. To je sice ideální stav, kdy nedochází ke skutečným konfliktům, ovšem v praxi řešení konfliktů tak jednoduché nebude, což si ukážeme v dalších kapitolách.

3. Situace, v níž již porovnání dvou souborů nedostačuje

Samozřejmě si můžeme vyzkoušet, jaké změny zjistíme „obyčejným“ příkazem diff, který dokáže porovnat obsah pouze dvou souborů. Porovnat můžeme obě novější verze oproti verzi původní, popř. samozřejmě obě novější verze mezi sebou. Pro snazší čtení použijeme unifikovaný výstup se zobrazením kontextových informací, jehož formát jsme si popsali minule:

diff -u old.txt new_mine.txt

V tomto případě se podle očekávání zjistila změna na třetím řádku, což se zobrazí jako operace vymazání původního řádku následovaná vložením řádku nového (oba významné řádky jsou zvýrazněny):

--- old.txt       2018-01-29 21:32:10.000000000 +0100
+++ new_mine.txt  2018-01-29 21:56:22.000000000 +0100
@@ -1,6 +1,6 @@
 První řádek původního souboru
 Druhý řádek původního souboru
-Třetí řádek původního souboru
+Tento řádek jsem změnil já
 Čtvrtý řádek původního souboru
 Pátý řádek původního souboru
 Šestý řádek původního souboru

Podobně zjistíme rozdíl mezi původní verzí a variantou připravenou jiným autorem:

diff -u old.txt new_someone_else.txt

S výsledky:

--- old.txt              2018-01-29 21:32:10.000000000 +0100
+++ new_someone_else.txt 2018-01-29 21:56:41.000000000 +0100
@@ -6,6 +6,6 @@
 Šestý řádek původního souboru
 Sedmý řádek původního souboru
 Osmý řádek původního souboru
-Devátý řádek původního souboru
+Tento řádek změnil někdo jiný
 Desátý řádek původního souboru
 Konec původního souboru

Žádné další možnosti pro porovnání tří různých verzí souborů nám však nástroj diff (i přes jeho značnou užitečnost v jiných případech!) nedává a musíme si tedy zvolit jiný nástroj. Právě popisům těchto nástrojů jsou věnovány navazující kapitoly.

4. Použití utility diff3 ovládané z příkazového řádku

Pokud při práci preferujete použití příkazové řádky popř. automatizaci celé činnosti (což není vždy snadné), lze pro porovnání tří variant textového souboru použít nástroj pojmenovaný přiléhavě diff3. Tomuto nástroji se v nejjednodušším případě předávají jména tří souborů v následujícím pořadí:

  1. novější verze souboru upravená lokálně
  2. starší (původní) verze
  3. novější verze souboru upravená mezitím dalším autorem

Zkusme se tedy podívat, jaké informace získáme pro tři verze našeho testovacího souboru. Použijeme příkaz:

diff3 new_mine.txt old.txt new_someone_else.txt

Výsledek by měl vypadat přibližně takto:

====1
1:3c
  Tento řádek jsem změnil já
2:3c
3:3c
  Třetí řádek původního souboru
====3
1:9c
2:9c
  Devátý řádek původního souboru
3:9c
  Tento řádek změnil někdo jiný

5. Jak číst výstup z nástroje diff3

Jak se má tento výstup, který se v mnoha ohledech odlišuje od výstupu utility diff, dešifrovat? Podívejme se na jednotlivé části výstupu a označme si je čísly:

Obrázek 3: Výstup z utility diff3 s číselným označením jednotlivých bloků.

Význam jednotlivých očíslovaných bloků je následující (čísla odrážek odpovídají předchozímu obrázku):

  1. Na prvním řádku výstupu vidíme znaky ====, které nám oznamují, že se tři verze souborů od sebe odlišují.
  2. Za těmito znaky se nachází pořadové číslo souboru, který se liší od ostatních dvou a jehož změny jsou zobrazeny dále. Pořadová čísla odpovídají pozicím jmen souborů na příkazové řádce při volání utilitydiff3.
  3. Řádka obsahující znaky 1:3c nám říká, že se v prvním souboru (new_mine.txt) změnil (c=change) řádek číslo 3. Ihned poté následuje výpis nové varianty obsahu tohoto řádku.
  4. Zcela stejným způsobem nám řádka 2:3c následovaná řádkou 3:3c říká, jak vypadá třetí řádek ve druhém a třetím souboru (pořadí je samozřejmě opět určeno pořadím parametrů na příkazové řádce).
  5. Celý první blok je tedy nutné číst takto: „v prvním souboru má třetí řádek podobu X, zatímco ve druhém a třetím souboru má stejnou podobu Y“.
  6. Celý druhý blok začínající řádkem ====3 se čte následovně: „v prvním a druhém souboru má devátý řádek podobu Z, zatímco ve třetím souboru má stejný řádek podobu Z“.

Poznámka: povšimněte si, že v tomto případě nástroj diff3 použil pouze jediný příkaz c (change). Ve skutečnosti se však později setkáme i s příkazem a (append), a to ve chvíli, kdy se v souborech bude měnit počet řádků, nebo pokud se budou řádky přesouvat.

6. Sloučení obou nových variant souboru

Vzhledem k tomu, že provedené změny byly provedeny na odlišných řádcích, které neleží vedle sebe, nedošlo vlastně ke skutečnému konfliktu a můžeme tedy využít další velmi užitečnou funkci nástroje diff3. Tou je sloučení obou novějších verzí do verze nejnovější :-) (opět se můžete podívat na obrázek číslo 2). Tato funkce se zapíná přepínačem –merge popř. jeho zkrácenou podobou -m:

diff3 --merge new_mine.txt old.txt new_someone_else.txt

Výsledný soubor je vypsaný na standardní výstup, takže ho lze snadno přesměrovat do nového souboru. Ve výpisu, který můžete vidět pod tímto odstavcem, jsem pro větší přehlednost zvýraznil oba řádky, které byly nezávisle na sobě změněny a následně sloučeny do výsledné verze:

První řádek původního souboru
Druhý řádek původního souboru
Tento řádek jsem změnil já
Čtvrtý řádek původního souboru
Pátý řádek původního souboru
Šestý řádek původního souboru
Sedmý řádek původního souboru
Osmý řádek původního souboru
Tento řádek změnil někdo jiný
Desátý řádek původního souboru
Konec původního souboru

Všimněte si, že výsledný soubor již v tomto případě nepotřebuje žádné další ruční úpravy. Níže uvidíme, že při vzniku konfliktů je situace složitější a vyžadují ruční zásahy do výsledku práce utility diff3.

7. Druhý příklad: změny provedené lokálně i vzdáleně na totožném řádku

Ve druhém – stále však ještě velmi jednoduchém – příkladu si ukážeme, co se stane ve chvíli, kdy se změny týkají pouze jediného řádku, který je změněn jak lokálně, tak i vzdáleným autorem. Každá varianta modifikovaného řádku je ovšem odlišná. Soubor new_mine.txt má obsah:

První řádek původního souboru
Druhý řádek původního souboru
Třetí řádek původního souboru
Čtvrtý řádek původního souboru
Pátý řádek původního souboru
Tento řádek jsem změnil já
Sedmý řádek původního souboru
Osmý řádek původního souboru
Devátý řádek původního souboru
Desátý řádek původního souboru
Konec původního souboru

Zatímco soubor new_someone_else.txt má tento obsah:

První řádek původního souboru
Druhý řádek původního souboru
Třetí řádek původního souboru
Čtvrtý řádek původního souboru
Pátý řádek původního souboru
Tento řádek změnil někdo jiný
Sedmý řádek původního souboru
Osmý řádek původního souboru
Devátý řádek původního souboru
Desátý řádek původního souboru
Konec původního souboru

Vidíme, že oba autoři nezávisle na sobě změnili šestý řádek, takže se podívejme na to, jak se s tímto úkolem vypořádá popisovaný nástroj diff3:

diff3 new_mine.txt old.txt new_someone_else.txt

Vygenerovaný výsledek by měl vypadat přibližně takto:

====
1:6c
  Tento řádek jsem změnil já
2:6c
  Šestý řádek původního souboru
3:6c
  Tento řádek změnil někdo jiný

Nyní se na prvním řádku nachází pouze znaky ==== bez uvedení čísla souboru, který se od ostatních dvou liší. Je to logické – existují totiž hned tři varianty toho samého řádku, takže zde neexistuje žádná vítězná varianta. Obsah výstupu je zřejmý – vždy je hlášena změna (c – change) na řádku číslo 6, a to postupně pro soubor číslo 1, 2 i 3.

V případě, že budeme chtít zobrazit jen konflikty s naší verzí souboru, lze pro tento účel použít přepínač -x:

diff3 -x new_mine.txt old.txt new_someone_else.txt

Výstup nám říká, že pokud budeme chtít provést běžné sloučení naší verze souboru, je v něm zapotřebí změnit šestý řádek, a to následujícím způsobem (diff3 samozřejmě nemůže zaručit, že tato změna bude dávat význam!):

6c
Tento řádek změnil někdo jiný
<Esc>

Všechny neprovedené ruční změny v naší verzi zobrazí diff3 po použití přepínače -e. Zde bude ovšem výstup naprosto stejný (což ovšem platí pouze pro tento konkrétní příklad, kde je jediná změna současně i konfliktem):

6c
Tento řádek změnil někdo jiný
<Esc>

Důležitá poznámka: vzhledem k tomu, že se při použití přepínačů -e a -x vygeneruje skript určený pro řádkový editor ed popř. pro vi/Vim, je na konec příkazů umístěn znak Esc, který se většinou zobrazí jako obyčejná tečka. Ovšem o tečku se skutečně nejedná, o čemž se můžeme přesvědčit po přesměrování výstupu do utilitky od popsané zde:

diff3 -x new_mine.txt old.txt new_someone_else.txt | od -t x1

Na výstupu z utilitky od skutečně můžeme vidět znak Esc následovaný znakem pro konec řádku:

0000000 36 63 0a 54 65 6e 74 6f 20 c5 99 c3 a1 64 65 6b
0000020 20 7a 6d c4 9b 6e 69 6c 20 6e c4 9b 6b 64 6f 20
0000040 6a 69 6e c3 bd 0a 2e 0a
0000050

Poznámka zcela na okraj: utilita od sice dokáže zobrazit i jména řídicích znaků, ovšem nebude správně fungovat v případě použití vícebajtového kódování popř. kódování s proměnnou délkou (UTF-8).

8. Vznik konfliktu při pokusu o sloučení změn

Zajímavé bude zjistit, co se stane v případě, že se pokusíme o sloučení obou novějších souborů:

diff3 --merge new_mine.txt old.txt new_someone_else.txt

V této chvíli se nástroj diff3 (pochopitelně) nemůže rozhodnout, které změně má dát přednost, takže namísto čistého sloučení vygeneruje následující výstup:

První řádek původního souboru
Druhý řádek původního souboru
Třetí řádek původního souboru
Čtvrtý řádek původního souboru
Pátý řádek původního souboru
<<<<<<< new_mine.txt
Tento řádek jsem změnil já
||||||| old.txt
Šestý řádek původního souboru
=======
Tento řádek změnil někdo jiný
>>>>>>> new_someone_else.txt
Sedmý řádek původního souboru
Osmý řádek původního souboru
Devátý řádek původního souboru
Desátý řádek původního souboru
Konec původního souboru

Na výstupu se oblast, v níž došlo ke konfliktu, zobrazila celkem třikrát. Poprvé pro první soubor (lokální nová verze), podruhé pro soubor druhý (původní verze) a potřetí pro soubor třetí (vzdáleně vytvořená nová verze). Jednotlivé bloky jsou od sebe odvozeny řádky se sekvencí znaků <, |, = a >:

<<<<<<< new_mine.txt
první blok
první blok
první blok
||||||| old.txt
druhý blok
druhý blok
druhý blok
=======
třetí blok
třetí blok
třetí blok
>>>>>>> new_someone_else.txt

Sloučení takto označených konfliktních změn již musí uživatel provést ručně v textovém editoru. Na závěr samozřejmě musí smazat i všechny informační řádky vložené nástrojem diff3 (u zdrojových kódů je situace jednodušší v tom, že vložené řádky vedou ve většině případů k chybě při překladu či pokusu o interpretaci).

Výběr pouze změn mezi soubory new_mine.txt a new_someone_else.txt:

diff3 --merge -3 new_mine.txt old.txt new_someone_else.txt

Výsledkem bude soubor:

První řádek původního souboru
Druhý řádek původního souboru
Třetí řádek původního souboru
Čtvrtý řádek původního souboru
Pátý řádek původního souboru
Tento řádek jsem změnil já
Sedmý řádek původního souboru
Osmý řádek původního souboru
Devátý řádek původního souboru
Desátý řádek původního souboru
Konec původního souboru

9. Třetí příklad: totožné změny provedené lokálně i vzdáleně na totožném řádku

Ve třetím příkladu si ukážeme, co se stane ve chvíli, kdy lokální i vzdálený autor změní shodný řádek, a to navíc naprosto stejně. K této situaci dochází kupodivu poměrně často, neboť spousta změn může být provedena různými lintery (či přímo v IDE), popř. dva autoři mohou opravit tu samou chybu.

Oba dva novější soubory, tj. jak soubor upravený lokálně, tak i soubor mezitím upravený vzdáleně, mají shodnou podobu. Jejich obsah je následující:

První řádek původního souboru
Druhý řádek původního souboru
Třetí řádek původního souboru
Čtvrtý řádek původního souboru
Pátý řádek původního souboru
Tento řádek jsem změnil já a současně naprosto stejně i někdo jiný
Sedmý řádek původního souboru
Osmý řádek původního souboru
Devátý řádek původního souboru
Desátý řádek původního souboru
Konec původního souboru

Nástroj diff3 samozřejmě dokáže správně rozpoznat, že změny provedené v prvním a třetím souboru jsou stejné; ve skutečnosti totiž v tomto případě nijak nerozlišuje, který soubor je původní a které soubory jsou novější. Výsledek by měl vypadat takto (připomeňme si, že 1:6c značí změnu na šestém řádku prvního souboru atd.):

====2
1:6c
3:6c
  Tento řádek jsem změnil já a současně naprosto stejně i někdo jiný
2:6c
  Šestý řádek původního souboru

Pokud o sloučení přepínačem –merge ovšem dopadne jinak, než byste možná očekávali, protože diff3 nenahradí původní řádek novým obsahem, ale vytvoří následující výstup, který autora donutí, aby změny přidal ručně:

První řádek původního souboru
Druhý řádek původního souboru
Třetí řádek původního souboru
Čtvrtý řádek původního souboru
Pátý řádek původního souboru
<<<<<<< old.txt
Šestý řádek původního souboru
=======
Tento řádek jsem změnil já a současně naprosto stejně i někdo jiný
>>>>>>> new_someone_else.txt
Sedmý řádek původního souboru
Osmý řádek původního souboru
Devátý řádek původního souboru
Desátý řádek původního souboru
Konec původního souboru

Pokud jste si naprosto jisti, že ke sloučení může dojít (což je v tomto případě pravda), můžete spustit příkaz –merge s modifikátorem -3 nebo jeho delší variantou –easy-only:

diff3 -3 --merge new_mine.txt old.txt new_someone_else.txt

Výsledek již vypadá rozumněji a nevyžaduje žádné další úpravy:

První řádek původního souboru
Druhý řádek původního souboru
Třetí řádek původního souboru
Čtvrtý řádek původního souboru
Pátý řádek původního souboru
Tento řádek jsem změnil já a současně naprosto stejně i někdo jiný
Sedmý řádek původního souboru
Osmý řádek původního souboru
Devátý řádek původního souboru
Desátý řádek původního souboru
Konec původního souboru

Poznámka: modifikátor -3 může vést k tomu, že některé změny nebudou do výsledného souboru zapracovány, takže je vhodné si výsledek zkontrolovat zpětným diffem s vaší lokální variantou.

10. Čtvrtý příklad: přidání nových řádků

Ve čtvrtém příkladu bylo do souboru new_mine.txt přidáno nových deset řádků:

První řádek původního souboru
Druhý řádek původního souboru
Třetí řádek původního souboru
Čtvrtý řádek původního souboru
Deset nových řádků, které jsem přidal já
Deset nových řádků, které jsem přidal já
Deset nových řádků, které jsem přidal já
Deset nových řádků, které jsem přidal já
Deset nových řádků, které jsem přidal já
Deset nových řádků, které jsem přidal já
Deset nových řádků, které jsem přidal já
Deset nových řádků, které jsem přidal já
Deset nových řádků, které jsem přidal já
Deset nových řádků, které jsem přidal já
Pátý řádek původního souboru
Šestý řádek původního souboru
Sedmý řádek původního souboru
Osmý řádek původního souboru
Devátý řádek původního souboru
Desátý řádek původního souboru
Konec původního souboru

Podobně bylo deset řádků přidaných i druhým autorem. Ovšem řádky byly přidány na jiné místo v souboru:

První řádek původního souboru
Druhý řádek původního souboru
Třetí řádek původního souboru
Čtvrtý řádek původního souboru
Pátý řádek původního souboru
Šestý řádek původního souboru
Sedmý řádek původního souboru
Osmý řádek původního souboru
Devátý řádek původního souboru
Deset nových řádků, který přidal někdo jiný
Deset nových řádků, který přidal někdo jiný
Deset nových řádků, který přidal někdo jiný
Deset nových řádků, který přidal někdo jiný
Deset nových řádků, který přidal někdo jiný
Deset nových řádků, který přidal někdo jiný
Deset nových řádků, který přidal někdo jiný
Deset nových řádků, který přidal někdo jiný
Deset nových řádků, který přidal někdo jiný
Deset nových řádků, který přidal někdo jiný
Desátý řádek původního souboru
Konec původního souboru

Nyní se můžeme podívat, jak se tato změna projeví na výstupu z aplikace diff3:

diff3 new_mine.txt old.txt new_someone_else.txt

Ve vygenerovaném výstupu můžeme vidět nové příkazy 2:4a, 3:4a atd. Tyto příkazy oznamují, že došlo k přidání nových řádků:

====1
1:5,14c
  Deset nových řádků, které jsem přidal já
  Deset nových řádků, které jsem přidal já
  Deset nových řádků, které jsem přidal já
  Deset nových řádků, které jsem přidal já
  Deset nových řádků, které jsem přidal já
  Deset nových řádků, které jsem přidal já
  Deset nových řádků, které jsem přidal já
  Deset nových řádků, které jsem přidal já
  Deset nových řádků, které jsem přidal já
  Deset nových řádků, které jsem přidal já
2:4a
3:4a
====3
1:19a
2:9a
3:10,19c
  Deset nových řádků, který přidal někdo jiný
  Deset nových řádků, který přidal někdo jiný
  Deset nových řádků, který přidal někdo jiný
  Deset nových řádků, který přidal někdo jiný
  Deset nových řádků, který přidal někdo jiný
  Deset nových řádků, který přidal někdo jiný
  Deset nových řádků, který přidal někdo jiný
  Deset nových řádků, který přidal někdo jiný
  Deset nových řádků, který přidal někdo jiný
  Deset nových řádků, který přidal někdo jiný

Konkrétně můžeme celý první blok přečíst následovně: řádky 5 až 14 přečtené ze souboru 1 musí být přidány za řádek číslo 4 ve druhémtřetím souboru, aby došlo ke shodě.

Druhý blok se čte podobně: aby došlo ke shodě mezi všemi třemi soubory, je nutné (nové) řádky číslo 10 až 19 ze souboru číslo 3 přidat za řádek 19 v prvním souboru a současně za řádek číslo 9 ve druhém souboru (odlišné číslování je způsobeno první změnou).

Poznámka: povšimněte si, že číslování odpovídá původním řádkům.

11. Sloučení obou nových variant souboru

Samozřejmě nám nic nebrání pokusit se o sloučení obou nových variant souboru:

diff3 --merge new_mine.txt old.txt new_someone_else.txt

Tentokrát nedošlo k žádným konfliktům a výsledek vypadá korektně:

První řádek původního souboru
Druhý řádek původního souboru
Třetí řádek původního souboru
Čtvrtý řádek původního souboru
Deset nových řádků, které jsem přidal já
Deset nových řádků, které jsem přidal já
Deset nových řádků, které jsem přidal já
Deset nových řádků, které jsem přidal já
Deset nových řádků, které jsem přidal já
Deset nových řádků, které jsem přidal já
Deset nových řádků, které jsem přidal já
Deset nových řádků, které jsem přidal já
Deset nových řádků, které jsem přidal já
Deset nových řádků, které jsem přidal já
Pátý řádek původního souboru
Šestý řádek původního souboru
Sedmý řádek původního souboru
Osmý řádek původního souboru
Devátý řádek původního souboru
Deset nových řádků, který přidal někdo jiný
Deset nových řádků, který přidal někdo jiný
Deset nových řádků, který přidal někdo jiný
Deset nových řádků, který přidal někdo jiný
Deset nových řádků, který přidal někdo jiný
Deset nových řádků, který přidal někdo jiný
Deset nových řádků, který přidal někdo jiný
Deset nových řádků, který přidal někdo jiný
Deset nových řádků, který přidal někdo jiný
Deset nových řádků, který přidal někdo jiný
Desátý řádek původního souboru
Konec původního souboru

12. Pátý příklad: vymazání řádků

V pátém příkladu některé řádky naopak vymažeme (samozřejmě opět v porovnání s obsahem původního souboru). Takže jen ve stručnosti:

Původní soubor old.txt:

První řádek původního souboru
Druhý řádek původního souboru
Třetí řádek původního souboru
Čtvrtý řádek původního souboru
Pátý řádek původního souboru
Šestý řádek původního souboru
Sedmý řádek původního souboru
Osmý řádek původního souboru
Devátý řádek původního souboru
Desátý řádek původního souboru
Konec původního souboru

Obsah souboru new_mine.txt:

První řádek původního souboru
Druhý řádek původního souboru
Pátý řádek původního souboru
Šestý řádek původního souboru
Sedmý řádek původního souboru
Osmý řádek původního souboru
Devátý řádek původního souboru
Desátý řádek původního souboru
Konec původního souboru

Obsah souboru new_someone.txt:

První řádek původního souboru
Druhý řádek původního souboru
Třetí řádek původního souboru
Čtvrtý řádek původního souboru
Pátý řádek původního souboru
Šestý řádek původního souboru
Devátý řádek původního souboru
Desátý řádek původního souboru
Konec původního souboru

Všechny tři varianty porovnáme přesně tak, jak jsme již zvyklí:

diff3 new_mine.txt old.txt new_someone_else.txt

Výsledek třícestného porovnání bude následující:

====1
1:2a
2:3,4c
3:3,4c
  Třetí řádek původního souboru
  Čtvrtý řádek původního souboru
====3
1:5,6c
2:7,8c
  Sedmý řádek původního souboru
  Osmý řádek původního souboru
3:6a

Z tohoto porovnání můžeme vidět, že nástroj diff3 vlastně nezná příkaz d (delete), takže nám jen říká, že aby došlo ke shodě mezi soubory, musí se do souboru číslo 1 přidat dva řádky (3 a 4), které jsou v obou dvou dalších souborech shodné. Navíc je nutné (aby došlo ke shodě) přidat dva řádky i do třetího souboru.

13. Sloučení obou nových variant souboru

Zkusme nyní provést třícestné sloučení:

diff3 --merge new_mine.txt old.txt new_someone_else.txt

Pokus o sloučení příkazem –merge dopadne následovně:

První řádek původního souboru
Druhý řádek původního souboru
Pátý řádek původního souboru
Šestý řádek původního souboru
Devátý řádek původního souboru
Desátý řádek původního souboru
Konec původního souboru

Výsledek v tomto případě vypadá podle očekávání – došlo ke smazání celkem čtyř řádků.

14. Poslední příklad: prohození řádků

V posledním příkladu se pokusíme prohodit dva řádky, a to v obou novějších variantách – jak v lokální, tak i vzdálené.

Obsah souboru new_mine.txt:

První řádek původního souboru
Třetí řádek původního souboru
Čtvrtý řádek původního souboru
Pátý řádek původního souboru
Šestý řádek původního souboru
Sedmý řádek původního souboru
Osmý řádek původního souboru
Devátý řádek původního souboru
Druhý řádek původního souboru
Desátý řádek původního souboru
Konec původního souboru

Obsah souboru new_someone.txt:

První řádek původního souboru
Druhý řádek původního souboru
Třetí řádek původního souboru
Pátý řádek původního souboru
Šestý řádek původního souboru
Sedmý řádek původního souboru
Čtvrtý řádek původního souboru
Osmý řádek původního souboru
Devátý řádek původního souboru
Desátý řádek původního souboru
Konec původního souboru

Výsledek třícestného porovnání:

====1
1:1a
2:2c
3:2c
  Druhý řádek původního souboru
====3
1:3c
2:4c
  Čtvrtý řádek původního souboru
3:3a
====3
1:6a
2:7a
3:7c
  Čtvrtý řádek původního souboru
====1
1:9c
  Druhý řádek původního souboru
2:9a
3:9a

15. Sloučení obou nových variant souboru

Zajímavé je, že i přes relativní komplikovanost změn je možné provést třícestné sloučení:

diff3 --merge new_mine.txt old.txt new_someone_else.txt

S výsledkem:

První řádek původního souboru
Třetí řádek původního souboru
Pátý řádek původního souboru
Šestý řádek původního souboru
Sedmý řádek původního souboru
Čtvrtý řádek původního souboru
Osmý řádek původního souboru
Devátý řádek původního souboru
Druhý řádek původního souboru
Desátý řádek původního souboru
Konec původního souboru

Poznámka: zkuste si ještě komplikovanější příklady – zjistíte, že automatické třícestné sloučení se většinou nepovede k úplné spokojenosti a bude nutné provést ruční úpravy.

16. Nástroje s grafickým uživatelským rozhraním, které dokážou provést porovnání a třícestné sloučení

Popisu možností utility diff3 ovládané z příkazové řádky jsme věnovali dosti velkou část dnešního článku. Je tomu tak především z toho důvodu, aby bylo patrné, k jakým situacím může při slučování různých variant souborů dojít. Nyní se již můžeme začít věnovat popisu nástrojů, které jsou vybaveny plnohodnotným grafickým uživatelským rozhraním a které taktéž slouží pro zjištění rozdílů mezi třemi verzemi souboru a současně i pro jejich sloučení. Těchto nástrojů existuje relativně velké množství (některé jsou součástí integrovaných vývojových prostředí), takže muselo dojít k zúžení výběru (alespoň pro tento článek). V dalších kapitolách si ukážeme třícestné slučování s využitím nástrojů xxdiff, KDiff3 a konečně asi nejpoužívanějšího open source nástroje pro tuto činnost – aplikace Meld.

Obrázek 4: Dialog s informacemi o velmi užitečném nástroji KDiff3

17. Utilita xxdiff (stručné připomenutí)

O utilitě nazvané xxdiff jsme se již zmínili v předchozím článku, takže si jen připomeňme, že tento relativně malý a na systémové prostředky nenáročný nástroj sice není tak známý jako Meld, ovšem nabízí podobné funkce a navíc je v něm možné velmi snadno provádět třícestný merge, tj. porovnání a aplikování změn mezi třemi verzemi souborů (typicky se jedná o společného předka, soubor změněný lokálně a soubor mezitím změněný někým jiným). Utilitě xxdiff je možné při jejím volání z příkazového řádku předat dva soubory:

xxdiff old.py new.py

Ovšem nás bude zajímat, jak provést třícestný merge. V tomto případě spustíme utilitu xxdiff následujícím způsobem:

xxdiff new_mine.txt old.txt new_someone_else.txt

Možnosti nástroje xxdiff jsou ukázány na následujících screenshotech:

Obrázek 5: Velmi nepříjemné je, že xxdiff stále nedokáže pracovat s Unicode (i když samozřejmě záleží na způsobu použití; některým týmům to nemusí vadit).

Obrázek 6: Nejjednodušší situace, ve které jsou změny provedeny na odlišných řádcích. Barevné pozadí nám říká, že nedochází ke konfliktům.

Obrázek 7: Nepatrně složitější situace, kdy jsou změny provedeny na shodném řádku. Zde si musíme vybrat, z jaké verze provést sloučení.

Obrázek 8: Jiná situace, ve které jsou změněny shodné řádky, které mají náhodou i stejný obsah.

Obrázek 9: Přes menu Display lze zobrazit čtvrté okno s výsledkem sloučení.

Obrázek 10: Takto vypadá postupné slučování změn. V horním (čtvrtém) okně lze sledovat, jak bude vypadat výsledek.

18. Užitečný nástroj KDiff3

Dalším nástrojem s plnohodnotným grafickým uživatelským rozhraním, který je možné použít pro porovnání a/nebo sloučení tří variant téhož souboru, je aplikace nazvaná příhodně KDiff3. Již ze jména je zřejmé, že se jedná o aplikaci založenou na frameworku Qt, ovšem samozřejmě ji lze použít i mimo prostředí KDE (ostatně všechny screenshoty vznikly na počítači s Fluxboxem). Nástroj KDiff3 dokáže pracovat i s Unicode, což je vhodné pro projekty, v nichž se používají znaky s nabodeníčky. Kromě běžného třícestného porovnání se zvýrazňují i změny provedené na shodných řádcích, což je velmi užitečné především při práci se zdrojovými kódy a hledání mnohdy jen jednoznakových úprav. Opět se podívejme na sérii screenshotů, které ukazují některé možnosti této utility:

Obrázek 11: Automatické sloučení souborů z našeho prvního příkladu. Povšimněte si, že původní soubor se nachází v levém okně, nikoli uprostřed (což je častější řešení).

Obrázek 12: Zde došlo k detekci konfliktu způsobeném změnami na stejném řádku, jedná se o obdobu druhého příkladu.

Obrázek 13: Zajímavá situace – změny provedené lokálně i vzdáleně jsou totožné, což je detekováno.

Obrázek 14: Situace, kdy došlo k přidání nových řádků, ovšem nikoli ke vzniku konfliktu.

Obrázek 15: Další situace, kdy došlo k přidání nových řádků, ovšem nikoli ke vzniku konfliktu.

Obrázek 16: Složitější případ, kdy byl jeden řádek v novější verzi souboru smazán.

Obrázek 17: Podobná situace, jako na předchozím screenshotu. I zde dokázal KDiff3 korektně synchronizovat oba novější soubory.

19. Nástroj Meld

Poslední nástroj s plnohodnotným grafickým uživatelským rozhraním, s nímž se dnes setkáme, se jmenuje Meld. Pravděpodobně se jedná o nejpoužívanější nástroj tohoto typu, minimálně na Linuxu. I Meld podporuje porovnání tří variant téhož souboru, samozřejmě s možností sloučení těchto variant do nejnovější verze. Při zvýraznění lokálních změn (v rámci jednoho řádku) se Meld snaží o zobrazení odlišných znaků nebo sekvence znaků (jiný kontrast barvy pozadí). Možnosti Meldu při třícestném slučování jsou ukázány na následující osmici screenshotů. Povšimněte si, že se, podobně jako u xxdiffu, původní varianta souboru zobrazuje v prostředním sloupci:

Obrázek 18: Nejjednodušší situace, ve které jsou změny provedeny na odlišných řádcích. Barevné pozadí nám říká, že nedochází ke konfliktům.

Obrázek 19: Nepatrně složitější situace, kdy jsou změny provedeny na shodném řádku. Zde si musíme šipkami vybrat, z jaké verze provést sloučení.

Obrázek 20: Jiná situace, ve které jsou změněny shodné řádky, které mají náhodou i stejný obsah.

Obrázek 21: Způsob zobrazení rozdílů mezi soubory v situaci, kdy došlo k přidání nových řádků.

Obrázek 22: Výsledek sloučení změn z obrázku číslo 21.

Obrázek 23: Komplikovanější situace; řádek byl oproti originálu vymazán.

root_podpora

Obrázek 24: Postupná akceptace jednotlivých dílčích změn.

Obrázek 25: Postupná akceptace jednotlivých dílčích změn.

20. Odkazy na Internetu

  1. Comparing and Merging Files
    https://www.gnu.org/softwa­re/diffutils/manual/diffu­tils.html
  2. Three-way merge
    https://en.wikipedia.org/wi­ki/Merge_(version_control)#Three-way_merge
  3. diff (1) – Linux Man Pages
    https://www.systutorials.com/doc­s/linux/man/1-diff/
  4. diff3 (1) – Linux Man Pages
    https://www.systutorials.com/doc­s/linux/man/1-diff3/
  5. 11 Linux diff3 Command Examples (Compare 3 Files Line by Line)
    https://www.thegeekstuff.com/2012/08/dif­f3-examples/
  6. How to compare three files in Linux using diff3 tool
    https://www.howtoforge.com/tu­torial/how-to-compare-three-files-in-linux-using-diff3-tool/
  7. Merging with diff3
    https://blog.jcoglan.com/2017/05/08/mer­ging-with-diff3/
  8. diff utility (Wikipedia)
    https://en.wikipedia.org/wi­ki/Diff_utility
  9. diff3
    https://en.wikipedia.org/wiki/Diff3
  10. GNU Wdiff
    https://www.gnu.org/software/wdiff/
  11. GNU wdiff Manual
    https://www.gnu.org/softwa­re/wdiff/manual/
  12. wdiff (1) – Linux Man Pages
    https://www.systutorials.com/doc­s/linux/man/1-wdiff/
  13. diff3 (1) – Linux Man Pages
    https://www.systutorials.com/doc­s/linux/man/1-diff3/
  14. sdiff (1) – Linux Man Pages
    https://www.systutorials.com/doc­s/linux/man/1-sdiff/
  15. Stránky nástroje Meld
    http://meldmerge.org/
  16. Meld na stránkách GNOME
    https://wiki.gnome.org/Apps/Meld
  17. Stránky nástroje TkDiff
    https://sourceforge.net/pro­jects/tkdiff/
  18. Zdrojové kódy TkDiffu
    https://sourceforge.net/pro­jects/tkdiff/files/tkdiff/4­.2/
  19. Poslední verze nástroje TkDiff
    https://sourceforge.net/pro­jects/tkdiff/files/latest/dow­nload
  20. Manuálová stránka k nástroji TkDiff
    http://linux.math.tifr.res­.in/manuals/man/tkdiff.html
  21. diffh: Make your diff easier to see
    https://inconsolation.wor­dpress.com/2013/10/07/dif­fh-make-your-diff-easier-to-see/
  22. Stránky projektu diffh
    https://sourceforge.net/pro­jects/diffh/
  23. Pretty Diff (implementovaný v JavaScriptu)
    http://prettydiff.com/
  24. Nástroje pro diff textů
    https://en.wikipedia.org/wiki/Diff-Text
  25. Pretty Diff (implementovaný v JavaScriptu)
    https://en.wikipedia.org/wi­ki/Pretty_Diff
  26. Stránky projektu colordiff
    https://www.colordiff.org/
  27. Skript idiff
    http://www.pixelbeat.org/scrip­ts/idiff
  28. Three way git merging with Meld
    https://lukas.zapletalovi­.com/2012/09/three-way-git-merging-with-meld.html
  29. xxdiff na serveru SourceForge
    https://sourceforge.net/pro­jects/xxdiff/
  30. Stránka nástroje KDiff3
    http://kdiff3.sourceforge.net/
  31. Seriál o programovacím jazyku TCL a GUI knihovně Tk
    https://www.root.cz/seria­ly/programovaci-jazyk-tcl/
  32. ActiveTcl
    https://www.activestate.com/activetcl
  33. Tiobe: žebříček popularity programovacích jazyků
    https://www.tiobe.com/tiobe-index/

Byl pro vás článek přínosný?

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.