cmd/go: only set vcs.modified=true if changes are relevant to build
要約
概要
go build や go install 実行時、ビルドに無関係なファイルがリポジトリに存在するだけで vcs.modified=true が設定され、バイナリに +dirty サフィックスが付いてしまう問題を修正するproposal。変更されたファイルが実際のビルドに影響するかどうかを判断した上で vcs.modified を設定するよう改善する。
ステータス変更
likely_accept → accepted
2026年6月10日に「likely accept」となり、1週間後の2026年6月18日のProposal Review Meetingにてコンセンサスに変化なしとして正式に accepted された。実装CL(CL 779200)が提示され、既存のパッケージロード処理を再利用できる合理的な実装であることが確認されたことも承認を後押しした。
技術的背景
現状の問題点
runtime/debug.BuildSetting の vcs.modified キーは、VCSの作業ツリーに変更ファイルが1つでも存在すると true になる。これはビルドと無関係なファイル(プロファイルファイル、一時ファイルなど)が存在するだけでバイナリに +dirty サフィックスが付いてしまうことを意味する。
特に問題となるのが以下のシナリオ:
# 完全にクリーンなリポジトリを想定
go build # ./foo バイナリ生成 → リポジトリが「変更あり」状態になる
go install # $GOBIN/foo に +dirty サフィックス付きでインストール、./foo 削除 → リポジトリがクリーンに戻る
go install # $GOBIN/foo に +dirty なしでインストール
go build でバイナリを生成すると、そのバイナリ自体が「未追跡ファイル」として認識され、次の go install が dirty ビルドとなる非冪等な動作が発生する。
提案された解決策
vcs.modified の判定ロジックを以下の精密な定義に変更する:
modified = 現在のビルドに使用されるファイルがVCSと異なる場合、あるいはクリーンなVCSチェックアウトでのビルドに使用されるファイルがVCSと異なる場合
具体的なアルゴリズム:
- VCS変更ファイルの一覧を取得
- 変更ファイルが現在のビルドに含まれる →
modified=true - 変更ファイルが現在のビルドのembedパターンにマッチする →
modified=true - 変更ファイルがビルド対象パッケージのディレクトリにあり、かつソースコードの拡張子を持ち、かつファイル名のルールで除外されない場合 → VCSから「クリーンバージョン」を取得してビルドタグを評価。クリーンビルドに含まれるなら
modified=true
ステップ4の「VCSからの取得」は最後の手段として必要最小限にのみ実行される。
これによって何ができるようになるか
ビルドやインストール操作が冪等になり、バイナリのバージョン情報が実際のビルド内容を正確に反映するようになる。
- プロファイルファイル(
.prof)や一時ファイルが残っていても+dirtyにならない go build後にgo installを実行してもバージョン文字列が変わらない- ファイルを削除しただけ(削除されたファイルがビルドに影響する場合)は正しく
modified=trueになる - VCS無視ファイルであってもリポジトリに登録されていれば正しく判定される
コード例
// Before: プロファイルファイルが存在するだけで dirty になる
// $ ls
// main.go go.mod cpu.prof
// $ go install example.com/mytool@v0.0.0
// → バイナリのバージョン: v0.0.0+dirty ← cpu.prof は無関係なのに dirty
// After: ビルドに無関係なファイルは無視される
// $ ls
// main.go go.mod cpu.prof
// $ go install example.com/mytool@v0.0.0
// → バイナリのバージョン: v0.0.0 ← cpu.prof は無視される
// ファイル削除のケース(false negative を防ぐ)
// $ git rm unused_but_important.go # 実はビルドに使われていたファイルを削除
// $ go build
// → vcs.modified=true ← クリーンビルドでは使われていたファイルが消えているため正しく dirty
議論のハイライト
- false negative 問題の発覚: 最初の提案「変更ファイルが現在のビルドに含まれる場合のみ dirty」では、ファイル削除や
//go:build ignoreタグ追加によってそのファイルがビルドから除外されるケースで false negative が生じることが判明。これはバイナリが「クリーンビルドと同一」と誤って報告することになり、許容できないとして却下された。 - 3つの実装案の検討と決定: (1) 現状維持、(2) 精密なアルゴリズム(false positive/negativeなし、VCS読み取りあり)、(3) 保守的判定(稀な false positive あり)の3案が検討され、最終的に (2) が選ばれた。VCS読み取りは最終手段として最小限に抑えられること、Go commandが既にVCS操作コードを持つことが決め手となった。
- 既存コードの再利用: 実装CLでは既存のパッケージロードロジックを再利用する設計となっており、将来的な変更(新しいファイル取り込み方式の追加など)にも追従しやすい構造になっている。embed対応が好例として言及された。
- VCSエラー時の挙動: VCSからファイルを読み取れない場合はエラーを出力して
-buildvcs=falseの使用を提案するメッセージを表示する既存の挙動に倣う。 - Subversion等のネットワーク依存VCSへの懸念:
go buildがネットワークを必要としないことへの懸念があったが、Subversionはオフラインでローカルのベースファイルを参照できるため問題なしと結論付けられた。