gitで更新のあったファイルに対してformatter(goimports)をかける
CIでlintのついでにformatter(gofmt, goimports, gofumpt)がかかっているかチェックしている環境があるとする。そこでformatされていないと怒られたファイルに対してformatterをかけたい。その方法のメモ。
あるコミットで変更されたファイルを集める
対象のコミットで変更されたファイルの一覧を集めてformatterにかけたい
git show --stat HEAD
をparseしてみる?
例えば対象のコミットをHEADということにすると git show --stat HEAD
の変更を利用するということが考えられる
以下の様な形。
$ git show --stat HEAD | grep -F '|' xxx/gen/mock/handler.go | 3 +- yyy/gen/mock/router.go | 3 +- zzz/gen/mock/next_state.go | 5 +- aws/s3/gen/mock/s3.go | 3 +- .../aws/aws-sdk-go/service/sqs/sqsifacemocks/SQSAPI.go | 122 +++++++++++------------ # 対象のファイルの一覧 $ git show | grep -F '|' | cut -d '|' -f 1 xxx/gen/mock/handler.go yyy/gen/mock/router.go zzz/gen/mock/next_state.go aws/s3/gen/mock/s3.go .../aws/aws-sdk-go/service/sqs/sqsifacemocks/SQSAPI.go
あとは一覧が取れればすんなり渡して行けば良いということに一見なりそうなのだけれど。そうはならない。ものによってはファイル名の出力が省略されてしまうことがある。
.../aws/aws-sdk-go/service/sqs/sqsifacemocks/SQSAPI.go
これが完全なファイルパスではないのでちょっと不便。
暫定的で頑健な普段の手癖
普段はあまり考えずにとりあえずbasenameだけあっていれば対象にしちゃって良いでしょ、と以下の様なコードを手癖で書いていたけれどもう少しまともなワンライナーを検討しても良いかもしれない。
for i in $(git show --stat HEAD | grep '|' | cut -d '|' -f 1); do find . -name "$(basename $i)"; done | xargs goimports -w
同一のbasenameのものが含まれるのでformatterに渡されるファイルが増える可能性はあるのだけれど、実用上はあまり問題にならない。まぁでももう少しキレイな方法を見出したい。
--stat=<column>
manを覗いてみると以下のようなことが書かれている。デフォルトのターミナルのcolum sizeは80でその値を元に良い感じに表示を調節しているよう。
$ man git-show ... --stat[=<width>[,<name-width>[,<count>]]] Generate a diffstat. By default, as much space as necessary will be used for the filename part, and the rest for the graph part. Maximum width defaults to terminal width, or 80 columns if not connected to a terminal, and can be overridden by <width>. The width of the filename part can be limited by giving another width <name-width> after a comma. The width of the graph part can be limited by using --stat-graph-width=<width> (affects all commands generating a stat graph) or by setting diff.statGraphWidth=<width> (does not affect git format-patch). By giving a third parameter <count>, you can limit the output to the first <count> lines, followed by ... if there are more. These parameters can also be set individually with --stat-width=<width>, --stat-name-width=<name-width> and --stat-count=<count>.
なのでテキトウに --stat
に大きめの数値を渡してあげれば大丈夫かもしれない。
$ git show --stat=100000000 HEAD | grep -F '|' | cut -d '|' -f 1 ... test/mocks/github.com/aws/aws-sdk-go/service/sqs/sqsifacemocks/SQSAPI.go
まぁ暫定的には。。
--name-status
たまたまmanをのぞいて見つけた --name-status
オプションの方が目的にはあっているかもしれない。
$ man git-show ... --name-status Show only names and status of changed files. See the description of the --diff-filter option on what the status letters mean.
以下の様な出力になる。変更されたもの(M)や追加されたもの(A)だけを取り出せるのでこちらのほうが良いかもしれない。
$ git show --name-status HEAD ... M xxx/gen/mock/handler.go M yyy/gen/mock/router.go M zzz/gen/mock/next_state.go M aws/s3/gen/mock/s3.go M test/mocks/github.com/aws/aws-sdk-go/service/sqs/sqsifacemocks/SQSAPI.go
一応これで大丈夫か調べてみる。
$ git log --stat | grep -F '...' | head -n 1 .../aws-sdk-go/service/sqs/sqsifacemocks/SQSAPI.go | 122 ++++++++++----------- $ git log --stat=1000000 | grep -F '...' | head -n 1 $ git log --name-status | grep -F '...' | head -n 1
大丈夫そう。
現在変更されているファイル
ついでに現在変更されているファイルにも使えるか試してみる(git reset HEAD~~
などでテキトウにそういう状況を再現して)。
現在変更されているファイルを対象に一覧したい場合。
$ git diff --name-status
addされたファイルのみを対象にしたい場合。
$ git diff --staged --name-status
というわけで一覧が欲しかったら以下の様な形にすれば良さそう。
$ git diff --name-status | grep -P '^(A|M)\t' | cut -f 2
まとめ
更新されたファイルの一覧が欲しかったら --name-status
が便利。git show
に限らず色々なコマンドで使える。