SouceTreeでコミット間の差分ファイルを抽出するカスタムアクション

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/417736/1afcbf49-6dcb-f2a8-890a-3d9a78e776e0.png" alt="git" width="232">

SourceTreeのカスタムアクションに登録すると、2点のコミット間のフォワード差分とリバート差分をサクッとアーカイブで出力出来るので、デプロイでやらかした時など緊急の切り戻し資材として便利。
## シェル

ポイントは`git diff`で`--name-status --diff-filter={差分シンボル}`オプションでコミット間の指定の差分ファイルを抽出できる

```sh:sh
git diff --name-status --diff-filter=ACMRD コミットハッシュ(from) コミットハッシュ(to)
A		{path/to/追加ファイル}
C		{path/to/コピー元ファイル}		{path/to/コピー先ファイル}
M		{path/to/変更ファイル}
R100	{path/to/リネーム前ファイル}	{path/to/リネーム後ファイル} < R*はリネーム認識精度 R
D		{path/to/削除ファイル}
```

### Mac版 - zshスクリプト版

**forward**

コミットハッシュ`$from`から`$to`の次の差分ファイルを`$to`コミットから抽出してアーカイブする

- --diff-filterで抽出する差分
  - `変更(M)` - (awk:$2)
  - `追加(A)` - (awk:$2)
  - `コピー(C)` - コピー先ファイル(awk:$3)
  - `リネーム(R)` - 変更後ファイル(awk:$3)
- --diff-filterで抽出しない差分
  - `削除(D)`されたファイル・・・`$to`コミットには存在しないためエラーになる

```sh:sh
#!/bin/bash

if [ "$2" = "" ]; then
  to=HEAD
  from=${1}
else
  to=${1}
  from=${2}
fi

git archive --format=zip --prefix=forward/ ${to} `git diff --name-status --diff-filter=AMCR ${from} ${to} | awk '/^R|^C/ {print $3; next} /^A|^M/ {print $2}'` -o forward.zip
```

**revert**

コミットハッシュ`$from`から`$to`の次の差分ファイルを`$from`コミット時点から抽出してアーカイブする

- --diff-filterで抽出する差分
  - `変更(M)` - (awk:$2)
  - `リネーム(R)` - 変更前ファイル(awk:$2)
  - `削除(D)` - 削除されたファイル(awk:$2)
- --diff-filterで抽出しない差分
  - `追加(A)`・・・`$from`コミットには存在しないためエラーになる
  - `コピー(C)`・・・`$from`コミットには存在しないためエラーになる

```sh:sh
#!/bin/bash

if [ "$2" = "" ]; then
  to=HEAD
  from=${1}
else
  to=${1}
  from=${2}
fi

git archive --format=zip --prefix=revert/ ${from} `git diff --name-status --diff-filter=MRD ${from} ${to} | awk '{print $2}'` -o revert.zip
```

### Windows - コマンド版

* バッチではコマンド結果をワンライナーで中継できないので、ファイルリストを抽出してから`git archive`にわたす
* `awk`コマンドは[\[busybox](http://frippery.org/busybox/)からバイナリを取得&どこかに展開してシステム環境変数のパスを通しておく

**forward**

```powershell:powershell
if "%2" EQU "" (
  set PARAM1=HEAD
  set PARAM2=%1
) else (
  set PARAM1=%1
  set PARAM2=%2
)

setlocal enabledelayedexpansion
set RET_DIR=
for /F "usebackq" %%i in (`git diff --name-status --diff-filter=AMCR %PARAM2% %PARAM1% ^| awk "/^R/ {print $3; next} /^C/ {print $3; next} /^A/ {print $2; next} /^M/ {print $2}"`) do (
  set RET_DIR=!RET_DIR! "%%i"
)

git archive --format=zip --prefix=forward/ %PARAM1% %RET_DIR% -o forward.zip
```

**revert**

```powershell:powershell
if "%2" EQU "" (
  set PARAM1=HEAD
  set PARAM2=%1
) else (
  set PARAM1=%1
  set PARAM2=%2
)

setlocal enabledelayedexpansion
set RET_DIR=
for /F "usebackq" %%i in (`git diff --name-status --diff-filter=MRD %PARAM2% %PARAM1% ^| awk "{print $2}"`) do (
  set RET_DIR=!RET_DIR! "%%i"
)

git archive --format=zip --prefix=revert/ %PARAM2% %RET_DIR% -o revert.zip
```
## SrouceTreeで使う

### カスタムアクションにスクリプトを登録する

上記スクリプトファイルとパラメータに$SHA(コミットハッシュ)を指定するとコミットハッシュを引数にしてスクリプトを実行してくれる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/417736/86416d7a-eacc-fefe-1385-b85cb57ed8d5.png)



### アーカイブを作成する

2点のコミットを選択する > コンテキストメニュー > カスタムアクション > スクリプトを選択

![image 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/417736/12433139-9213-8ccf-8456-b740be444083.png)

### 出力されたアーカイブ

こいつを展開したらforward/revertのファイル差分が入っている

```bash
ls -lav /path/to/this_repository

total 200
drwxr-xr-x  16 user  staff    512  2 16 21:51 .git
-rw-r--r--   1 user  staff  16701  2 16 21:54 forward.zip < これ
...
```