gitでnumstatを利用して総追加行数、総削除行数を取得するワンライナー

上期も終わり開発中のgitリポジトリの情報を採取して遊んでいました。Qiitaでコミット数や総追加行数、総削除行数を取得するワンライナーについての投稿があり活用させて頂いています。ありがたい...!

リンク先のワンライナーでコミット数は正確に計れるのですが、総追加行数と総削除行数がなにかおかしい。なぜか総追加行数が非常に多く計算されていました。

というわけで原因を調べてみました。

原因1. 1ファイルのみの変更の時にgrepがスルーされる

1ファイルのみの変更の場合、以下のような出力になります。

$ git log --shortstat --oneline --no-merges

f86f749 Insert open graph meta tag contents when adding a link item refs #29
 1 file changed, 67 insertions(+), 33 deletions(-)

grep filesやるとどうやらひっかからないみたいです。

原因2. gitのshortstatでinsertionsやdeletionsが省略されていた

どうも--shortstatオプションを使用する場合、insertionsやdeletionsが0の場合に省略されるようです。

commit 4083c7554d996e1ee0199c9ab44d9352a14e1d3e
Author: 私
Date:   Fri Aug 22 08:10:12 2014 +0900

    Set item description's default value refs #34

 1 file changed, 6 insertions(+)

なのでどうやらQiitaに投稿されていらっしゃるお方のワンライナーでは、insertionsが0(省略される)、deletionsがあった場合にもinsertionsとしてカウントされるようです。畜生shortstat。

numstatを使うとうまくいくらしい

というわけでどうすれば上手くいくのか調べると、--numstatオプションを使うといいという情報がでてきました。

例:git log --numstat

Author: 私
Date:   Fri Sep 26 02:06:03 2014 +0900

    Remove duplicate gem refs #11

0       1       Gemfile

1番目の数値が追加行数、2番目の数値が削除行数です。0のときも省略されず出力されているのが確認できます。

numstatを使ったワンライナー

stackoverflowにnumstatを使用したワンライナーがありました。

$ git log --numstat --pretty="%H"  | awk 'NF==3 {plus+=$1; minus+=$2} END {printf("+%d, -%d\n", plus, minus)}'

How can I calculate the number of lines changed between two commits in git? - Stack Overflow

試しにgithubで開発している自分のレポジトリでやってみると...

$ git log --numstat --pretty="%H"  | awk 'NF==3 {plus+=$1; minus+=$2} END {printf("+%d, -%d\n", plus, minus)}'
+6339, -380

f:id:koyamay:20141006020925p:plain

合ってる!勝利! コミッターや期間を指定したい場合は--author--since--untilオプションを使えばokですね。

ワンライナー解説

awkについて全く知らない私なので有識者ワンライナーの解説をして頂きました。

$ git log --numstat --pretty="%H"  | awk 'NF==3 {plus+=$1; minus+=$2} END {printf("+%d, -%d\n", plus, minus)}'

よかろう。
NFはフィールドの数を示す定数だ。
なので、NF==3のときだけ、波括弧内が実行される
awkの変数には $ をつけないので、 plus, minus はそれぞれ変数名。
$1, $2は1番目、2番目のフィールドをそれぞれ指す。
ちなみに、$3で3番目、$NFで最終フィールドなどということもできる。
これでnumstatしたときに出てくる、 1   3   foo/bar.c みたいな行を拾ってそれぞれ加えていくわけ。 
続く、ENDは入力が尽きたあと(つまり最後)に条件が成立して、printfを実行する。
printfはC言語とかとだいたい同じだから、整形して出力するだけだな。

わかりやすすぎわろた。ありがとうございます有識者の方!お金絶対返します!