clock-up-blog

go-mi-tech

Gitの話:不要になったコミットオブジェクトが削除される瞬間を観測する

Git Advent Calendar 2017 - Qiita 3日目

どこからも参照されなくなった(ブランチやタグ等に参照されなくなった)コミットオブジェクトはガベージコレクタの仕組みによりいつか削除されることになる。今回はその削除機構を即座に動作させてみる。

まずコミット2個を準備

$ mkdir -p /c/projects/sample
$ cd /c/projects/sample
$ git init

$ git commit -m "Initial Commit" --allow-empty
[master (root-commit) 4b54c0b] Initial Commit

$ git commit -m "Second Commit" --allow-empty
[master c46ae46] Second Commit

f:id:kobake:20171204200720p:plain:w500

このコマンドにより新しいコミット「4b54c0b」「c46ae46」が作成された。
コミット「4b54c0b」はコミット「c46ae46」に参照されているため、不要とはみなされない。
コミット「c46ae46」は HEAD および master ブランチに参照されているため、不要とはみなされない。

どこからも参照されない孤独なコミットオブジェクトの再現

どこからも参照されない孤独なコミットオブジェクトは、たとえば以下のような操作により生成される。

$ git commit --amend --allow-empty -m "Second Commit3"
[master 76f9ecf] Second Commit3

f:id:kobake:20171204200358p:plain:w450

「git commit --amend」により新しいコミット「76f9ecf」が作成され、HEAD および master ブランチが「76f9ecf」を向く形となる。

この時点で旧コミット「c46ae46」は履歴上どこからも参照されることがなく、ガベージコレクタ(git gc)対象になったかのように見える。

ガベージコレクタ前

ガベージコレクタが実行される前の時点では依然として コミット「c46ae46」は存在している。
そのような状態であるから「git log」コマンドにより内容の参照も行うことすらできる。

$ git log -1 c46ae46 --oneline
c46ae46 Second Commit

実際に不要コミットが削除されるタイミング

ガベージコレクタ処理は、利用ユーザが意識することなく適当なタイミングで自動実行(実際には何か別の操作処理に不随してガベージコレクタが自動実行されることが多い)されることが多い。

明示的なガベージコレクタ

「git gc」コマンドにより明示的にガベージコレクタを実行することができる。

$ git gc

しかし実はこれでけでは不要(に見える)コミットオブジェクトがすぐに消える可能性は低い。
実際には HEAD ポインタやブランチ参照の他にもさまざまな機構がコミットオブジェクトを参照している。

たとえば reflog のこと等を忘れがちだったりするのだが(この reflog も一時的ではあるが各種コミットオブジェクトへの参照を持っている)、そのあたりも考慮した上で「git gc」実行すると良い。

(reflogが持つ情報をクリア)
$ git reflog expire --expire=now --all

(再度 gc し直し)
$ git gc --aggressive --prune=now

結果

上述の「git reflog expire」および「git gc」により、不要コミット「c46ae46」の削除が確認された。

$ git log -1 c46ae46
fatal: ambiguous argument 'c46ae46': unknown revision or path not in the working tree.


今日の話はここまで。

});