Help us understand the problem. What is going on with this article?

clocとシェルスクリプトでgit管理ソースの改修ステップ数を集計

More than 1 year has passed since last update.

やりたいこと

世の中にはソースコードの行数を知りたがる人がいます。

Gitリポジトリから詳細なステップ数を取得するツールとしてclocがあり、これだけで十分便利なのですが、一部のファイルはカウントから除外するとか、全体のステップ数と改修分のステップ数を出すとか、コメントを含めないのと含めたのとそれぞれ合算するとか、細かい集計を毎回手作業でやるのは地味に面倒です。

というわけで、シェルでJSONをいじる練習も兼ねて自動化してみました。

アウトプットイメージ

最終的にこんなものを作ります。

■ロジックのみ
・追加:1000
・修正:1000
・流用:1000
---
・計 :3000

■コメント・空行を含む
・追加:2000
・修正:2000
・流用:2000
---
・計 :6000

行単位で、追加されたものを「追加」、変更されたものを「修正」、変更のないものを「流用」としてカウントします。

実行環境

  • Windows 10 PC
    • 基本的にはLinuxでもできることしかやらないので、CI環境にもっていくこともできると思います
  • git
  • git bash(シェルの実行環境)
  • cloc
    • clocコマンドが使えるようにPATHを通しておきます
    • Windows用のexeもありますが、npmからグローバルインストールすることも可能
  • jq
    • BashでJSONを扱うため
    • Windowsではscoopなどからインストールできます
    • PATHを通しておきます

概要

以下のようなことをやります。

  • Gitリポジトリからクローン
  • 全ステップ数、差分ステップ数をclocで取得し、json形式で出力
  • jsonをjqに渡し、必要な情報だけ取り出して集計
  • テキストファイルとして保存

事前準備

ベースコミットのハッシュ値確認

改修のBeforeとなるgitコミットのハッシュ値を調べておきます。

このコミットと最新コミット(HEAD)までの差分ステップ数から、追加/修正/流用を判定します。

言語識別キーの確認

対象のソースが配置されているルートディレクトリで clocコマンドを打ってみます。

例としてPrismのソースであれば、以下のような結果が出力されます。

$ cloc ./
     895 text files.
     883 unique files.
     278 files ignored.

github.com/AlDanial/cloc v 1.78  T=3.01 s (278.4 files/s, 25393.2 lines/s)
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
C#                             743          13034          11411          47593
MSBuild script                  39            143            106           2043
JSON                             4              0              0            623
XAML                            27             39              4            459
Markdown                         6            130              0            333
YAML                             8             40              0            182
XML                             10             49            128             64
PowerShell                       1             11              2             29
-------------------------------------------------------------------------------
SUM:                           838          13446          11651          51326
-------------------------------------------------------------------------------

左端の「Language」の中で、カウントの対象とする言語の表記を確認しておきます。
ここでは「XML」「C#」「XAML」を対象とすることにします。

カウントしたい言語が結果に出てこない場合、言語定義を自分で作成する必要があります。
詳細はCreate Custom Language Definitionsを参照。

無視リスト作成

一部のファイル/フォルダをカウント対象から除外したい場合、無視リストを作っておいて指定することができます。

ここでは stepignore.txt という名前で以下のようなファイルを作りました。

Sandbox/Windows10/SampleData
Sandbox/Xamarin

その他いろいろオプションがあるので詳しくはREADMEを。

シェルスクリプト

はじめに完成品を載せます。

#!/bin/env bash

# Gitリポジトリ
readonly GIT_REPO=https://github.com/PrismLibrary/Prism.git
# 差分比較元のコミットハッシュ
readonly BASE_COMMIT=2a89100f
# 収集対象言語
readonly LANGS='["XAML","C#","XML"]'

# リポジトリから一時フォルダへclone
git clone $GIT_REPO ./_work
cd _work

# clocでステップ数を取得
step_total=`cloc --vcs=git --json --exclude-list-file=../stepignore.txt`
step_diff=`cloc --json --exclude-list-file=../stepignore.txt --diff $BASE_COMMIT HEAD`

# 一時フォルダの削除
cd ..
rm -rf ./_work

# 全体のステップ数(ロジック、コメント)
logic_total=`echo $step_total | jq '[ .'$LANGS'.code ] | add'`
comment_total=`echo $step_total | jq '[ .'$LANGS'.comment,.'$LANGS'.blank ] | add'`

