Jak vytvořit patch v Gitu

Git je nástroj pro správu verzí a hromadné sledování změn souborů v rámci jednoho projektu. Rozdíl dvou verzí souboru se označuje jako diff z anglického slova difference a popisuje nám jak se soubory navzájem liší.

Změnu jednoho souboru můžeme popsat textovým souborem s koncovkou .diff, který nám popisuje, jaké změny musíme provést, abychom se dostali z jedné verze na druhou. Tomuto vygenerovanému souboru se pak říká patch (záplata).

Takový soubor vypadá například takto:

diff --git a/file.txt b/file.txt
index 322e040..13f7c5d 100644
--- a/file.txt
+++ b/file.txt
@@ -1,2 +1,3 @@
-test modified content
+test modified 2 content
 new test line
+third new line
\ No newline at end of file

Poznámka: Pro vygenerování diff souboru lze na Unix-like systémech příkaz diff a pro aplikaci této záplaty zase příkaz patch.

K čemu je to dobré, když se nám Git o sledování změn stará automaticky? Patche se nám hodí když:

  • potřebujeme poslat změnu souboru nebo commit pomocí e-mailu, např. chceme poslat někomu opravu a jeho systém neumí přijímat pull requesty
  • pro přenos commitů z jednoho repozitáře do druhého
  • když musíme udělat změnu (opravu) v rámci složky, která se běžně ignoruje (typicky opravy ve složce vendor)
  • chceme si pro sebe zaznamenat nějakou změnu souboru, nebo opravu, aniž bychom vytvářeli složitě repozitář pro verzování

Patch vytváříme vždy vůči nějakým změnám. První co tedy musíme udělat, je provést nějakou změnu v repozitáři. Pak je více způsobů jak patch vytvořit a záleží už na nás, co použijeme:

Jak vytvořit patch z commitu

Vytvoříme změnu a uděláme commit. V konzoli zadáme:

git format-patch -1 73511d3

kde 73511d3 je prvních 7 znaků z hashe provedeného commitu. To nám automaticky vytvoří soubor s příponou .diff. V programu SourceTree toto samé můžeme udělat pomocí Actions -> Create patch -> Patch from commits. Zde vybereme commit ze kterého chceme patch vytvořit.

Hash commitu můžeme zjistit pomocí:

git log --pretty=oneline -3

Což nám zobrazí poslední 3 commity, každý hezky na jednu řádku.

Příkaz format-patch nám vygeneruje rozšířený diff soubor v mbox formátu, který obsahuje i údaje o commitu a lze poslat přímo e-mailem:

From 42127fb46108f622d4f82d9178e65982be3f7984 Mon Sep 17 00:00:00 2001
From: Vojta Svoboda <vojtasvoboda.cz@gmail.com>
Date: Sat, 9 Aug 2014 10:58:35 +0200
Subject: [PATCH] New commit


---
 file.txt | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)


diff --git a/file.txt b/file.txt
index 08cf610..322e040 100644
--- a/file.txt
+++ b/file.txt
@@ -1 +1,2 @@
-test content
\ No newline at end of file
+test modified content
+new test line
-- 
2.0.0

Tento soubor lze poslat příkazem:

git send-email patch.diff

který používá interní funkci /usr/sbin/sendmail.

Jak vytvořit patch bez commitu

Vytvoříme změnu souborů. Pokud nemáme soubory v indexu (nejsou ve stage) napíšeme:

git diff > patch.diff

kde patch.diff je název souboru kam chceme patch uložit. Pokud chceme udělat diff ze souborů co jsou ve stage, tak použijeme příkaz:

git diff --cached > patch.diff

V programu SourceTree toto samé můžeme udělat pomocí Actions -> Create patch -> Patch from Working copy.

Jak vytvřit patch vůči jiné větvi

Jedna z dalších možností je vytvoření patche vůči nějaké větvi, typicky nějaká master větev kterou právě opravujeme v naší větvi. Provedeme opravu jedním, nebo více commity a zadáme:

git format-patch master --stdout > patch.diff

kde master je větev kterou opravujeme a aktuálně jsme přepnutý v naší větvi.

Aplikace patche

Nyní když máme patch vytvořený můžeme ho zkusit použít. Nejdříve se podíváme co patch obsahuje:

git apply --stat patch.diff

kde patch.diff je soubor s patchem, nám vypíše seznam změn, které patch provádí. Dále:

git apply --check patch.diff

nám provede kontrolu patche ještě než ho aplikujeme do naší větve. Pokud je vše v pořádku, zadáme:

git apply patch.diff

což nám aplikuje daný patch. Apply nám ale pouze změní dané soubory, nevytváří žádný commit. Pro kompletní rekonstrukci použijeme:

git am patch.diff

což je aplikovaní patche včetně merge do aktuální větve v přesné formě jak byl zamýšlen. Zkratka am znamená apply mail, tzn. přijmutí patche který jsme přijali e-mailem. Tento poslední příkaz nám vytvoří kompletní commit včetně nastavení autora a popisu (commit message). V programu SourceTree toto samé můžeme udělat pomocí Actions -> Apply patch a vybereme soubor s patchem.

Git bundles

Pro přenos historie commitů nebo celých větví, z jednoho repozitáře do druhého, můžeme také použít příkaz:

git bundle create changes.bundle master

Tento příkaz vlastně zastupuje příkazy git fetch a git pull, když zrovna nemáme dostupný příme síťové spojení do repozitáře.

Problém s chybou "patch does not apply"

V případě problémů s patchováním můžeme zkusit příkaz:

git apply --ignore-space-change --ignore-whitespace mychanges.patch