Přejít k navigační liště

Zdroják » Různé » Vimdiff – nástroj drsňáků

Vimdiff – nástroj drsňáků

Články Různé

Používání vimdiffu jsem se dlouho vyhýbal, ale došlo i na mě. Podělím se s vámi o své zkušenosti.

Text vyšel původně na autorově webu.

Musím se vám k něčemu přiznat… Už patnáct let je Vim můj nejoblíbenější textový editor. A občas, čas od času, i hlavní nástroj na programování.

Umím si poeditovat vimrc, který po léta udržuju a vylepšuju. Dokonce jsem se i naučil trochu Vim script/VimL a napsal dva zanedbatelné a nedotažené pluginy (pro Gradle a WSDL).

Ale vždycky jsem se jako čert kříži vyhýbal jedné věci – používání vimdiff. Nicméně na každého jednou dojde. Z určitých (pro článek nepodstatných) důvodů jsem si nemohl pro nové vývojové prostředí nastavit P4Merge, a tak jsem vstoupil do zapovězené komnaty.

Disclaimer: Tenhle článek píšu jako shrnutí toho, jak jsem práci s vimdiff pochopil. Pokud máte víc zkušeností, budu rád, když se podělíte v komentářích.

2-way merge

Nejjednodušší způsob, jak používat vimdiff – pokud pomineme, že umí dělat i „plain old“ diff – je 2-way merge: máme vedle sebe dvě okna se zvýrazněnými rozdíly a chceme mezi nimi tyto změny propagovat.

vimdiff, 2-way merge

​Stavu na předešlém obrázku, který je výchozí pro merge, se dá dosáhnout několika způsoby:

  • Příkazem: vimdiff myFile.txt theirFile.txt
  • Příkazem: vim -d myFile.txt theirFile.txt
  • Kombinací příkazů:
    • vim myFile.txt
    • :diffsplit theirFile.txt
  • Kombinací příkazů
    • vim -O myFile.txt theirFile.txt (vsplit obou souborů)
    • :diffthis (zapne diff na aktuálním bufferu)
    • Ctrl-W Ctrl-W (skok do druhého bufferu)
    • :diffthis (zapne diff v druhém bufferu)

Základní příkazy

Tak, diff máme zobrazený, co s ním? První věc – je potřeba se v diffu umět pohybovat. Kromě toho, že můžete použít jakýkoli skok, který znáte z běžného Vimu, jsou tu dva příkazy, které umožňují skákat po jednotlivých rozdílech:

  • ]c skočí na následující diff
  • [c skočí na předcházející diff

Za druhé – chceme propagovat změny z/do aktuálního bufferu: skočíme na diff, který chceme upravit a:

  • do, nebo :diffget natáhne změny z „druhého“ bufferu do toho aktuálního.
  • dp, nebo :diffput propaguje změny z aktuálního bufferu do „toho druhého“.

Za třetí – změny uložíme. Kromě příkazů na standardní ukládání (:wZZ atd.) se může hodit:

  • :only zavře všechny ostatní buffery kromě toho aktuálního
  • :qall zavře všechny otevřené buffery
  • :only | wq zavře ostatní buffery + uloží stávající + ukončí Vim. Cool!

Eventuálně za čtvrté – pokud věci nejdou hladce, může se šiknout:

  • :diffupdate znovu proskenuje a překreslí rozdíly (u komplikovanějších mergů nemusí Vim správně pochopit danou změnu)
  • :set wrap nastavení zalamování řádků (hodí se při velmi dlouhých řádcích, typicky některá XML)
  • zo/zc otevře/zavře skryté (folded) řádky

3-way merge

Nemusím vám říkat, že 2-way merge je pro školáky – profíci makaj v Gitu či v Mercurialu a tam je dvoucestný merge nedostačující. Ke slovu přichází 3-way merge. Co to je?

Schéma 3-way merge

3-way merge není nic složitého. V podstatě jde o to, že máme dvě verze, které mají společného předka. V mergovacím nástroji pak vidíme všechny tři verze vedle sebe a většinou máme k dispozici ještě čtvrté okno s aktuálním výsledkem merge.

3-way merge v aplikaci

Nastavení Gitu

Nastavení spolupráce Gitu a vimdiff je jednoduché – stačí spustit z příkazové řádky následující sadu příkazů:

$ git config --global merge.tool vimdiff
$ git config --global merge.conflictstyle diff3
$ git config --global mergetool.prompt false
$ git config --global mergetool.keepBackup false

Pokud se podíváte do ~/.gitconfig, měli byste tam vidět:

[merge]
    tool = vimdiff
    conflictstyle = diff3
[mergetool]
    prompt = false
    keepBackup = false

Nastavení Mercurialu

Nastavení Mercurialu je podobně jednoduché. Otevřeme soubor ~/.hgrc příkazem

$ hg config --edit

a vložíme následující řádky

[ui]
merge = vimdiff

[merge-tools]
vimdiff.executable = vimdiff
vimdiff.args = -f -d $output -M $local $base $other -c "wincmd J" -c "set modifiable" -c "set write"
vimdiff.premerge = keep

[extensions]
hgext.extdiff =

[extdiff]
cmd.vimdiff = vimdiff

Sekce [extensions] a [extdiff] nejsou pro merge nutné, ale hodí se, pokud chceme vimdiff používat jako dodatečný externí diff nástroj. Sekundární diff spustíme příkazem hg vimdiff.

Základní příkazy

Základní příkazy jsou stejné jako v sekci 2-way merge, s výjimkou příkazů dp/do (:diffput/:diffget) – pokud bychom je nyní použili, vimdiff nám zahlásí chybu:

More than two buffers in diff mode, don't know which one to use

To je v pořádku: u 3-way merge se ve vimdiff otevřou 4 buffery, všechny v diff módu. Takže do té doby, než se vimdiff naučí komunikovat telepaticky, je potřeba mu říct, ze kterého bufferu chceme danou změnu natáhnout.

vimdiff, 3-way merge v Gitu

Klasické merge flow vypadá následovně:

  1. Začínáme v dolním „výsledkovém“ bufferu.
  2. ]c (skočit na následující diff, který chceme mergovat)
  3. :diffget <identifikace-bufferu> získáme změnu z daného bufferu (viz dále)
  4. Opakujeme 2-3.
  5. :only | wq uložíme merge.