# 追加・修正分のステップ数(ロジック、コメント)
logic_added=`echo $step_diff | jq '.added | [ .'$LANGS'.code ] | add'`
logic_modified=`echo $step_diff | jq '.modified | [ .'$LANGS'.code ] | add'`
comment_added=`echo $step_diff | jq '.added | [ .'$LANGS'.comment,.'$LANGS'.blank ] | add'`
comment_modified=`echo $step_diff | jq '.modified | [ .'$LANGS'.comment,.'$LANGS'.blank ] | add'`

# 流用分を算出
logic_same=$((logic_total-logic_added-logic_modified))
comment_same=$((comment_total-comment_added-comment_modified))

# 出力
{
  echo "■ロジックのみ"
  echo "・追加:"$logic_added
  echo "・修正:"$logic_modified
  echo "・流用:"$logic_same
  echo "---"
  echo "・計 :"$logic_total
  echo ""
  echo "■コメント・空行を含む"
  echo "・追加:"$((logic_added+comment_added))
  echo "・修正:"$((logic_modified+comment_modified))
  echo "・流用:"$((logic_same+comment_same))
  echo "---"
  echo "・計 :"$((logic_total+comment_total))
}>./step-result.txt

以下で解説します。

定数

以下は環境に合わせて書き換えます。
コミットハッシュと対象言語は事前準備で調べたものを使用してください。
ハッシュは頭4ケタもあれば普通は十分らしいです。念のため8ケタ書きました。

# Gitリポジトリ
readonly GIT_REPO=https://github.com/PrismLibrary/Prism.git
# 差分比較元のコミットハッシュ
readonly BASE_COMMIT=2a89100f
# 収集対象言語
readonly LANGS='["XAML","C#","XML"]'

LANGSはカンマ区切りの部分にスペースを入れるとダメっぽいので注意。

clocでステップ数を取得

全体のステップ数と、ベースコミットからの差分とを順に取得しています。

step_total=`cloc --vcs=git --json --exclude-list-file=../stepignore.txt`
step_diff=`cloc --json --exclude-list-file=../stepignore.txt --diff $BASE_COMMIT HEAD`

先ほど作った無視リストを --exclude-list-file オプションで指定しています。

totalのほうで --vcs=git を指定する代わりに cloc ./ という感じでディレクトリを指定することもできるのですが、その場合は .gitignore を考慮してくれないので注意が必要です。

それと、 --diff で実行すれば same, modified, added(, removed) が出力されるので、これらを合算すればトータルステップ数になりそうなものなんですが、このsameの判定がちょっと怪しくて、合算しても行数が一致しません。

なので、トータルステップ数は別で取得し、そこからadded,modifedを引いたものを流用分とカウントすることにします。

全体のステップ数を取り出す

全体ステップ数を取得するclocコマンドの戻り値は以下のような形になります。

{
  "header": {
    "cloc_url": "github.com/AlDanial/cloc",
    "cloc_version": "1.78",
    "elapsed_seconds": 2.93183207511902,
    "n_files": 838,
    "n_lines": 76423,
    "files_per_second": 285.828102882046,
    "lines_per_second": 26066.6361653396
  },
  "C#": {
    "nFiles": 743,
    "blank": 13034,
    "comment": 11411,
    "code": 47593
  },
  "MSBuild script": {
    "nFiles": 39,
    "blank": 143,
    "comment": 106,
    "code": 2043
  },
  "JSON": {
    "nFiles": 4,
    "blank": 0,
    "comment": 0,
    "code": 623
  },
  "XAML": {
    "nFiles": 27,
    "blank": 39,
    "comment": 4,
    "code": 459
  },
  "Markdown": {
    "nFiles": 6,
    "blank": 130,
    "comment": 0,
    "code": 333
  },
  "YAML": {
    "nFiles": 8,
    "blank": 40,
    "comment": 0,
    "code": 182
  },
  "XML": {
    "nFiles": 10,
    "blank": 49,
    "comment": 128,
    "code": 64
  },
  "PowerShell": {
    "nFiles": 1,
    "blank": 11,
    "comment": 2,
    "code": 29
  },
  "SUM": {
    "blank": 13446,
    "comment": 11651,
    "code": 51326,
    "nFiles": 838
  }
}

