git diff によって新規作成ファイルの差分も出力したい

# 結論
GitHub Actions のシェル芸とかで、`git diff` によって差分を知りたいけど、新規作成ファイルを見落としてしまうのに困ったことはあるだろう。
その場合、下記を実行すればいい。
※ 下記は、`terragrunt init` による自動生成が実行されていることを確認するためのシェルスクリプトの一部である。
```
untracked_files=$(git ls-files --others --exclude-standard)
echo "SSSSSSSSSS_903672 untracked_files:"
echo "$untracked_files"
echo "$untracked_files" | xargs -r git add -N
if git diff --quiet; then
  echo "SSSSSSSSSS_345636 No changes detected by terragrunt init and terragrunt run-all init."
else
  echo "EEEEEEEEEE_945798 Changes detected by terragrunt init and terragrunt run-all init."
  git diff
  exit 1
fi
echo "$untracked_files" | xargs -r git reset
```
`git diff` の前に `git add -N .` だけしか実行しないと、削除したファイルがステージングエリアに上がり、`git diff` で表示されなくなってしまう。なので、トラックされてないファイルに対してのみ、`git add -N` を実行するようにしている。



# 知識
- `git diff`
  - `git diff` はステージングエリアとワーキングディレクトリとの差分を表示する
  - `git diff --cached` はステージングエリアと最新コミットとの差分を表示する 
- `git add -N .`
  - `-N` オプションは、`--intent-to-add` オプションの省略形
- `git reset`
  - `git reset HEAD .` や `git reset .` や `git reset HEAD` と全く同じ意味
  - `--mixed` オプションが省略されている

# 検証
## (A)
```
~/practice$ ls -la
total 16
drwxr-xr-x  3 hyperhirokiwsl2 hyperhirokiwsl2 4096 Feb 21 21:02 .
drwxr-x--- 29 hyperhirokiwsl2 hyperhirokiwsl2 4096 Feb 21 20:56 ..
drwxr-xr-x  8 hyperhirokiwsl2 hyperhirokiwsl2 4096 Feb 21 20:56 .git
-rw-r--r--  1 hyperhirokiwsl2 hyperhirokiwsl2  132 Feb 21 20:51 README.md
```
### (1) ファイルの変更
#### (a-1)
`README.md` に変更を加えた。
```
~/practice$ git diff --name-only # 正常終了
README.md
~/practice$
```

#### (a-2)
`README.md` に変更を加えた。
```
~/practice$ git add -N . # ステージングエリアに置かれない
~/practice$ git diff --name-only # 正常終了
README.md
~/practice$ 
```

#### (b-1)
`README.md` に変更を加えた。
```
~/practice$ git diff --name-only --quiet # エラー終了
~/practice$
```
#### (b-2)
`README.md` に変更を加えた。
```
~/practice$ git add -N .
~/practice$ git diff --name-only --quiet # エラー終了
~/practice$
```
#### (c-1)
`README.md` に変更を加えた。
```
~/practice$ git diff --name-only --exit-code # エラー終了
README.md
~/practice$
```

#### (c-2)
`README.md` に変更を加えた。
```
~/practice$ git add -N .
~/practice$ git diff --name-only --exit-code # エラー終了
README.md
~/practice$
```



### (2) ファイルの削除
#### (a-1)
`README.md` を削除した。
```
~/practice$ git diff --name-only # 正常終了
README.md
~/practice$
```
#### (a-2)
`README.md` を削除した。
`git add -N .` すると、**削除したファイルはステージングエリアに置かれる**。
```
~/practice$ git add -N . # 削除したファイルはステージングエリアに置かれる
~/practice$ git diff --name-only # 正常終了
~/practice$ 
```
#### (b-1)
`README.md` を削除した。
```
~/practice$ git diff --name-only --quiet # エラー終了
~/practice$
```
#### (b-2)
`README.md` を削除した。
`git add -N .` すると、**削除したファイルはステージングエリアに置かれる**。
```
~/practice$ git add -N . # 削除したファイルはステージングエリアに置かれる
~/practice$ git diff --name-only --quiet # 正常終了
~/practice$
```
#### (c-1)
`README.md` を削除した。
```
~/practice$ git diff --name-only --exit-code # エラー終了
README.md
~/practice$
```

#### (c-2)
`README.md` を削除した。
`git add -N .` すると、**削除したファイルはステージングエリアに置かれる**。
```
~/practice$ git add -N . # ステージングエリアに置かれる
~/practice$ git diff --name-only --exit-code # 正常終了
~/practice$
```



### (3) ファイルの作成
#### (a-1)
`aaa.txt` を作成した。
```
~/practice$ git diff --name-only # 正常終了
~/practice$
```
#### (a-2)
`aaa.txt` を作成した。
```
~/practice$ git add -N . # ステージングエリアに置かれない
~/practice$ git diff --name-only # 正常終了
aaa.txt
~/practice$
```
#### (b-1)
`aaa.txt` を作成した。
```
~/practice$ git diff --name-only --quiet # 正常終了
~/practice$
```
#### (b-2)
`aaa.txt` を作成した。
```
~/practice$ git add -N . # ステージングエリアに置かれない
~/practice$ git diff --name-only --quiet # エラー終了
~/practice$
```

#### (c-1)
`aaa.txt` を作成した。
```
~/practice$ git diff --name-only --exit-code # 正常終了
~/practice$
```
#### (c-2)
`aaa.txt` を作成した。
```
~/practice$ git add -N . # ステージングエリアに置かれない
~/practice$ git diff --name-only --exit-code # エラー終了
aaa.txt
~/practice$
```





