🐱

Gitのコミット等は通常簡単には消えない

2023/10/05に公開


Gitのcommit等のオブジェクトはgit resetでは消えずgit stashもcommitオブジェクトを作成します。そうなるとdangling commitやdropされたはずのstash等の不要と思われるオブジェクトの実体はいつ消されるのかを調べた備忘です。明確になっていない部分があるため理解が間違っている可能性があります。

結論

最低でも90日間、通常はそれ以上の相当の期間自動では消えない。
それほど履歴保持能力が高くバージョン管理システムとして堅牢な設計になっている。

すぐに消したい時

  • ローカルリポジトリの場合
    # 到達不能なreflogエントリーを削除
    git reflog expire --expire-unreachable=now --all
    # 到達不能なオブジェクトを削除 (今よりも古い=全到達不能オブジェクト)
    git gc --prune=now
    
    • 到達可能な特定のファイルを削除する場合
      • git reset等で到達不能にしてから上記コマンド実行
      • 作業ツリー,ローカルリポジトリを全削除してgit clone
  • リモートリポジトリの場合

GitのGabage Collection概要

  • 到達不能なオブジェクトの掃除コマンドとしてgit gcが存在する
    • git fetchgit merge等を実行した時に自動でgit gc --autoが実行される
    • git gc --autoには実際に掃除するかの判断基準があり基準を超えない限り削除されない
    • デフォルト基準は約7000以上のオブジェクトファイル又は50以上のpackファイルの存在
      • 小規模なレポジトリでは絶対に自動では削除されない
    • packファイルはオブジェクトファイルを纏めたアーカイブファイルのようなもの
    • git gc --autoの判断基準を超えた場合、同時にreflogやrerere等の削除も実行される
  • 自身で作成したcommitオブジェクトはreflog(操作履歴のようなもの)から参照されている
    • dangling commitだとしてもreflogエントリーが消えない限り到達可能とみなされ消えない
    • これはcommitオブジェクトとして実装されているstashも同様
    • デフォルトでreflogの有効期間は90日のためその期間は消えない
    • 失効してもgit gc --autoの基準を超えない限り自動削除されずreflogも残り続ける
  • ローカルとリモートの整合性を保つためオブジェクトが十分古くならない限り削除されない
    • 以下のようなケースでレポジトリが壊れる
      1. リモート側で到達不能なオブジェクトを削除
      2. ローカルから1.で削除したオブジェクトを参照するcommitオブジェクトがpushされる
      3. リモート側でそのcommitオブジェクトがnull参照のような状態となり壊れる
    • 上記のような破壊を防ぐために古い,新しいというような有効期間で管理されている
    • blobオブジェクトが古くても新しいtreeオブジェクトから参照されれば削除対象にならない
    • treeオブジェクトが古くても新しいcommitオブジェクトから参照されれば削除対象にならない
  • git push --forceではリモートリポジトリに存在したcommitオブジェクトは消えない
    • 元々存在したcommitオブジェクトはdangling commitとして残る
    • 削除するためにはリモート側で到達不能なオブジェクトの削除処理が必要
    • GitHubでは定期的にgit gcが実行されている...らしいが詳細不明
  • git push,git pullでreflogは同期されない
    • 他リポジトリで作成されたdangling commitはgit gc --prune=nowのみで消える
  • 認証情報などgitで管理すべきでない情報は構造的にgitに追加されないような運用設計が重要

参考文献

Discussion