Obecně, identifikátor bufferu získáme příkazem :ls. To je ale dost nepraktické a nepřehledné. Další možnost je identifikovat buffer částečným názvem souboru. Tady přichází na pomoc jak Git, tak Mercurial, který přidávají k názvům souborů příhodný suffix.

Merge v Gitu

Git přidává do názvů mergovaných souborů následující suffixy, v pořadí zleva doprava: LOCAL (vlevo), BASE (uprostřed), REMOTE (vpravo). Pro natažení změny z (levého) bufferu LOCAL můžeme použít příkaz :diffg LO.

Výpis bufferů pro Git:

:ls
  1 #a   "./myFile_LOCAL_7424.txt"      line 1
  2  a   "./myFile_BASE_7424.txt"       line 0
  3  a   "./myFile_REMOTE_7424.txt"     line 0
  4 %a   "myFile.txt"                   line 12

Merge v Mercurialu

vimdiff, 3-way merge v Mercurialu

Mercurial přidává do názvů mergovaných souborů následující suffixy, v pořadí zleva doprava: orig (vlevo), base (uprostřed), other (vpravo). Pro natažení změny z (levého) bufferu orig můžeme použít příkaz :diffg orig.

Výpis bufferů pro Mercurial:

:ls
  1 %a   "myFile.txt"                   line 2
  2  a-  "myFile.txt.orig"              line 0
  3  a-  "/tmp/myFile.txt~base.iZwwuA"  line 0
  4  a-  "/tmp/myFile.txt~other.km9Itr" line 0

Co mi (zatím) schází?

Musím říct, že potom, co jsem si vimdiff osahal, pochopil jeho logiku a naučil se jeho příkazy, jsem si ho docela oblíbil.

Jediná výtka zatím jde za jeho neschopností skákat přímo po konfliktech – Git i Mercurial dělají výborně automatické merge a ty jsou samozřejmě vidět ve vimdiffu taky, jako pouhá změna bez konfliktu. Mít nějaký příkaz, který rozlišuje pouhý diff a konflikt, by bylo fajn.

Komentáře

Subscribe
Upozornit na
guest
4 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
mcepl

Co se týče skákání po konfliktech, tak je to prostinké, milý Watsone:

/<<<<<CR>

:).

Ale jinak pokud se Vaše diffování týká uložení souborů v gitu, tak silně doporučuji nasazení pluginu vim-fugitive. Silně doporučuji. Instrukce k jeho používání je možné nalézt ve výtečných videích Vimcasts.

fmatejic

No, ja mam svuj vim jak barevnou paletu a pluginu prehrsle (treba i na wiki), jako asi kazdy, kdo ho vyuziva. Ale na problem jsem narazil az kdyz jsem vimdiffem porovnaval vicero jazyku, obcas nebylo videt, co je co (stejne barvy) – ne vzdy se naparsuji barvicky dobre.
Nakonec muj .vimrc obsahuje :
if &diff
syntax off
endif

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.

Pocta C64

Za prvopočátek své programátorské kariéry vděčím počítači Commodore 64. Tehdy jsem genialitu návrhu nemohl docenit. Dnes dokážu lehce nahlédnout pod pokličku. Chtěl bych se o to s vámi podělit a vzdát mu hold.