Git における `assume-unchanged` と `skip-worktree` の技術的比較

はじめに

Git を利用する開発現場では、特定のファイルを「一時的に無視」したいというニーズが頻繁に発生する。例えば、共有リポジトリ内の構成ファイルや、ビルドによって一時的に変更されるファイルが該当する。

Git にはこのようなケースに対応する2つの機能が存在する:
- assume-unchanged
- skip-worktree

本稿では、これら2つの機能の違い、使いどころ、内部挙動を技術的観点から明確に比較する。


1. 基本概念

1.1 assume-unchanged

git update-index --assume-unchanged <path>

このコマンドは、Git に対して「このファイルは今後変更されないものとして扱ってよい」というヒントを与える。
Git のステータストラッキングにおいて、変更検知の対象から外されるため、git status でも変更が表示されなくなる。

ただし、実際には 変更されていても Git はそれに気づかない という点で注意が必要である。

1.2 skip-worktree

git update-index --skip-worktree <path>

こちらは「このファイルの内容はワークツリー上で編集されても、Git は一切関知しない」ことを意味する。
Git の挙動としては、ローカルの変更は完全に無視されるうえに、pull などによってリモートの変更が存在しても、上書きされることがない。

この動作は、開発者ごとの設定差異(例:.env, config.json)など、ローカルで保持すべきカスタムファイルを扱う場面に適している。


2. 比較表

特徴 assume-unchanged skip-worktree
ステージング対象になるか いいえ いいえ
git status で表示されるか 表示されない 表示されない
git diff に出るか 出ない 出ない
リモート変更で pull 時に上書きされるか 上書きされる可能性あり 上書きされない(安全)
用途 ビルド成果物、一時ファイルなど 設定ファイルのローカル上書きなど
フラグの優先度 高(競合時はこちらが効く)

3. 内部挙動の違い

両者は Git の index に対して異なるフラグを設定している。

  • assume-unchanged は「パフォーマンス最適化」の一環で設計されており、あくまで Git のチェックを省略することが目的。
  • skip-worktree は「意図的に作業ディレクトリで変更されるファイル」に対する処理として設計されており、追跡から除外することが目的。
git ls-files -v

上記コマンドで現在のフラグ状態を確認可能である。

プレフィックス 意味
H skip-worktree
h assume-unchanged

4. 両者を同時に使った場合

技術的には、assume-unchangedskip-worktree の両方のフラグを同じファイルに対して設定することは可能である。
ただし、skip-worktree のほうが優先されるため、assume-unchanged を併用する意味は事実上ない。


5. 実用的な適用例

ケース1:自動生成ファイル(例:.min.js, *.cache

assume-unchanged が適している。
ローカルでのビルドで変更されるが、Git 上のトラッキングは不要な場合。

ケース2:開発者ごとのローカル設定(例:config.local.json

skip-worktree を使用するべき。
Git 管理は維持しつつ、ローカルごとの差異を無視したい場合。


6. 注意点と運用上のリスク

  • どちらのフラグも Git の状態を「隠蔽」するものであり、使い方を誤ると意図せぬデータ損失が発生する可能性がある
  • 特にチーム開発では、ドキュメントで運用ルールを明示すべき
  • CI/CD パイプラインや GitHub Actions がこれらのファイルに関与する場合、.gitignore との整合性にも注意する必要がある

おわりに

assume-unchangedskip-worktree は見た目の挙動が似ているため混同されがちだが、目的と設計思想が異なる。

正しく理解し、目的に応じて使い分けることで、Git による開発フローをより安全かつ柔軟に構築できる。