gitで差分ファイルを抽出する

  • 429
    いいね
  • 1
    コメント

案件で「作業の差分を納品してくれ」とか言われることってよくあります。
今までは手作業でディレクトリ作って、ファイルをコピーしてましたが、
もう、そんなうんざりする作業とはおさらばできそうです。
git archivegit diff の合わせ技で差分を出力できる事がわかったからです。

例えば、一個前のコミットから現在のコミットまでの差分を取り出したい時は、

git archive --format=zip --prefix=root/ HEAD `git diff --diff-filter=d --name-only HEAD^ HEAD` -o archive.zip

まずは、git archive について。
--format=zip を付けるとzipで固めてくれます。
--prefix=root/ は抽出したファイルをrootディレクトリに入れた状態にしてくれます。
-o archive.zip で出力先と出力名を指定しています。
HEAD は抽出元のコミットで、
抽出したいファイルやディレクトリをgit diffを使って指定しています。

git diff--name-only を付けると、ファイルのパスを返してくれるのです。
--diff-filter=d オプションで不要な削除の差分をフィルタリングすると、
差分ファイルに必要なファイルパス一覧が取得できます。
例では、HEAD^HEAD のコミット間での差分のファイル一覧が返るので、
それを git archive がzipにしてくれるというわけです。

関数化した

毎回コマンド書くのは辛いので、
関数化してみました。
zshとbashで動作を確認しました。

function git_diff_archive() 
{
  local diff=""
  local h="HEAD"
  if [ $# -eq 1 ]; then
    if expr "$1" : '[0-9]*$' > /dev/null ; then
      diff="HEAD~${1} HEAD"
    else
      diff="${1} HEAD"
    fi
  elif [ $# -eq 2 ]; then
    diff="${2} ${1}"
    h=$1
  fi
  if [ "$diff" != "" ]; then
    diff="git diff --diff-filter=d --name-only ${diff}"
  fi
  git archive --format=zip --prefix=root/ $h `eval $diff` -o archive.zip
}

引数なしで git_diff_archive と呼ぶと HEAD を丸っとzipにします。

git_diff_archive

引数に数値を指定すると、HEADHEAD~数値 の差分を抽出します。
つまり、数値分前のコミットからの差分になります。

git_diff_archive 数値

引数にコミット識別子(IDとかHEADとかHEAD^とかdiffの引数として使えるもの)を指定すると、
HEAD と コミット識別子 の差分を抽出します。
IDで一発指定したい時とか便利です。
この時、HEADよりも新しいIDとかは渡さないようにしてください。
想定している抽出物を得られない可能性が高いです。

git_diff_archive コミット識別子

コミット識別子を2つ渡すと、その2つのコミットの差分を抽出します。
ちょっと前のバージョンの差分が欲しいとか言われたときに使えます。
渡す順番は新しいコミット、古いコミットの順番で渡してください。
間違うと、想定している抽出物を得られない可能性が高いです。

git_diff_archive コミット識別子1 コミット識別子2

何かおかしなところがありましたら、
やさしいツッコミお待ちしております。

修正履歴

2015/06/25
引数を1つだけ渡すときに、数字で始まるコミットIDを渡すとエラーが起きていました。

2015/06/24
差分にファイルの削除が含まれると、それも差分として一覧に追加されてしまい、
zipに固めるときにファイルがなくてエラーになってました。

2016/11/05
git diff に渡すコミット識別子の順番を入れ替えました。
以前の順番だと、新しいコミットから古いコミットを比べた差分になってしまい、
差分が上手く取得できないケースがありました。
それにともない --diff-filterD から d に変更しました。