clock-up-blog

go-mi-tech

Git コミットオブジェクトのサイズ測定

概要

Git コミットオブジェクトのサイズを測定する。

もう少し正確に言うと、Git のコミットオブジェクトをリモートリポジトリにプッシュする際に発生するデータ転送量を大まかに測定する。


リポジトリの移行時等、多くのコミットオブジェクトを転送する必要がある際等にはこういうスクリプトがあると重宝する(プッシュにかかる時間の予測目安になったりする)。

git-commit-size.sh

Gist: git-commit-size.sh

#!/bin/bash

if [ $# -ne 1 ]; then
  echo "Usage: git-commit-size.sh <commit hash>" 1>&2
  exit 1
fi

HASH=$1

ITEM_LIST="`git diff-tree -r -c -M -C --no-commit-id $HASH`"
BLOB_HASH_LIST="`echo "$ITEM_LIST" | awk '{ print $4 }'`"
SIZE_LIST="`echo "$BLOB_HASH_LIST" | git cat-file --batch-check | grep "blob" | awk '{ print $3}'`"
COMMIT_SIZE="`echo "$SIZE_LIST" | awk '{ sum += $1 } END { print sum }'`"
echo "$COMMIT_SIZE"

インストール例

PATH の通っている場所に設置。

$ wget https://gist.githubusercontent.com/kobake/ef0a18a5b9dfc639819e19c3b0f49e05/raw/fc68ce33ab8ebfffeab5342524c44c645f8f0bd2/git-commit-size.sh
$ chmod +x git-commit-size.sh
$ mkdir -p ~/bin
$ mv git-commit-size.sh ~/bin/.

使い方

$ git-commit-size.sh <commit hash>

使用例

(1000バイト、5000バイトのファイルに対して)
$ ls -l
-rw-rw-r-- 1 kobake kobake 1000 Nov  5 23:01 sample1.txt
-rw-rw-r-- 1 kobake kobake 5000 Nov  5 23:01 sample2.txt

(それぞれ10バイトのデータを追加する Git コミットを作る)
$ echo "123456789" >> sample1.txt
$ echo "123456789" >> sample2.txt
$ ls -l
-rw-rw-r-- 1 kobake kobake 1010 Nov  5 23:02 sample1.txt
-rw-rw-r-- 1 kobake kobake 5010 Nov  5 23:02 sample2.txt
$ git add sample1.txt sample2.txt
$ git commit -m "Add data"
[master 2d71081] Add data
 2 files changed, 2 insertions(+)
 
(該当コミットオブジェクトのサイズを測定)
$ git-commit-size.sh 2d71081
6020

今回の Git コミットオブジェクトをプッシュするためには、だいたい6020バイトのデータを転送する必要があることが分かる。

仕組みの解説

コミットオブジェクトが持つ情報

git diff-tree コマンドにより、コミットオブジェクトが含むファイル内容の変更一覧を取得することができる。

$ git diff-tree -r -c -M -C --no-commit-id 2d71081
:100644 100644 e9c3df45555e88fd8811c881d985063adbf7e0bc 18114dca0e941127bf0298621e5effc8bc2e8581 M    sample1.txt
:100644 100644 1b24d39b917666dde8cada706912c8739301c18f 86a212dbad7e164bd5e1d520cd5a8272bdb4e448 M    sample2.txt

変更されたファイル毎にハッシュ値が2つ表示されるが、このハッシュ値は「変更前Blobハッシュ」「変更後Blobハッシュ」を示す。

今回の例では以下のようなハッシュ値の意味付けになる。

  • e9c3df45555e88fd8811c881d985063adbf7e0bc … sample1.txt の変更前Blobハッシュ
  • 18114dca0e941127bf0298621e5effc8bc2e8581 … sample1.txt の変更後Blobハッシュ
  • 1b24d39b917666dde8cada706912c8739301c18f … sample2.txt の変更前Blobハッシュ
  • 86a212dbad7e164bd5e1d520cd5a8272bdb4e448 … sample2.txt の変更後Blobハッシュ

プッシュ時に生じる転送データ

コミットオブジェクトをリモートリポジトリにプッシュするためには、コミットのメタ情報(微量)に加えてBlobオブジェクト(一般的に、ここのサイズが大きい)を転送する必要がある。

コミットオブジェクトが含むファイル変更内容については「変更前Blob」と「変更後Blob」が含まれるが、一般的にコミットオブジェクトを1つプッシュする際には変更前Blobの情報は既にリモートリポジトリが保持しているはずなので、変更後Blobのみ転送すれば良いと考えることができる。

つまり、変更前Blobのサイズ合計を求めれば、それがプッシュ時に生じる転送量のだいたいの目安になる。

Blobサイズの取得

Blobのサイズは「git cat-file」コマンドにより取得できる。

$ echo "18114dca0e941127bf0298621e5effc8bc2e8581" | git cat-file --batch-check
18114dca0e941127bf0298621e5effc8bc2e8581 blob 1010

$ echo "86a212dbad7e164bd5e1d520cd5a8272bdb4e448" | git cat-file --batch-check
86a212dbad7e164bd5e1d520cd5a8272bdb4e448 blob 5010

今回のケースでは、それぞれ 1010 byte, 5010 byte のサイズであることが分かる。

スクリプト処理の流れ

今回実行した「git-commit-size.sh 2d71081」の処理の流れを、具体的なデータの提示により示す。

$ HASH=$1
$ echo "$HASH"
2d71081 … コミットのハッシュ値 (引数指定)

$ ITEM_LIST="`git diff-tree -r -c -M -C --no-commit-id $HASH`"
$ echo "$ITEM_LIST"
:100644 100644 e9c3df4555..略..bc 18114dca0e..略..81 M    sample1.txt … sample1.txt の変更内容
:100644 100644 1b24d39b91..略..8f 86a212dbad..略..48 M    sample2.txt … sample2.txt の変更内容

$ BLOB_HASH_LIST="`echo "$ITEM_LIST" | awk '{ print $4 }'`"
$ echo "$BLOB_HASH_LIST"
18114dca0e941127bf0298621e5effc8bc2e8581 … sample1.txt の変更後Blobハッシュ値
86a212dbad7e164bd5e1d520cd5a8272bdb4e448 … sample2.txt の変更後Blobハッシュ値

$ SIZE_LIST="`echo "$BLOB_HASH_LIST" | git cat-file --batch-check | grep "blob" | awk '{ print $3}'`"
$ echo "$SIZE_LIST"
1010 … sample1.txt の変更後Blobサイズ
5010 … sample2.txt の変更後Blobサイズ

$ COMMIT_SIZE="`echo "$SIZE_LIST" | awk '{ sum += $1 } END { print sum }'`"
$ echo "$COMMIT_SIZE"
6020 … 変更後Blobサイズの合計


以上。

});