たまに使うのですが、その度に調べたりしているのでまとめておきました。
patchファイルを作成する
git diff
git diffの場合はシンプルです。今変更しているファイルの差分を出力するだけです。
$ git diff > file.patch
git log
git logの場合はそのままだと、リポジトリ全てのログが表示されてしまうのでリビジョン指定やファイル指定を行うことがほとんどです。
よく使うのは特定ファイルの直近コミットの差分、特定ファイルの指定コミットの差分ですね。
# fileの直近コミットの差分
$ git log -u -1 file > file.patch
# fileの指定コミットの差分
$ git log -u -1 {コミットのhash値} file > file.patch
no-prefixオプション
git diffやgit logはルートディレクトリからの相対パスにprefixをつけた状態でファイルパスを表示します。
--- a/file
+++ b/file
これが、patchコマンドで適応するときどう影響してくるかは後述しますがno-prefixオプションをつけるとこのprefixを無くすことができます。
--no-prefix
Do not show any source or destination prefix.
実際にno-prefixオプションをつけてみると下記のように出力されます。
--- file
+++ file
patchファイルを適応する
patchファイルの適応にはpatchコマンドを使用します。
$ patch < file.patch
このコマンドを実行するパスが非常に大事になってきます。
git diffによって出力されたファイルパスをpatchコマンドを実行したパスを起点にファイルを探します。
この挙動をpオプションで制御することができます。
pオプション
-pnum or --strip=num
Strip the smallest prefix containing num leading slashes from each file name found in the patch file. A sequence of one or more adjacent slashes is counted as a single
slash. This controls how file names found in the patch file are treated, in case you keep your files in a different directory than the person who sent out the patch.
For example, supposing the file name in the patch file was
/u/howard/src/blurfl/blurfl.c
setting -p0 gives the entire file name unmodified, -p1 gives
u/howard/src/blurfl/blurfl.c
without the leading slash, -p4 gives
blurfl/blurfl.c
and not specifying -p at all just gives you blurfl.c. Whatever you end up with is looked for either in the current directory, or the directory specified by the -d
option.
ヘルプに記載されている通りですが要約すると下記のようになるかと思います。
- オプションが指定されていない場合はファイル名のみ
- 0を指定するとルートディレクトリからの相対パス
- 0以外の数字を指定すると、指定した数分のスラッシュまでを削る
なので、ルートディレクトリでpatchコマンドを実行する際は-p0をつけるとよいのですが、
先述のno-prefixオプションを使っていない場合には実際のパスには存在しないa/,b/が付与されているので-p0を使いたい場合にはno-prefixをつけようということです。
no-prefixをつけない場合には-p1をつけてa/,b/を無視すればよいので、要はどっちで考慮するかということです。
# ルートディレクトリでno-prefixをつけて出力
$ git diff --no-prefix > file.patch # patch出力
$ git checkout . # 変更を元に戻す
$ patch -p0 < file.patch # 変更を適応する
# ルートディレクトリでno-prefixをつけないで出力
$ git diff > file.patch # patch出力
$ git checkout . # 変更を元に戻す
$ patch -p1 < file.patch # 変更を適応する
たとえ、間違えてもpatchコマンドはファイルが見つからないときに、下記プロンプトが出てカレントディレクトリからのパスを記載すれば適応してくれます。
File to patch:
適応した変更を元に戻す
patchコマンドのRオプションを使えばできます。
$ patch -R < file.patch
Gitで管理されているファイルでこんなことをやっても、なんも面白くないですが
例えばサーバ上はGit管理されていなくて、同じディレクトリ構成のローカル環境がGitで管理されている場合などに
patchファイル作成して適応すれば便利かもしれません。