Git Advent Calendar / Jun. 28日目の記事です。27日目はつるはしで過去を発掘するでした。
Git リポジトリの外で git diff / git apply
あまり知られていないことですが、 git diff
と git 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
で適用できます。もう diff
と patch
には戻れませんね。
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 ...
とします。git
と apply
の間にオプションを書くことに注意してください( 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
で作ったパッチを、コミットを作らずに適用したいときにも使えます。