投稿

git pull で出る gc.log 警告と unreachable loose objects

git pull で出る gc.log 警告と unreachable loose objects

遭遇したエラー

git pull 実行時に以下の警告が出た。

Auto packing the repository in background for optimum performance.
See "git help gc" for manual housekeeping.
warning: The last gc run reported the following. Please correct the root cause
and remove .git/gc.log
Automatic cleanup will not be performed until the file is removed.

warning: There are too many unreachable loose objects; run 'git prune' to remove them.

pull 自体は成功するが、警告が毎回出続ける状態になる。

警告の意味を分解する

1. gc.log が残っている

過去にバックグラウンドで自動実行された git gc(”Auto packing the repository in background” のやつ)が何かを警告して終了した。その内容が .git/gc.log に書かれている。

2. 自動 gc が止まっている

Automatic cleanup will not be performed until the file is removed. の通り、.git/gc.log が存在する間、Git は以降の自動 gc をスキップする。同じ失敗を繰り返さないためのセーフガード。gc.log を消さない限り自動メンテが永久に止まる。

3. unreachable loose objects が多すぎる

どこからも参照されていない(ブランチ・タグ・reflog から辿れない)孤立した object ファイルが .git/objects/ 配下に大量に積もっていた。git gc は通常 2 週間の猶予を持って残すので自動では消えず、ユーザーに git prune を促した。

つまり「自動 gc が unreachable object の多さを検知 → 破壊的な prune は自動でやらない → ログ残して停止」という状態。

原因

「unreachable loose object が積もる」原因は、コミットや object を作った後にその参照を捨てる操作を繰り返したこと。代表的なのは以下。

  • git rebase / git commit --amend — 元のコミットは新コミットに置き換わって参照を失う。rebase で 10 commit 書き換えれば 10 個の orphan ができる
  • git reset --hard で先祖に戻す — 戻した分のコミットが宙に浮く
  • ブランチの削除 — マージ済みでないブランチを -D で消すと、そのブランチ先端以降が孤立
  • git fetch で古い ref が捨てられる — リモートが force-push されていた等
  • 大量の git stash 作成 → drop

reflog(デフォルト 90 日、unreachable は 30 日)に残っている間は「unreachable だけど reflog 経由で reachable」なので prune 対象外。reflog から落ちて初めて「真の unreachable」になり、それが閾値(デフォルト 6700 個前後)を超えると git gc --auto が今回の警告を出す。

特定の 1 つの操作が原因というより、長期間運用しているリポジトリでの累積で起きる。

解消手順

git prune --expire=now    # 2週間より新しい unreachable object も全部削除
git gc                    # パック化・整理
rm .git/gc.log            # 警告のトリガを除去

prune の破壊性に注意

git prune --expire=now2 週間以内の unreachable object も即削除する。最近 reset / rebase で捨てた commit を git reflog 経由で復活させたい可能性があるなら使わない方がいい。

安全側でいくなら --expire=now を外してデフォルト(2 週間)に任せる。

git prune    # 2週間より古い unreachable object のみ削除
git gc
rm .git/gc.log

確認コマンド

現在の状態を把握するには以下が役立つ。

# reflog 保持期間(デフォルト未設定なら 90 日 / 30 日)
git config gc.reflogExpire
git config gc.reflogExpireUnreachable

# reflog エントリ数
git reflog --all | wc -l

# 自動 gc の閾値
git config gc.auto

# repo の整合性チェック
git fsck --no-dangling

予防

長期運用するリポジトリでは、定期的に git gc を手動で回すか、gc.auto の閾値を下げてマメに走らせると累積を防げる。

git config gc.auto 256

まとめ

  • gc.log の存在は自動 gc が止まっているサイン
  • 原因は rebase / amend / branch 削除の長期累積
  • 解消は git prunegit gcrm .git/gc.log の 3 ステップ
  • --expire=now は強力だが reflog からも消えるので reset 取り消しの保険を捨てることになる

トレンドのタグ