Git
AdventCalendar
GitDay 28

どこでも使える git diff と git apply

More than 5 years have passed since last update.

Git Advent Calendar / Jun. 28日目の記事です。27日目はつるはしで過去を発掘するでした。

Git リポジトリの外で git diff / git apply

あまり知られていないことですが、 git diffgit apply は Git リポジトリの外でも使えます。普通の diff ではできない、バイナリファイルを含む2つのディレクトリの差分を取りたいときなどに重宝します。ちょっと試してみましょう。

$ mkdir dir1 dir2
$ echo foo > dir1/text
$ echo FOO > dir2/text
$ dd if=/dev/random of=dir1/binary bs=128 count=1
$ dd if=/dev/random of=dir2/binary bs=128 count=1

テキストファイルとバイナリファイルが入ったディレクトリを2つ作りました。普通の diff ではバイナリファイルの差分を取ることはできません。

diff -u dir1 dir2

Binary files dir1/binary and dir2/binary differ
diff -u dir1/text dir2/text
--- dir1/text   2012-06-28 16:48:32.000000000 +0900
+++ dir2/text   2012-06-28 16:48:32.000000000 +0900
@@ -1 +1 @@
-foo
+FOO


一方 git diff --binary を使うとバイナリを含むパッチが得られます。

git diff --binary dir1 dir2

diff --git a/dir1/binary b/dir2/binary
index ce195bd3098cc3b7d9ea45170ae98463d131b3fd..00bbd22890c313431de290904e113305ce99fcab 100644
GIT binary patch
literal 128
zcmV-`0Du1;k;Pa0+FuT5!>8TMElMN9Z%UuV(Tp}ptw)JgHb%ThC}#@j)Zg{X4(Yeg
zfX+xCRLKBQKgWDS#D)-z(mJ@gT8~;}Z92E(d}<vQnpTw)nl?Q%1;W-Hx;2^6xj!Ef
i%}<GnpBm$~xF>{|;42RdCI?2*JSg#nOx8E2B9vaYi#yZ+

literal 128
zcmV-`0Du20VOk!F$T*e~S4k_&Yxd->rZ+!pu9lyoZIANBsyQkVLw$$bmPV%?s!+3Z
zj+TZ0Zo#oxfc8Q;@cJ9WFkrKwbXtAZUnnJp4CR=73c}zSDZ-yYRl8vKd&M5Dy#gCJ
ib#HN7*JJ}YBj;`sg?r+$yBS;CFfHAu4B@J0>lBK%$37nb

diff --git a/dir1/text b/dir2/text
index 257cc56..bc56c4d 100644
--- a/dir1/text
+++ b/dir2/text
@@ -1 +1 @@
-foo
+FOO

このパッチは git apply で適用できます。もう diffpatch には戻れませんね。

Git リポジトリの中で git diff / git apply

さて、 Git リポジトリの中にあるファイルやディレクトリの差分を取るには少し工夫が必要です。 git diff --binary HEAD:dir1 HEAD:dir2 のようにコミットを明示するか、 git diff--no-index オプションをつけてインデックスを無視します。

### Git リポジトリの外では普通に差分が取れる
$ git diff --binary dir1 dir2
diff --git a/dir1/binary b/dir2/binary
# (略)

### リポジトリを作ると——
$ git init .; git add .; git commit -m "commit"

### 差分が取れない
$ git diff --binary dir1 dir2
# (何も出力されない)

### コミットを明示すればいい
$ git diff --binary HEAD:dir1 HEAD:dir2
diff --git a/dir1/binary b/dir2/binary
# (略)

### --no-index を使ってもいい
$ git diff --no-index --binary dir1 dir2
diff --git a/dir1/binary b/dir2/binary
# (略)

また、こうして作ったパッチを Git リポジトリの中で適用するには、 git --git-dir= apply ... とします。gitapply の間にオプションを書くことに注意してください( apply の後ではありません)。

### Git リポジトリの中で dir1 と dir2 の差分を取って、
### dir1 に適用したい
$ git diff --no-index --binary dir1 dir2 > patch
$ cd dir1

### 元々の text の中身は "foo"
$ cat text
foo

### 普通に git apply してもパッチは当たらない
$ git apply -p2 ../patch
$ cat text
foo

### --git-dir= をつけるとパッチが当たる
$ git --git-dir= apply -p2 ../patch
$ cat text
FOO

git --git-dir= apply は、パッチに含まれるコミット情報を無視することができて便利です。たとえば、無関係な2つの Git リポジトリの一方でパッチを作り、もう一方に適用することができます。また、 git format-patch で作ったパッチを、コミットを作らずに適用したいときにも使えます。