Git での新規ファイル作成を含んだファイル変更有無の判定方法
Written by @ryysud Dec 17, 2018 23:33 · 2144 words · 5 minutes read
TL;DR
- git add -N (–intent-to-add) で新規ファイル作成でもファイル変更として git diff で拾うことが可能になる
- git diff –exit-code で終了ステータス元にファイル変更有無をハンドリングすることが可能になる
- この2つを組み合わせて Git での新規ファイル作成を含んだファイル変更有無の判定が実現できる
Git での新規ファイル作成を含んだファイル変更有無の判定方法
Git の2つのコマンドを組み合わせて、新規ファイル作成を含んだファイル変更有無の判定を行うので、まずは2つのコマンドの解説をしていきます。なお、こちらの記事は Git での操作とファイルのバージョン情報がどのように遷移していくかのマッピングが理解できていることを前提に説明していきます。ちょっと不安な方は こちら をチェックするか、適当にググっておさらいしてみるのも良いかもしれません。
利用する Git のバージョン
$ git --version
git version 2.16.1
git add -N (–intent-to-add)
こちらのコマンドを実行することで「このファイルは後で git add しますよー」ということが宣言できます。あまりピンと来ていない方(自分が実際にそうでした…。)もいるかもしれませんが、この処理を実行することで新規に作成されたファイル(Untracked File)も git diff で差分(= 新規作成なのでファイルの全内容)を確認することが可能になります。百聞は一見に如かずなので、コマンドを実行しながら実際に動きをみていきます。
前準備として動作確認用のリポジトリ sample-repo を作成
$ mkdir sample-repo
$ cd sample-repo
$ git init
Initialized empty Git repository in /path/to/sample-repo/.git/
新規にファイルを作成した際の git diff の挙動
新規作成されたファイルは git status で Untracked files としては判定されていますが、stage/index に登録されていないので、git diff では何も出力されません。
$ echo 'This file is untracked file' > untracked_file
$ git status
On branch master
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
untracked_file
nothing added to commit but untracked files present (use "git add" to track)
$ git diff untracked_file
新規にファイルを作成した後で git add -N (–intent-to-add) した際の git diff の挙動
新規作成されたファイルは git status では変わらず Untracked files としては判定されていますが、git add -N (–intent-to-add) を実行した後だと git diff で変更内容が差分として出力されることがわかります。
$ git add -N untracked_file
$ git status
On branch master
No commits yet
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
new file: untracked_file
no changes added to commit (use "git add" and/or "git commit -a")
$ git diff untracked_file
diff --git a/untracked_file b/untracked_file
index e69de29..0e9c55c 100644
--- a/untracked_file
+++ b/untracked_file
@@ -0,0 +1 @@
+This file is untracked file
なお、注意事項として git add -N (–intent-to-add) は、あくまで「このファイルは後で git add しますよー」とう宣言をするだけなので、コミットする際には再度 git add を実行することが必須(git commit -a でも問題なし)となります。以下に例を記載しておきます。
git add しないとエラー終了となりコミットできない
$ git diff untracked_file
diff --git a/untracked_file b/untracked_file
index e69de29..0e9c55c 100644
--- a/untracked_file
+++ b/untracked_file
@@ -0,0 +1 @@
+This file is untracked file
$ git commit -m 'Add file'
On branch master
Initial commit
Changes not staged for commit:
new file: untracked_file
no changes added to commit
git add することでコミットできる
$ git add untracked_file
$ git commit -m 'Add file'
[master (root-commit) fc64f9d] Add file
1 file changed, 1 insertion(+)
create mode 100644 untracked_file
このように git add -N (–intent-to-add) で新規ファイル作成でもファイル変更として git diff で拾うことが可能になります。
git diff –exit-code
通常の git diff だと、ファイルの変更有無問わず、常に終了ステータスは 0 となりますが、git diff に –exit-code のオプションを付与することで、diff と同じように差分がないときは終了ステータスを 0 に、差分があるときは終了ステータスを 1 とすることが可能になります。
通常の git diff
ファイルの変更有無問わず、常に終了ステータスは 0 となる。
$ cat untracked_file
This file is untracked file
$ echo 'New line' >> untracked_file
$ git diff untracked_file
diff --git a/untracked_file b/untracked_file
index 0e9c55c..304274a 100644
--- a/untracked_file
+++ b/untracked_file
@@ -1 +1,2 @@
This file is untracked file
+New line
$ echo $?
0
git diff –exit-code
差分がないときは終了ステータスが 0 で、差分があるときは終了ステータスが 1 となる。
$ git diff --exit-code untracked_file
diff --git a/untracked_file b/untracked_file
index 0e9c55c..304274a 100644
--- a/untracked_file
+++ b/untracked_file
@@ -1 +1,2 @@
This file is untracked file
+New line
$ echo $?
1
このように git diff –exit-code で終了ステータス元にファイル変更有無をハンドリングすることが可能になります。
シェルスクリプト
上記2つのコマンドを用いて、以下の仕様のシェルスクリプトを書きました。
- sample-repo に新規ファイル作成を含んだ変更があった際に ‘There are changes’ という文字列を出力
- sample-repo に新規ファイル作成を含んだ変更がない際に ‘There are no changes’ という文字列を出力
git diff –exit-code で終了ステータスが 1 となり途中で処理が中断されないように set +e したり、git diff –exit-code で判定に不要な差分を出力しないために –quiet オプションを付与するなど、細かな制御もいれたシェルスクリプトとなっております。
$ cat check.sh
#!/usr/bin/env bash
set -e
TARGET_REPO='./sample-repo'
cd ${TARGET_REPO}
git add -N .
set +e
git diff --exit-code --quiet
if [[ $? -eq 1 ]]; then
echo 'There are changes'
else
echo 'There are no changes'
fi
新規ファイル作成を含んだ変更を一切加えていない sample-repo にシェルスクリプトを実行してみる
想定通り ‘There are no changes’ が出力された。
$ ll -d sample-repo
drwxr-xr-x 4 ryysud staff 136B 12 18 00:22 sample-repo
$ bash check.sh
There are no changes
sample-repo で新規にファイルを作成してシェルスクリプトを実行してみる
想定通り ‘There are changes’ が出力された。
$ echo 'New file' > ./sample-repo/new_file
$ bash check.sh
There are changes
sample-repo でファイルを変更してシェルスクリプトを実行してみる
想定通り ‘There are changes’ が出力された。
$ rm -f ./sample-repo/new_file
$ echo 'New line' >> ./sample-repo/untracked_file
$ bash check.sh
There are changes
また、当スクリプトとは関係ありませんが、判定処理の中でコミットをする際には git add -N (–intent-to-add) の後で再度 git add が必要なことは忘れずに。
さいごに
結構レアなユースケースだとは思いますが、インターネッツにまとまってるものがなかったので、今回記事としてまとめてみました。例として記載したシェルスクリプトは非常に単純なものなので、各位のユースケースに応じて適宜修正してご活用ください。では、良きシェル芸ライフを〜!