ファイル差分を抽出する方法(^p^)

#こんにちは！今日も元気に勉強しています！(*´꒳`*)


ついに、納品ファイルをまとめてほしい！という時が訪れたので、できるようになろうと頑張りました。

http://webtech-walker.com/archive/2012/09/git_export_diff.html
こちらのサイトを知り合いの方に教えてもらいトライしました。ありがとうございます！


もともとは、git archiveを使って差分を出していたのですが、上手くいかない時があるandこれだとちゃんとでないと指摘をいただいたので
やり方は今の方法にしました。。（私のやり方がわるいだけなのでしょうか。。><

以下コマンドで実行します。(iterm2で自分の場合は実行してます

[1]rootに移動
```
cd /
```
[2]bin内に実行ファイルをおく(ほかにも方法はあるかもしれませんが現時点ではここにおきました。

対象のプロジェクトで.gitのある一つ上の階層で、
```
git export-diff コミットid コミットid 吐き出しフォルダ
```
みたいなかんじで実行します。

実行しているファイルは、
https://gist.github.com/hokaccha/3764870
こちらのソースです！(私が作ったのではないです。><もっと頑張ります。
自分の勉強のために掻い摘んで実際に何がかいてあるのか、読み解いてみたいと思います。(間違っていたらそっと教えていただけると嬉しいです。

```lang:git-export-diff
#!/bin/sh

set -e

#ここで引数が足りない時はヒントを出すようにしています。
if [ $# -ne 2 -a $# -ne 3 ]; then
  echo 'Usage:'
  echo '  $ git export-diff <commit> <output_dir>'
  echo '  $ git export-diff <commit> <commit> <output_dir>'
  exit 1
fi

#ここで引数が2つだったら、それぞれがcommitidなのか、吐き出すdirなのか、の指定をしています
if [ $# -eq 2 ]; then
  COMMIT=$1
  OUT_DIR=$2
fi

#ここで引数が3つだったら、それぞれがcommitidなのか、吐き出すdirなのか、の指定をしています
if [ $# -eq 3 ]; then
  COMMIT="$1 $2"
  OUT_DIR=$3
fi

#diffコマンドを使って差分の対象となるファイルパスを取得しています。
#--name-onlyは、指定されたコミットでの変更ファイルの名前を吐き出すオプションです。

TARGETS=`git diff $COMMIT --name-only`

#引数で渡された名前のフォルダを作ります。
mkdir $OUT_DIR

#変更ファイル名をループ処理させます。
for target_file in $TARGETS
do
  # 実際に対象ファイルが存在しているのかチェック。
  if [ -e $target_file ]; then
    # いれます。
    outdir="${OUT_DIR}/`dirname $target_file`"
    # 実際にファイル含めたディレクトリがないとき
    if [ ! -d $outdir ]; then
      # 作る
      mkdir -p $outdir
    fi
    # コピーします。
    cp $target_file $outdir
  fi
done
```

というかんじだとよみました。

実際には、
```
$ git export-diff HEAD^ piyo
```
と実行すると
head(最新)とhead^(一つ前のコミット)との差分をpiyoにいれてくれました。
コミットidを一つにするとデフォルトでheadとの差分という形になるようです。

ちなみにpiyoフォルダは、
```
mkdir $OUT_DIR
```
で作ってくれるようなので、安心ですね！


これで作業が終わってほっとしてる時にくる納品ファイル格納もこわくない！(*´꒳`*)


[追記@2016/05]
上のものを使用していてmergeが入ったものが吐き出されてしまうと知り、うーん、、ちょっと考えたものがこちら。現在改修中です。期間が１日だった時にうまくとれないということを試しに使っていただいた隣の席のエンジニアさんからご指摘頂いているのでそこをとりあえず直そう、そのための時間を作ろうと思っています。flexibleじゃ全くないです、遊びで作った程度です。mumu

```
#!/bin/sh

set -e

#引数は3つ
if [ $# -lt 3 ]; then
  echo 'Usage: please set 3 params AT LEAST, dear!! '
  echo 'if your commiter name is two word for example tanaka fooo, please enter one word tanakafooo'
  echo 'コミット名がふたつ以上の単語からできている場合は、くっつけて入力してください。'
  echo 'はきだすファイルがある場合に、上書きしたくないときは5番目の引数に、imと追加してください。何もつけないと上書き'
  echo '  $ git export-diff-custom <commit> <output_dir> <author指定> <im(optional/immutable)>'
  echo '  $ git export-diff-custom <比較commit指定> <比較commit指定(optional)> <output_dir指定> <author指定> <im(optional/immutable)>'
  echo 'now, your params is....'$#
fi


# 最低数の3つパラメータをセットされたときの変数
if [ $# -eq 3 ]; then
  COMMIT_DIFF="$1"
  #パラメータを念のため補完
  COMMITB_ORIG="HEAD"
  OUT_DIR=$2
  AUTHOR="$3"
  #指定していない場合は上書き処理を許可
  OVERWRITE='ow'
  # 対象コミットのみの日にちを特定(yyyy-mm-dd)し、セット
  # TIME_DIFF=`git log --pretty=format:%ad --date=short $COMMIT_DIFF -1`
  # TIME_ORIG=`git log --pretty=format:%ad --date=short $COMMIT_ORIG -1`
  echo `git log --pretty=format:%ad --date=short $COMMIT_DIFF -1`
  # パラメーターチェック
  if [ $COMMIT_DIFF ] && [ $COMMITB_ORIG ] && [ $OUT_DIR ] && [ $AUTHOR ] && [ $TIME_DIFF ] && [ $TIME_ORIG ] ; then
    echo '[OK] パラメはちゃんとはいっています。順調です──=≡Σ((( つ•̀ω•́)つ'
  else
    echo '[ERROR1]以下の項目を確認してください。'
    echo 'コミットidをわすれている、吐き出しフォルダ名を指定していない、対象者を指定し忘れている。'
    echo '  $ git export-diff-custom <commit> <output_dir> <author指定> <over>'
  fi
fi

# 3つ以上のパラメータをセットされたときの変数
# a -gt b : a > b
if [ $# -gt 3 ]; then
  COMMIT_DIFF="$1"
  COMMITB_ORIG="$2"
  OUT_DIR=$3
  AUTHOR=$4
  echo $COMMIT_DIFF/$COMMITB_ORIG/$5
  if [ $5 ]; then
    echo 'hhh'
    #指定されたらow(overwrite)/im(immutable)に。
    OVERWRITE=$5
  else
    #指定していない場合は上書き処理を許可
    OVERWRITE='ow'
  fi

  # 対象コミットの日にちを特定(yyyy-mm-dd)し、セット
  TIME_DIFF=`git log --pretty=format:%ad --date=short $COMMIT_DIFF -1`
  echo $TIME_DIFF
  TIME_ORIG=`git log --pretty=format:%ad --date=short $COMMIT_ORIG -1`
  echo $TIME_ORIG
  if [ $COMMIT_DIFF ] && [ $COMMITB_ORIG ] && [ $OUT_DIR ] && [ $AUTHOR ] && [ $TIME_DIFF ] && [ $TIME_ORIG ] ; then
    echo '[OK] パラメはちゃんとはいっています。順調です─=≡Σ((( つ•̀ω•́)つ'
  else
    echo '[ERROR2]以下の項目を確認してください。'
    echo 'コミットidを指定わすれている、吐き出しフォルダ名を指定していない、対象者を指定し忘れている。'
    echo '  $ git export-diff-custom <比較commit指定> <比較commit指定> --<output_dir指定> <author指定>'
  fi
fi


# a -ge b : a => b 共通処理
if [ $# -ge 3 ]; then
  # 期間を指定してsha1を取り出す。
  # もじ指定期間が特定の日のみだったら
  echo 'aa'/${TIME_DIFF}/$TIME_ORIG
    _TIME_DIFF=`git log --pretty=format:%ad --date=iso $COMMIT_DIFF -1`
    _TIME_ORIG=`git log --pretty=format:%ad --date=iso $COMMIT_ORIG -1`
  echo $_TIME_DIFF/$_TIME_ORIG
  if [ ${TIME_DIFF} == $TIME_ORIG ]; then
    echo 'aa'
    _TIME_DIFF=`git log --pretty=format:%ad --date=iso $COMMIT_DIFF -1`
    _TIME_ORIG=`git log --pretty=format:%ad --date=iso $COMMIT_ORIG -1`
    echo $_TIME_DIFF/$_TIME_ORIG
    LOG=`git log --format=%H --after=${_TIME_DIFF} --before=${_TIME_ORIG}`
  else
    LOG=`git log --format=%H --since=$TIME_DIFF --until=$TIME_ORIG`
  fi


  #結合に最初スペースをいれるために用意
  SPACE=' '
  #マージログにスペースを追加した
  MERGE_LOG=$SPACE`git log --merges --format=%H --since=$TIME_DIFF --until=$TIME_ORIG`
  LOG_ALL=$LOG$MERGE_LOG
  ar="'${LOG_ALL}'"
  # 改行するためにLFにコードをセット
  LF=$(printf '\\\012_')
  LF=${LF%_}
  #sha1の一覧。改行されたものを作成
  a=`echo $LOG_ALL | sed 's/ /'"$LF"'/g'`
  # 重複行のみ出力
  # 重複していない行のみ出力(mergeコミットを除外)
  b=`echo "$a" | sort | uniq -u`
  # d=(`echo "$a" | sort | uniq -u`)
  c=($b)


  e=(`echo "${b[@]}"`)
  #重複したコミットを削除したものを全部改行した状態で、代入。
  array=`echo "${c[@]}"`
  for i in ${array[@]};
  do
    #対応のsha1 @return sha1(string)
    # echo '***********'
    # echo $i
    # echo '***********'
    #対応のコミットのコミット発行者 @return name(string)
    _aut_log=`git log ${i} --pretty=format:%an -1`
    #コミット発行者の単語数が複数だった時用の処理(スペースをなくす)
    aut_log=`echo $_aut_log | sed 's/ /''/g'`

    #対象のauthorではなかった場合は、処理をスキップし、次の要素にうつる
    if [ ${aut_log} != $AUTHOR ]; then
      echo "[SKIP] ${aut_log}/$AUTHOR Oops,,, Not match commiter. skip this log……ｼﾀﾀﾀｯ ﾍ(*¨)ﾉ"
      continue
    fi
    #特定のコミット１件のファイル抜き出しを実施
    obj=$SPACE`git log ${i} -1 --name-only --pretty=format:""`

    #結果をその都度最後尾に代入.result_file(対象の修正ファイル一覧)を作成
    result_file+=$obj
  done

  #ファイルの一覧。改行されたものを作成
  rf=`echo $result_file | sed 's/ /'"$LF"'/g'`
  #重複している場合はsort -uで最初の行だけ出力.uniqで、重複したものを特定。=> 実際の特定ひとが作業したファイルのリスト
  fooooooo=`echo "$rf" | sort -u | uniq`

  # 吐き出し口を作る
  # すでに同じ名前のフォルダがあったら、
  if [ -e $OUT_DIR ]; then
    # 上書き禁止
    echo ${OVERWRITE}
    if [ ${OVERWRITE} == 'im' ]; then
      # 時間を取得し、フォルダ名の末尾につける。
      TIME=`date "+_%Y-%m-%d-%Hh-%Mm-%Ss"`
      mkdir $OUT_DIR$TIME
      echo '[OK]上書き禁止モードなので、'$OUT_DIR$TIME'が作成されました。'
    # 上書きしてokだったら
    else
      # 既存フォルダを削除
      rm -rf $OUT_DIR
      mkdir $OUT_DIR
      echo '[OK]'$OUT_DIR'が継続作成されました。'
    fi
  # なかったら
  else
    mkdir $OUT_DIR
    echo '[OK]'$OUT_DIR'が新規作成されました。'
  fi

  for file in ${fooooooo[@]};
  do
    if [ -e $file ]; then
      outdir="${OUT_DIR}/`dirname $file`"
      if [ ! -d $outdir ]; then
        #mkdir -p　は、エラーメッセージを表示しない。指定したdirが存在しなければ作成され、存在する場合は、エラーメッセが出ずに、コマンド終了。
        mkdir -p $outdir
      fi

      cp $file $outdir
      echo $file/$outdir

      echo '[OK]' $file 'is copied.....!.._〆(Θωθ*) completed( ˘ω˘)'
    fi
  done

      echo '♩ ♫ ♬--♩ ♫ ♬---♩ ♫ ♬-♩ ♫ ♬---'
      echo '[FILE LIST]'
      echo ' '$fooooooo
      echo '[TOTAL NUM]...'${#fooooooo[@]} 'file is exported.(°∀°)'
      echo '-♩ ♫ ♬---♩ ♫ ♬----♩ ♫ ♬---♩-'

fi
#if [ $# -ge 3 ]END

echo '[END]Thanx!'

```