## (B)
```
~/practice$ tree -a -L 4 -I ".git"
.
├── README.md
├── aaa.txt
└── bbb.txt

0 directories, 3 files
```
### (1) 何もしていない状態
#### (a)
```
~/practice$ git diff --name-only # 正常終了
~/practice$ git diff --name-only --cached # 正常終了
~/practice$ git ls-files --others --exclude-standard # 正常終了
~/practice$ git ls-files # 正常終了
README.md
aaa.txt
bbb.txt
~/practice$ git ls-files --stage # 正常終了
100644 2f86896032d8150964f0ba6f15f0ee682d31b8cf 0       README.md
100644 ad07d1002f06c3ac06855fcb038ecb5d176f9e59 0       aaa.txt
100644 1ed96ea944516fff72e3782b4f201bccabee1924 0       bbb.txt
```
### (2) ファイル変更
#### (a-1)
`README.md` に変更を加えた。
```
~/practice$ git diff --name-only  # 正常終了
README.md
~/practice$ git diff --name-only --cached # 正常終了
~/practice$ git ls-files --others --exclude-standard # 正常終了
~/practice$ git ls-files  # 正常終了
README.md
aaa.txt
bbb.txt
~/practice$ git ls-files --stage # 正常終了
100644 2f86896032d8150964f0ba6f15f0ee682d31b8cf 0       README.md
100644 ad07d1002f06c3ac06855fcb038ecb5d176f9e59 0       aaa.txt
100644 1ed96ea944516fff72e3782b4f201bccabee1924 0       bbb.txt
~/practice$ 
```
#### (a-2)
`README.md` に変更を加えた。
```
~/practice$ git add -N . # 正常終了
~/practice$ git diff --name-only # 正常終了
README.md
~/practice$ git diff --name-only --cached # 正常終了
~/practice$ git ls-files --others --exclude-standard # 正常終了
~/practice$ git ls-files 
README.md
aaa.txt
bbb.txt
~/practice$ git ls-files --stage # 正常終了
100644 2f86896032d8150964f0ba6f15f0ee682d31b8cf 0       README.md
100644 ad07d1002f06c3ac06855fcb038ecb5d176f9e59 0       aaa.txt
100644 1ed96ea944516fff72e3782b4f201bccabee1924 0       bbb.txt
~/practice$ 
```

### (3) ファイル削除
#### (a-1)
`README.md` を削除した。
```
~/practice$ git diff --name-only # 正常終了
README.md
~/practice$ git diff --name-only --cached # 正常終了
~/practice$ git ls-files --others --exclude-standard # 正常終了
~/practice$ git ls-files # 正常終了
README.md
aaa.txt
bbb.txt
~/practice$ git ls-files --stage # 正常終了
100644 2f86896032d8150964f0ba6f15f0ee682d31b8cf 0       README.md
100644 ad07d1002f06c3ac06855fcb038ecb5d176f9e59 0       aaa.txt
100644 1ed96ea944516fff72e3782b4f201bccabee1924 0       bbb.txt
```

#### (a-2)
`README.md` を削除した。
```
~/practice$ git add -N . # 正常終了
~/practice$ git diff --name-only # 正常終了
~/practice$ git diff --name-only --cached # 正常終了
~/practice$ git ls-files --others --exclude-standard # 正常終了
~/practice$ git ls-files # 正常終了
README.md
aaa.txt
bbb.txt
~/practice$ git ls-files --stage # 正常終了
100644 2f86896032d8150964f0ba6f15f0ee682d31b8cf 0       README.md
100644 ad07d1002f06c3ac06855fcb038ecb5d176f9e59 0       aaa.txt
100644 1ed96ea944516fff72e3782b4f201bccabee1924 0       bbb.txt
```


### (4) ファイル作成
#### (a-1)
`ccc.txt` を作成した。
```
~/practice$ git diff --name-only # 正常終了
~/practice$ git diff --name-only --cached # 正常終了
~/practice$ git ls-files --others --exclude-standard # 正常終了
ccc.txt
~/practice$ git ls-files # 正常終了
README.md
aaa.txt
bbb.txt
~/practice$ git ls-files --stage # 正常終了
100644 2f86896032d8150964f0ba6f15f0ee682d31b8cf 0       README.md
100644 ad07d1002f06c3ac06855fcb038ecb5d176f9e59 0       aaa.txt
100644 1ed96ea944516fff72e3782b4f201bccabee1924 0       bbb.txt
```

#### (a-2)
`ccc.txt` を作成した。
```
~/practice$ git add -N . # 正常終了
~/practice$ git diff --name-only # 正常終了
ccc.txt
~/practice$ git diff --name-only --cached # 正常終了
~/practice$ git ls-files --others --exclude-standard # 正常終了
~/practice$ git ls-files # 正常終了
README.md
aaa.txt
bbb.txt
ccc.txt
~/practice$ git ls-files --stage # 正常終了
100644 2f86896032d8150964f0ba6f15f0ee682d31b8cf 0       README.md
100644 ad07d1002f06c3ac06855fcb038ecb5d176f9e59 0       aaa.txt
100644 1ed96ea944516fff72e3782b4f201bccabee1924 0       bbb.txt
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       ccc.txt
```

# 参考
https://reboooot.net/post/how-to-check-changes-with-git/

https://qiita.com/null_candy/items/67b9c55adf52bbb75ceb

https://maku693.hatenablog.jp/entry/2021/04/05/200000

https://stackoverflow.com/questions/855767/can-i-use-git-diff-on-untracked-files

https://git.vger.kernel.narkive.com/yH88SS4R/diff-new-files-without-using-index

https://reasonable-code.com/git-add-n/

https://stackoverflow.com/questions/12994197/how-to-remove-only-intent-to-add-files-from-index-in-git/12994240