Gitのassume-unchangedとskip-worktreeの違い:ローカル設定ファイルの変更を正しく無視する方法

Git tutorial - IT technology blog
Git tutorial - IT technology blog

クイックスタート:3分でわかる即効手順

深夜2時、新しいビルドをデプロイした直後にステージングサーバーのデータベース接続が落ちてしまいました。復旧のために config/database.json の接続文字列(connection string)を緊急修正する必要があります。しかし、この設定値を誤ってリポジトリにコミットしたくありませんし、該当ファイルはすでにGitの追跡対象(tracked)になっているため、.gitignore に追加しても無視されません。

最も安全かつ確実な解決策は、skip-worktree フラグを使用することです:

# 1. database.json のローカルな変更を無視するようGitに指示
git update-index --skip-worktree config/database.json

# 2. 確認:変更一覧(modified)からファイルが非表示になる
git status

# 3. 再び追跡対象に戻す場合(最新コードのpullや変更のコミット時)
git update-index --no-skip-worktree config/database.json

ローカルでのコマンド実行速度を向上させたいだけで、リモート側でもそのファイルが決して変更されないと確信できる場合は、assume-unchanged が代替の選択肢となります:

# ファイルの変更チェックをスキップ
git update-index --assume-unchanged config/database.json

# 通常の追跡状態に戻す
git update-index --no-assume-unchanged config/database.json

技術的な本質:見かけの動作に騙されてはいけない

コマンドを実行すると、どちらのフラグでも変更されたファイルが git status に表示されなくなります。しかし、根本的な違いは「リモートとの競合(コンフリクト)が発生した際のGitの挙動」にあります。

1. assume-unchanged:巨大リポジトリ向けのI/O最適化

この機能は、数十万ファイル規模の巨大なモノリシックリポジトリ(ChromiumやLinuxカーネルなど)向けに開発されました。全ファイルに対して stat() システムコールを頻繁に実行すると、深刻なディスクI/Oボトルネックが発生します。このフラグを設定することは、「このファイルは絶対に変更しないので、Gitはディスクをスキャンする手間を省いてよい」 と約束することを意味します。

しかし、誤ってファイルをローカルで編集してしまった場合、以下のようなリスクが生じます:

  • Gitが変更チェックをスキップするため、コミット時に警告すら表示されません。
  • git pull を実行した際、リモート側でそのファイルが更新されていると、Gitはフラグを自動的に解除するかそのまま上書きしてしまい、苦労して調整したローカル設定が消滅してしまいます。

2. skip-worktree:ローカル設定を守るシールド

--skip-worktree フラグは、ローカル固有の設定を保持したいというニーズに完璧に応えます。このコマンドはGitに対して、「ローカルでこのファイルを変更しているが、リポジトリのバージョンを維持し、ローカルの変更を決してプッシュしない」 という明確な指示を伝えます。

  • git pull 時にリモート側と競合が発生した場合、Gitは勝手に上書きすることなく、能動的に処理を停止して明確なエラーを通知します。
  • 通常の git merge や git rebase の操作によってこのフラグが勝手に解除されることはありません。

クイック比較

項目 assume-unchanged skip-worktree
主な目的 巨大リポジトリのディスクI/O負荷軽減 ローカルでの変更内容の保護
git pull競合時の挙動 フラグが解除されやすく、上書きされる危険性あり pullをブロックしてエラーを通知し、ファイルを保護
適した対象ファイル 静的SDK、肥大化したvendorライブラリ 設定ファイル、認証情報、環境設定ファイル

無視されているファイル一覧の管理

フラグを設定してから数週間経つと、どのファイルを無視したかを忘れがちです。プロジェクトの実際のインデックス状態を確認するには、git ls-files -v コマンドを使用します。

フラグ別にファイルを抽出

# skip-worktree が設定されているファイルを一覧表示(大文字のS)
git ls-files -v | grep '^S'

# assume-unchanged が設定されているファイルを一覧表示(小文字のh)
git ls-files -v | grep '^h'

先頭文字の意味:

  • S:skip-worktree が有効なファイル
  • h:assume-unchanged が有効なファイル
  • H:正常に完全追跡されているファイル

入力時間を節約する便利なエイリアス集

毎回長いコマンドを入力するのは非効率です。~/.gitconfig ファイルに以下の3つのエイリアスを追加しておくことをおすすめします:

git config --global alias.ignore-local "update-index --skip-worktree"
git config --global alias.unignore-local "update-index --no-skip-worktree"
git config --global alias.ignored-locals "!git ls-files -v | grep '^S'"

これで、必要に応じて短いコマンドで操作できるようになります:

git ignore-local config/database.json
git ignored-locals

実務で役立つ3つの重要ポイント

  1. 設定ファイルには必ず skip-worktree を使用する: 認証情報や接続文字列に assume-unchanged を使用してはいけません。不注意な git checkout ひとつで、時間をかけて設定した内容がすべて吹き飛んでしまう恐れがあります。
  2. コード更新前にフラグを解除する: skip-worktree を設定したファイルが原因で git pull がブロックされた場合は、git unignore-local <file> を実行して変更をスタッシュ(stash)し、pullを完了させてからスタッシュを適用し直して再度フラグを設定します。
  3. 環境変数の分離が本来のベストプラクティス: Gitのフラグはデバッグや緊急対応のための一時的な対症療法に過ぎません。長期的には、追跡対象の .env.example と、.gitignore に含めるローカルの .env を分離する設計を徹底するべきです。
Share: