Tutorijal: sintaksni merge, jj sa zakljucanim push-ovanim commit-ima, diff nad PDF/DOCX, docdiff CLI
Podešeno 19–26. septembra 2026. na Kimi mašini (Windows 11, git 2.55). Kontekst i "zašto": xit-haxy-patch-based-merge i binarni-fajlovi-verzionisanje-i-diff.
| Alat | Verzija | Čemu služi |
|---|---|---|
| mergiraf | 0.16.3 | rešava git konflikte po sintaksi koda |
| jj (Jujutsu) | 0.45.1 | alternativni klijent nad git repoom |
| textconv (pdftotext, pandoc) | — | git diff nad PDF i DOCX fajlovima |
Binarni fajlovi su u %LOCALAPPDATA%\Microsoft\WinGet\Packages\... i na PATH-u (važi u novootvorenom terminalu).
Config fajlovi:
~/.gitconfig (bekap pre izmena: ~/.gitconfig.bak-2026-09-19)~/.gitattributes (75 linija za mergiraf + blok za binarne formate)%APPDATA%\jj\config.tomlRadi sam, ništa se ne poziva ručno. Uključen je za merge, rebase, cherry-pick nad 75 tipova fajlova iz ~/.gitattributes.
git merge grana # mergiraf prvo probava sintaksni mergeŠta dobijaš: konflikti tipa "dve grane dodale različite funkcije/importe na isto mesto" prolaze bez intervencije. Isti konflikt bez mergiraf-a staje.
| Komanda | Šta radi |
|---|---|
mergiraf solve <fajl> | reši fajl koji već ima oznake konflikta |
mergiraf review <id> | pokaže šta je mergiraf odlučio (id ispiše on) |
mergiraf=0 git merge x | preskoči mergiraf za tu jednu komandu |
Ograničenja: samo tekstualni kod (ne binarni fajlovi), i nikad ne pogađa — pravi konflikt ostavlja sa oznakama.
Git ne proverava rešenje konflikta: čovek može upisati bilo šta, i to prođe (evil merge). Jedina komanda koja to pokaže:
git show --remerge-diff <merge-commit> # razlika: mehanički merge vs. commit-ovano
git log --remerge-diff # kroz istorijuZato posle svakog automatskog merge-a važe testovi, a ne vera u alat.
Uključivanje na postojećem repou (git ostaje, jj ide pored):
cd <repo>
jj git init --colocate
jj bookmark track master@origin # da lokalni master prati servergit status posle ovoga piše HEAD (no branch) — normalno.
Rad:
jj git fetch
jj new main@origin # počni novi commit na vrhu main-a
# ... menjaj fajlove; nema `git add`, jj snima sam ...
jj st # šta je izmenjeno
jj describe -m "Poruka" # poruka za trenutni commit
jj new # zatvori ga i počni sledeći
jj bookmark create moja-grana -r @-
jj git push --bookmark moja-granaUsklađivanje i dorada:
jj git fetch
jj rebase -d main@origin # konflikt ne prekida rad, zapiše se u commit
jj resolve --list # koji fajlovi su u konfliktu
jj resolve # pokreće mergiraf (podešen kao merge-editor)Vraćanje bilo čega:
jj op log # dnevnik svih operacija
jj undo # vrati poslednju
jj abandon <change-id> # baci commitPodešeno je da je sve što postoji na serveru nepromenljivo:
"immutable_heads()" = "builtin_immutable_heads() | remote_bookmarks()"Provereno: jj describe nad push-ovanim commit-om vraća Error: Commit ... is immutable. Lokalni commit-i se menjaju normalno.
Posledica: dorada posle review-a ide novim commit-om, ne prepravkom:
jj new moja-grana
# ... ispravke ...
jj describe -m "Ispravke posle review-a"
jj bookmark move moja-grana --to @
jj git push --bookmark moja-granacore.autocrlf=true u sistemskom configu; jj po defaultu ne konvertuje krajeve linija → prvi commit menja svaku liniju u svim fajlovima. Rešeno globalno: working-copy.eol-conversion = "input-output".jj.exe nema longPathAware u manifestu, pa Windows limit od 260 znakova važi i kad je LongPathsEnabled=1. Repo putanja mora biti kratka (~<100). Simptom: Failed to write index file ... (os error 3).jj describe prvo)..gitignore nije uredan, u commit uleti i .env. Stroža varijanta: snapshot.auto-track = "none()" + jj file track.Isključivanje jj-a iz repoa: obriši .jj, pa git checkout master.
Podešeno globalno; radi u svakom repou, bez ičega u projektu:
git diff ugovor.pdf # vidi se tekst, ne "Bin 1433 -> 1433 bytes"
git log -p ponuda.docx # istorija sadržaja dokumenta
git show HEAD:ponuda.docx | pandoc --to=plain # jedna verzija kao tekstProvereno na dve verzije istog ugovora — diff je pokazao samo izmenjene stavke (rok 30→45 dana, cena 1000→1200 EUR).
Kako je podešeno:
diff.pdf.textconv = sh -c "pdftotext -layout -q \"$0\" -"
diff.docx.textconv = pandoc --to=plain --wrap=none
+ cachetextconv=true (keš konverzije, drugi diff je instant)
~/.gitattributes: *.pdf binary diff=pdf ; *.docx binary diff=docx ; *.exe/*.dll binaryDetalji koje treba znati:
binary je obavezan, inače git PDF vidi kao tekst i CRLF konverzija ga pokvari.pdftotext mora da piše na stdout (-), zato sh -c omotač.--stat i dalje piše Bin ... bytes; tekst se vidi u punom diff-u.git diff --no-textconv isključuje konverziju.# XLSX preko CSV-a (traži xlsx2csv)
git config --global diff.xlsx.textconv "xlsx2csv"
echo '*.xlsx binary diff=xlsx' >> ~/.gitattributes
# EXE/DLL: spisak izvezenih funkcija umesto bajtova (traži VS build tools)
git config --global diff.exe.textconv 'sh -c "dumpbin /exports \"$0\""'Princip: oba fajla pretvori u tekst, pa uporedi tekst. Radi i kad fajlovi nisu u repou.
Pretvaranje u tekst po formatu:
| Format | Komanda |
|---|---|
| DOCX | pandoc --to=plain --wrap=none a.docx |
pdftotext -layout -q a.pdf - | |
| XLSX | xlsx2csv a.xlsx (nije instalirano) |
docdiffCLI koji sve gornje radi sam (~/.claude/docdiff.js, na PATH-u kao docdiff):
docdiff v1.docx v2.docx # dva DOCX-a
docdiff ugovor.pdf ugovor.docx # razliciti formati
docdiff t1.xlsx t2.xlsx # tabeleIzlaz (podrazumevano po rečima):
--- v1.docx (pandoc)
+++ v2.docx (pandoc)
Rok: [-30-]{+45+} dana
Garancija: [-12 meseci-]{+24 meseca+}
{+Napomena: dodata stavka+}| Opcija | Efekat |
|---|---|
--lines | klasičan diff po redovima |
--blank | ne čisti prazne redove (tačniji prelom, više šuma) |
--raw | ispiše samo pretvoren tekst fajla A |
--keep | ostavi .txt fajlove i javi gde su |
--no-color | bez boja |
Izlazni kod: 0 isti sadržaj, 1 ima razlika, 2 greška — može u skript i u hook.
Formati i čime se pretvaraju: PDF preko pdftotext; DOCX, ODT, RTF, PPTX, EPUB, HTML, MD preko pandoc; XLSX, XLS, DOC, PPT preko LibreOffice-a (soffice --headless); TXT, CSV, JSON, XML direktno. Ako pandoc padne, sam pređe na LibreOffice.
Ručni recepti ispod su korisni kad treba nešto posebno (drugi format prikaza, filtriranje).
pandoc --to=plain --wrap=none v1.docx > a.txt
pandoc --to=plain --wrap=none v2.docx > b.txt
git diff --no-index --word-diff a.txt b.txtIzlaz označava izmene unutar reda: Rok: [-30-]{+45+} dana. --word-diff je bolji od običnog diff-a za tekst pisan u rečima.
Napomena o <(...): konstrukcija radi u Git Bash-u sa MSYS alatima (diff, grep, pandoc), ali NE sa native Windows programima kao git.exe. Bash prosledi putanju /dev/fd/63, koju git vidi kao /proc/<pid>/fd/63 i ne ume da je otvori (error: Could not access '/proc/1532/fd/63'). Zato za git diff prvo upiši u prave fajlove, a diff -u <(...) <(...) može direktno. U PowerShell-u <(...) ne postoji uopšte — to je druga stvar.
Za ugovore i dopise je Word: Review → Compare ipak najbolji, jer poredi strukturu dokumenta i rezultat daje kao praćene izmene. Gornji recept je za skript i za brz pregled.
Može, jer se poredi tekst, a ne format:
pdftotext -layout -q ugovor.pdf - > p.txt
pandoc --to=plain --wrap=none ugovor.docx > d.txt
diff -u -B p.txt d.txt-B ignoriše razliku u praznim linijama — bez njega svaki pasus ispada kao izmena, jer pandoc ostavlja prazan red između pasusa, a pdftotext ne.
Šta se poredi, a šta ne:
Znači: dobro za "da li je promenjen rok ili cena", nije za "da li dokument izgleda isto".
cp ~/.gitconfig.bak-2026-09-19 ~/.gitconfig # git podešavanja
winget uninstall jj-vcs.jj
winget uninstall Mergiraf.Mergiraf~/.gitattributes obriši ručno ako ga ne želiš (pre setup-a nije postojao).
Git LFS, WinMerge, Beyond Compare, radare2, ImageMagick, restic — vidi binarni-fajlovi-verzionisanje-i-diff za kada šta uzeti.