Gitの深淵:`assume-unchanged` と `skip-worktree` の境界線を掌握せよ
DevOpsの最前線にいる諸君なら、一度は直面したはずだ。「環境依存の設定ファイル」や「ローカルでしか動かないデバッグ用パッチ」を、うっかりコミットしかけて冷や汗を流した経験が。
多くのエンジニアがこの問題を解決するために `git update-index` を叩く。しかし、諸君は理解しているか? `–assume-unchanged` と `–skip-worktree` 、この二つの「似て非なる」コマンドが、なぜこれほどまでに混同され、そしてなぜ致命的なトラップを内包しているのかを。
今日は、Gitのインデックス(ステージングエリア)の深層を探り、現場で二度と「設定ファイル迷子」を起こさないための、プロフェッショナルな解法を提示する。
—
1. 内部アーキテクチャの視点:何が起きているのか
Gitのインデックスは、単なる「コミット待ちの場所」ではない。それはワークツリーとオブジェクトデータベースをつなぐ高速なキャッシュ層だ。
`–assume-unchanged` の真実
これはGitに対して「このファイルは(私が触らない限り)変更されないはずだから、いちいちディスクのstat情報を確認しに行くな」と命じるフラグだ。
- 本来の目的: パフォーマンス最適化。巨大なリポジトリにおいて、ファイル変更検知のオーバーヘッドを減らすために設計された。
- 仕様: Gitが「変更されたはずがない」と決めつけるため、たとえ諸君がローカルで書き換えても、Gitはそれを無視する。
`–skip-worktree` の真実
これは「このファイルはワークツリーに存在していても、Gitの管理から切り離して扱え」と命じるフラグだ。
- 本来の目的: スパースチェックアウトや、ローカル固有の設定ファイルを管理するために設計された。
- 仕様: Gitはファイルの状態を完全に無視する。たとえ `git pull` でリモート側がそのファイルを更新しても、このフラグが立っていれば、ローカルのファイルが強制的に上書きされることはない。
—
2. 決定的な「悲劇」:なぜ混同してはいけないのか
この二つを混同すると、プロジェクトのリリースパイプラインを破壊しかねない。
- `assume-unchanged` の落とし穴:
`git pull` でリモート側に変更があった場合、Gitは「変更はないはずだ」という思い込みを優先しようとして、コンフリクトを正しく検知できない、あるいは予期せぬ挙動を起こす。そもそも「ファイルが更新された」という事実を検知した瞬間にフラグが解除されるという仕様があり、Gitの気まぐれなタイミングで管理対象に戻ってしまう。
- 結論: 設定ファイルやローカル変更の退避に `–assume-unchanged` を使うのは、エンジニアリング上の怠慢であり、バグの温床だ。
—
3. プロの現場における「正しい」自動化ハック
ローカル設定ファイルを管理したいなら、`–skip-worktree` 一択だ。さらに、これを手動で管理するのはナンセンスだ。チーム開発において「個人の設定忘れ」を防ぐためのスクリプトを共有しよう。
推奨:ワークツリーの自動保護スクリプト
`.git/hooks/post-checkout` や `.git/hooks/post-merge` に仕込むことで、特定のファイルを強制的に `skip-worktree` 状態に保つことができる。
!/bin/bash
.git/hooks/post-merge & post-checkout
ローカル固有の設定ファイルを強制的にgitの追跡から除外(無視)する
CONFIG_FILES=(“config/database.yml” “config/secrets.local.yaml”)
for file in “${CONFIG_FILES[@]}”; do
if [ -f “$file” ]; then
# すでにskip-worktreeが設定されているか確認し、なければ付与
# -v オプションで現在の状態を表示(デバッグ用)
git update-index –skip-worktree “$file”
echo “[CI/CD Hook] Protected local config: $file”
fi
done
—
4. 極限の運用:`git-template` での完全自動化
各エンジニアの環境で個別に設定させるのは情弱のやり方だ。Gitテンプレートディレクトリを利用し、リポジトリクローン時にこの設定が自動で適用されるようにせよ。
1. テンプレートの準備: `~/.git-templates` を作成。
2. 設定: `git config –global init.templatedir ‘~/.git-templates’`
3. Hooks配備: 上記スクリプトを `~/.git-templates/hooks/` に配置する。
これで、チームの誰がリポジトリをクローンしても、環境依存の設定ファイルが「Gitの追跡から静かに消える」状態がデフォルトになる。
—
5. 伝説的アーキテクトからの助言
最後に、厳しい現実を突きつけておく。
`–skip-worktree` は強力だが、「管理対象から外れている」という事実を忘れることは、技術的負債の隠蔽に等しい。このフラグを使っているファイルは、CI/CDパイプライン上で検証できない「闇」になる。
もし設定ファイルを保護したいのであれば、本当の解は以下にある。
1. 環境変数の活用: ファイルに依存せず、プロセス実行時に環境変数で注入する。
2. テンプレート化: `config.yml.example` を管理し、ローカル起動スクリプトで自動的に `config.yml` を生成する。
`–skip-worktree` は、あくまで「どうしても避けられないレガシーな運用」に対する外科手術だ。これを多用する状況になったら、設計そのものを見直せ。それが、最高峰のDevOpsエンジニアが到達すべき視座である。
諸君、コマンドラインを崇拝するな。システムの本質を見極め、その上でコードを走らせろ。