1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

git rm --cachedで追跡解除したファイルがブランチ切替で消えることがある

1
Last updated at Posted at 2026-08-31

はじめに

.gitignore に入れて git rm --cached すれば、そのファイルは安全圏に入ります。そう思っていたところ、ローカルにしかない設定ファイルが気づかないうちに消えていました。

原因は、追跡解除がコミット単位の操作でしかなく、その変更を含まないブランチでは、ファイルが依然として「追跡対象」のままだったことです。
今回は .claude/settings.local.json(Claude Codeのローカル許可設定)で発生させましたが、他にも .env やIDEの個人設定ファイルなど、**ローカル固有ファイルを後から .gitignore に入れようとした際に起こりうるミスです。

何が起きたか

経緯は以下の通りです。

  1. feature ブランチ上で git rm --cached してファイルを追跡解除し、.gitignore に追記してコミット
  2. main にはその変更をまだマージしていない(=main では依然としてファイルが追跡されたまま)
  3. 作業のために main へ checkout
  4. しばらくして元のブランチに戻る

この間、次の2つが起きます。

1. 追跡ありのブランチに切り替えた瞬間 → 警告なく上書きされる

main はまだそのファイルを追跡しているため、checkout した瞬間にディスク上のファイルが、mainブランチが持っている古い内容で上書きされます。ローカルでしか更新していなかった内容はここで消えます。

2. 追跡なしのブランチに戻った瞬間 → worktreeから削除される

元のブランチ(追跡解除済み)に戻ると、gitは「このブランチにこのファイルは存在しないはず」と判断し、ディスクからファイルを削除します。

上書き→削除の順で、ローカル設定が失われます。

.gitignore されたファイルはgitの保護対象外

通常、追跡中のファイルをローカルで変更した状態で checkout しようとすると、gitは

error: Your local changes to the following files would be overwritten by checkout

と表示して処理を止めます。一方、.gitignore に入っているファイルはこの保護の対象外です。gitからすれば「無視してよいと明示されたファイル」であるため、上書きも削除も警告なしに実行されます。

つまり .gitignore に入れるという操作は、「gitに管理させない」だけでなく「gitの保護からも外す」ことを意味します。

根本原因:追跡解除は「全ブランチに行き渡って初めて有効」

git rm --cached はワーキングツリーではなくコミットに対する操作であり、そのコミットを含まないブランチには影響しません。長命ブランチ(main / develop など)がある構成では、フィーチャーブランチで追跡解除しただけでは事故は止まりません。mainにマージされてブランチ全体に行き渡るまでの間、mainを触るたびに同じ事故が起こりえます。

さらに、.gitignore の行を追加するだけでは解決しません。上書き・削除の原因は「そのブランチのindexにファイルが存在していること」であるため、.gitignore への追記と、indexからの削除(git rm --cached)はセットでなければ意味がありません

対処法

応急処置:全ブランチで追跡解除を揃える

# 作業中の変更が無いか確認
git status

# main から hotfix ブランチを切る
git fetch origin
git switch -c hotfix/untrack-local-file origin/main

# 追跡だけ解除(--cached なのでディスク上のファイルは残る)
git rm --cached path/to/local-file

# .gitignore に追記
printf '
path/to/local-file
' >> .gitignore

git add .gitignore
git commit -m "chore: ローカル固有ファイルを追跡対象から除外する"
git push -u origin hotfix/untrack-local-file
gh pr create --base main

マージ後は main を develop や他の作業ブランチへも戻し入れる必要があります。これを怠ると、ブランチ間で .gitignore の内容が食い違ったままになり、次のマージで同じ行がコンフリクトします。

根本対策:最初から .gitignore に入れておく

最も確実な方法は、ローカル固有ファイルをリポジトリの最初のコミットの時点で .gitignore に入れておくことです。

後から外す場合、全ブランチに変更した .gitignore が行き渡るまでの間、事故が起こりうる状態が続きます。

消えたファイルは追えるのか

追跡対象外のファイルは履歴から追えないと思いがちですが、過去に一度でも追跡されていた期間があれば、消失時刻まで特定できる場合があります。

# 1. 追跡されていた期間があるかを確認する(--all が重要)
git log --oneline --all -- 'path/to/local-file'

# 2. どのコミットがファイルを持っているか(追跡あり/なしの食い違いを見る)
git cat-file -e <commit>:path/to/local-file && echo "追跡あり"

# 3. reflogでブランチ切替の時刻を突き合わせる
git reflog --date=iso -40

# 4. ディレクトリのmtimeで裏を取る(配下ファイルの増減時刻と一致するか)
ls -la path/to/

checkout: moving from A to Bpull origin X: Fast-forward の時刻が、そのままファイルの上書き・削除の時刻になります。

ただし、復元できるのは当然「最後に追跡されていた時点の内容」のみです。追跡解除後にローカルだけで積み上げた変更は残っていません。

まとめ

gitの仕様はきちんと理解しないと怖いですね。

コンフリクトばっかり気にしていましたが、こういう些細な部分にも気を配りたいものです。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?