ファイル名の表記を整理するため、大文字を含む名前をすべて小文字のkebab-caseへ変更しました。
例えば、次のようなファイル名です。
sample-Worker.ps1
demo-Parser.ps1
example-Runner.ps1これらを、
sample-worker.ps1
demo-parser.ps1
example-runner.ps1のように小文字に変更しました。
ところが、その後Gitの状態を確認すると不可解な現象が発生しました。VS Codeでは対象ファイルを正常にModifiedとして認識しています。
一方、Git CLIから新しいファイル名を指定すると、追跡されていないような挙動になります。
調査したところ、原因はWindows環境で大文字・小文字だけを変更したリネームがGitインデックスへ正しく反映されていなかったことでした。
発生した問題
最初に気付いたのは、例としてsample-worker.ps1のようなファイルでした。VS Codeのエクスプローラーでは、ファイルがModifiedとして表示されています。
ところが、次のコマンドを実行しても何も表示されません。
git ls-files -v -- sample-worker.ps1履歴を確認しても同様でした。
git log --all --oneline -- sample-worker.ps1同じGitリポジトリにある他のファイルは正常に認識されています。さらに、同じように大文字を小文字へ変更した別のファイルでも同じ現象が発生しました。
そのため最初は、
.gitignore
skip-worktree
assume-unchanged
VS CodeとGit CLIが別のリポジトリを見ているといった原因も疑いました。
しかし、どれも今回の状況とは一致しませんでした。
原因を調査する
調査1. git statusではModifiedと認識されていた
ファイル名を指定せずに、
git status --shortを実行すると、Git自身は変更を認識していました。例えば次のような状態です。
M sample-worker.ps1
M logs/example.jsonl
M templates/example-config.mdここでさらに調査すると、原因が見えてきました。
Gitインデックスに登録されているファイル名を、大文字・小文字を無視して検索します。
git ls-files |
Select-String -Pattern "sample-worker|demo-parser" -CaseSensitive:$falseすると、次のように変更前の名前が表示されました。
sample-Worker.ps1
demo-Parser.ps1実際のファイル名は、
sample-worker.ps1
demo-parser.ps1です。
つまり、状態は次のようになっていました。
実際のファイル名
sample-worker.ps1
Gitインデックス上の名前
sample-Worker.ps1Gitはファイル自体の変更は認識していますが、インデックスには変更前の大文字・小文字が残っていました。
調査2. core.ignoreCaseはtrueだった
Gitの設定も確認しました。
git config --get core.ignorecase結果は、
trueでした。
Windowsでは通常、ファイル名の大文字・小文字を区別しないファイルシステムとして扱われます。
そのため、
sample-Worker.ps1
sample-worker.ps1をWindows側では同じファイルとして扱えます。Gitもcore.ignoreCase=trueの環境では、この特性を考慮してファイルを扱います。
今回、
sample-Worker.ps1
↓
sample-worker.ps1のように大文字・小文字だけを変更したため、Windows上ではファイル名が変わっていても、Git側では同じファイルとして処理され、インデックスに古い表記が残っていました。
調査3. VS CodeがModifiedと表示していたのは正常だった
途中では、
VS CodeではModified
Git CLIでは新しい名前が見つからないという状態だったため、VS Code側のGit連携も疑いました。しかし、VS Codeの表示は正常でした。
Git自身も、
git status --shortでは、
M sample-worker.ps1と変更を検出しています。問題だったのはファイルの変更検出ではなく、Gitインデックスに保存されているパスの大文字・小文字でした。
そのため、
git log --all --oneline -- sample-worker.ps1のように新しい名前を指定しても、期待した履歴を取得できませんでした。
Git側には、
sample-Worker.ps1という旧表記が残っていたためです。
調査4. kebab-case自体が原因ではなかった
今回、問題が発生した直後は、
大文字を混ぜたファイル名を使うと危険なのではないかと考えました。
そこで、ファイル名をすべて小文字のkebab-caseへ変更しました。
sample-worker.ps1
demo-parser.ps1
example-runner.ps1しかし、VS Codeを再起動したあと、変更した複数のファイルで同じ問題が発生しました。
ここで、kebab-caseへの変更そのものではなく、多数のファイルについてケースだけを変更したことが共通点だと分かりました。
例えば、
example-Runner.ps1
↓
example-runner.ps1は、文字列として変化しているのはRからrだけです。
一方、
example_runner.ps1
↓
example-runner.ps1のようにアンダースコアをハイフンへ変更する場合は、今回とは性質が異なります。
問題になったのは、
大文字・小文字だけ変更
+
Windows
+
core.ignoreCase=trueという組み合わせでした。
問題対策
1. 一時ファイル名を経由して1ファイルずつ修正する
今回の問題は、実際のファイル名だけが小文字になり、Gitインデックスには変更前の大文字を含む名前が残っていることが原因でした。
そのため、Gitが管理している変更前のファイル名から、一度まったく別の名前へ変更し、その後目的の小文字のファイル名へ変更しました。
例えば、Gitインデックスに、
example-Runner.ps1という名前が残っていて、実際のファイル名を、
example-runner.ps1へ変更したい場合は、次のようにします。
git mv example-Runner.ps1 temp.ps1
git mv temp.ps1 example-runner.ps1つまり、
example-Runner.ps1
↓
temp.ps1
↓
example-runner.ps1という順番です。
一度temp.ps1のような大文字・小文字以外も異なる名前を経由することで、Gitにファイル名の変更を明確に認識させられます。
今回は同様の状態になっていたファイルについて、この方法を使って1ファイルずつ修正しました。
なお、変更後の名前から、
git mv example-runner.ps1 temp.ps1と実行すると、Gitインデックスにはまだexample-Runner.ps1という名前が登録されているため、次のようなエラーになる場合があります。
fatal: not under version control, source=example-runner.ps1, destination=temp.ps1その場合は、Gitが管理している正確な名前を確認します。
git ls-files |
Select-String -Pattern "example-runner" -CaseSensitive:$false例えば、
example-Runner.ps1と表示されたら、その名前を最初のgit mvに指定します。
2. core.ignoreCaseをfalseにする方法は採用しない
対策として、
git config core.ignoreCase falseに変更する方法も考えられます。しかし、今回は採用しませんでした。
core.ignoreCaseは、Gitが使用しているファイルシステムの大文字・小文字の扱いに対応するための設定です。
Windows環境で単純にfalseへ変更すると、別のファイル名判定で問題を引き起こす可能性があります。
今回必要なのはGit全体のケース判定を変更することではなく、
Gitインデックスに残った旧ファイル名
↓
現在使用する小文字のファイル名へ正しく更新することです。
そのため今回はcore.ignoreCase=trueのまま、対象ファイルを、
変更前の名前
↓
一時的な名前
↓
変更後の名前の順番で1ファイルずつgit mvし、Gitインデックスのファイル名を修正しました。
まとめ
Windows環境で既存ファイルの名前を、
sample-Worker.ps1
↓
sample-worker.ps1のように大文字から小文字へ変更したところ、VS CodeではModifiedと表示される一方、
git ls-files
git log
git mvで新しいファイル名を正しく扱えない問題が発生しました。
原因を調べると、
git config --get core.ignorecaseは、
trueとなっており、Gitインデックスには変更前の、
sample-Worker.ps1
demo-Parser.ps1という名前が残っていました。今回の原因は、kebab-caseやVS Codeの不具合ではなく、
Windows上で
大文字・小文字だけのリネームを行った
↓
Gitは同じファイルとして認識
↓
インデックスには古いケースが残ったというものでした。
今後はファイル名を小文字のkebab-caseへ統一しつつ、既存ファイルについてケースだけを変更するときは、Gitが認識している旧ファイル名からgit mvで明示的に変更するようにします。
今回のようにVS CodeとGit CLIの表示が食い違って見える場合は、
git status --short
git ls-files
git config --get core.ignorecaseを確認すると、Gitが実際にどのファイル名をインデックスへ記録しているか切り分けやすくなります。