これをjqコマンドに渡して、対象言語のステップ数を集計します。

logic_total=`echo $step_total | jq '[ .'$LANGS'.code ] | add'`
comment_total=`echo $step_total | jq '[ .'$LANGS'.comment,.'$LANGS'.blank ] | add'`

jqの構文はちょっとクセがありますが、 logic_total は C#,XML,XAMLの code の値を取ってきて合算したもの、 comment_totalcommentblank の値を取ってきて合算したものになっています。

追加・修正のステップ数を取り出す

差分のclocコマンドの戻り値は以下のような形になります。

{
  "header": {
    "cloc_url": "github.com/AlDanial/cloc",
    "cloc_version": "1.78",
    "elapsed_seconds": 5.76067495346069,
    "n_files": 322,
    "n_lines": 31889,
    "files_per_second": 55.8962278901989,
    "lines_per_second": 5535.63605959799
  },
  "added": {
    "XAML": {
      "blank": 6,
      "code": 95,
      "nFiles": 2,
      "comment": 0
    },
    "JSON": {
      "blank": 0,
      "code": 498,
      "nFiles": 1,
      "comment": 0
    },
    "XML": {
      "blank": 23,
      "code": 15,
      "nFiles": 3,
      "comment": 59
    },
    "Markdown": {
      "comment": 0,
      "nFiles": 1,
      "blank": 24,
      "code": 118
    },
    "YAML": {
      "comment": 0,
      "nFiles": 0,
      "blank": 4,
      "code": 14
    },
    "MSBuild script": {
      "comment": 19,
      "nFiles": 2,
      "blank": 11,
      "code": 324
    },
    "C#": {
      "comment": 455,
      "code": 3365,
      "blank": 657,
      "nFiles": 67
    }
  },
  "same": {
    "XAML": {
      "blank": 0,
      "code": 43,
      "nFiles": 0,
      "comment": 0
    },
    "JSON": {
      "blank": 0,
      "code": 0,
      "nFiles": 0,
      "comment": 0
    },
    "XML": {
      "blank": 0,
      "code": 9,
      "nFiles": 0,
      "comment": 40
    },
    "Markdown": {
      "comment": 0,
      "nFiles": 0,
      "blank": 0,
      "code": 130
    },
    "YAML": {
      "comment": 0,
      "nFiles": 0,
      "blank": 0,
      "code": 106
    },
    "MSBuild script": {
      "comment": 60,
      "nFiles": 0,
      "blank": 0,
      "code": 1138
    },
    "C#": {
      "comment": 1177,
      "code": 13850,
      "blank": 0,
      "nFiles": 0
    }
  },
  "modified": {
    (略)
  },
  "removed": {
    (略)
  },
  "SUM": {
    "added": {
      "nFiles": 76,
      "code": 4429,
      "blank": 725,
      "comment": 533
    },
    "same": {
      "nFiles": 0,
      "code": 15276,
      "blank": 0,
      "comment": 1277
    },
    "modified": {
      "nFiles": 178,
      "code": 2822,
      "blank": 0,
      "comment": 17
    },
    "removed": {
      "nFiles": 68,
      "code": 4184,
      "blank": 832,
      "comment": 1794
    }
  }
}

これをjqに渡し、addedとmodifiedをそれぞれ取り出します。

logic_added=`echo $step_diff | jq '.added | [ .'$LANGS'.code ] | add'`
logic_modified=`echo $step_diff | jq '.modified | [ .'$LANGS'.code ] | add'`
comment_added=`echo $step_diff | jq '.added | [ .'$LANGS'.comment,.'$LANGS'.blank ] | add'`
comment_modified=`echo $step_diff | jq '.modified | [ .'$LANGS'.comment,.'$LANGS'.blank ] | add'`

出力

書式はお好みで。

{
  echo "■ロジックのみ"
  echo "・追加:"$logic_added
  echo "・修正:"$logic_modified
  echo "・流用:"$logic_same
  echo "---"
  echo "・計 :"$logic_total
  echo ""
  echo "■コメント・空行を含む"
  echo "・追加:"$((logic_added+comment_added))
  echo "・修正:"$((logic_modified+comment_modified))
  echo "・流用:"$((logic_same+comment_same))
  echo "---"
  echo "・計 :"$((logic_total+comment_total))
}>./step-result.txt

以上です。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away