前回のDay 33では、改行コードの全行差分事故を恒久的に防ぐ
.gitattributes の完全定義と、リポジトリの一括正規化手順を学びました。続く本日のDay 34は、第7部「Windows & C# (.NET) 固有の落とし穴完全制覇」の第2弾――
すべてのWindows / C#開発者が一度は必ず絶叫する【Windowsの大文字・小文字ファイル名変更トラブル(git mv)】を徹底攻略します!
「Windowsのローカル環境やVisual Studioでは何の問題もなくビルドが通り、意気揚々とプルリクエストを作成したのに、GitHub Actions(Linux CI)が走った瞬間に謎のビルドエラーで落ちた…!」
「エクスプローラーやIDEで
UserService.cs を userservice.cs にリネームしたはずなのに、Gitが全く差分を認識してくれない…!」「ネットで見かけた
git config core.ignorecase false を実行したら、同一フォルダに大文字と小文字のファイルが2重出現してリポジトリが壊れた…!」このような怪奇現象は、Windows(大文字小文字を区別しない)とLinux / GitHub(厳格に区別する)というOSの基本思想の違いから発生します。
本講義では、Gitの内部フラグ
core.ignorecase のメカニズムを解明し、事故を100%防ぐ git mv による確実な改名作法を完全マスターしていきましょう!
① 導入・なぜWindowsとLinux/GitHubで事故が起きるのか?
Windows上でC#開発を行っていると、ファイル名の大文字・小文字を意識する機会はほとんどありません。
Windowsの標準ファイルシステムである NTFS(または FAT32) は、「大文字・小文字を区別しない(Case-Insensitive)」 仕様になっているためです。
たとえば、UserService.cs というファイルが存在するフォルダで userservice.cs や USERSERVICE.CS を開こうとしても、Windowsはすべて同じファイルとして正常にアクセスできます。
しかし、Webサーバーやクラウド、そしてGitHub Actionsをはじめとする現代のCI/CD環境の大多数は Linux(ext4等) で動作しています。
Linuxは 「大文字・小文字を厳格に区別する(Case-Sensitive)」 ため、UserService.cs と userservice.cs は完全に「別の2つのファイル」として扱われます。
- C#コード側:
using MyApp.Services;やDI設定でuserservice.csを参照するようになった。 - Windowsローカル: ファイルシステムが大文字小文字を無視するため、古い
UserService.csが残っていてもビルドが奇跡的に通ってしまう。 - GitHub Actions (Linux): リポジトリ上に
UserService.csしか存在しない場合、Linuxコンパイラ(dotnet build)は「userservice.csが存在しません!(CS2001エラー)」と判定して即座にビルドが爆発します。
② Gitのデフォルト設定「core.ignorecase」の罠
「じゃあ、エクスプローラーやVisual Studioでファイル名を小文字に変更してコミットすればいいのでは?」と誰もが思います。
しかし、実際にエクスプローラーで UserService.cs を userservice.cs に変更して git status を打ってみてください。
$ git status
On branch main
nothing to commit, working tree clean ← な、何も変わっていない扱い!?
驚くべきことに、Gitは「変更なし(working tree clean)」と表示し、ファイル名の変更を完全に黙殺します!
この原因こそが、Windows環境でGitを初期化した際に自動的にオンになる設定 core.ignorecase = true です。
🔍 core.ignorecase が true になっている理由
GitはWindowsが「大文字小文字を区別しないOS」であることを知っているため、過剰な差分検知を防ぐ目的でデフォルトで core.ignorecase = true を設定します。
その結果、「大文字小文字しか違わないファイル名変更」は 同一ファイルのままと判断され、Gitのインデックス(ステージング領域)に登録されない のです。
開発者はリネームできたと思い込んでプッシュしますが、リモートリポジトリ(GitHub)上には古い大文字のまま残り続け、ローカルとリモートの致命的な乖離が発生します。
インターネットの記事の中には「git config core.ignorecase false にすれば大文字小文字を検知してくれる」と書かれているものがありますが、Windows環境でこれを実行するのは極めて危険です!
OS側のファイルシステム(NTFS)は大文字小文字を同一とみなすのに対し、Git側だけが別物として扱うため、同一フォルダ内に大文字版と小文字版の2重ファイルが存在すると誤認され、ブランチの切り替えやマージの際にファイルが消失したり上書きされたりする壊滅的な破損を引き起こします。
③ 安全・確実な解決策!「git mv」を使った改名作法
では、Windows上で大文字・小文字のファイル名変更をGitに正しく認識させるにはどうすればよいのでしょうか?
その絶対的な正解が、Git専用の移動・改名コマンド git mv を使用することです!
方法A: git mv による直接改名(最新Git推奨)
近年の Git for Windows(v2.x以降)では、git mv を使えば大文字小文字のみの変更であっても直接リネームを認識できるよう改良されています。
$ git mv UserService.cs userservice.cs
# ステータスを確認(renamed として即座にステージングされる!)
$ git status
Changes to be committed:
renamed: UserService.cs -> userservice.cs
# コミットして完了
$ git commit -m "refactor: UserService.cs を userservice.cs に改名"
$ git push origin main
方法B: 環境に左右されない「確実な2段階改名」(プロの鉄板技)
古いバージョンのGit環境や、一部のGit GUIツール、または複雑なフォルダ階層において、git mv UserService.cs userservice.cs を実行すると fatal: destination exists というエラーで弾かれる場合があります。
そのような時でも100%確実に成功するのが、一時的な別名を経由する 「2段階改名」 です。
$ git mv UserService.cs UserService_temp.cs
# ステップ2: 一時名から目的の小文字名へ改名
$ git mv UserService_temp.cs userservice.cs
# ステータス確認(Git内部で自動的に UserService.cs -> userservice.cs の1つの改名に統合される!)
$ git status
Changes to be committed:
renamed: UserService.cs -> userservice.cs
$ git commit -m "refactor: UserService.cs を userservice.cs に安全改名"
④ SVN比較 & C# (.NET) 現場での実践テクニック
Subversion時代、Windows上でファイル名の大文字小文字を変更しようとすると、「すでに同じ名前のファイルが存在します」という競合エラーが発生し、コミットも更新もできなくなって作業コピーを泣く泣く再チェックアウトした経験を持つベテラン開発者は多いはずです。
Gitにおいてもファイルシステムの壁は存在しますが、Gitは 「インデックス(ステージング領域)という論理的な管理層」 を持っているため、git mv を通じてインデックスを明示的に更新でき、SVNのようにリポジトリ全体がロックされて破壊されるリスクを綺麗に回避できます。
C# / Visual Studio 現場で守るべき3大鉄則
-
Visual Studioのリファクタリング機能(F2)後は必ず git status を確認する
クラス名をUserServiceからuserserviceに変更する際、Visual Studioは親切に「ファイル名も変更しますか?」と聞いてくれます。
しかし、Visual Studio内部のGit連携バージョンによってはエクスプローラー同様にファイル変更がGitに伝わらないことがあります。リファクタリング後は必ずターミナルやGit変更ウィンドウでrenamedがステージングされているか確認しましょう。 -
GitHub Actions等のCIで「Linuxランナー(ubuntu-latest)」を常時稼働させる
Windowsデスクトップアプリ(WPF/WinForms)でない限り、Web API(ASP.NET Core)やクラスライブラリのビルドテストはCI上でruns-on: ubuntu-latestを設定しましょう。
Linux上でdotnet buildを走らせることで、大文字小文字のミスやパスの区切り文字違いをプルリクエストの段階で100%自動検知できます。 -
TortoiseGitを使う場合は「名前を変更」メニューを利用する
エクスプローラー上で右クリックし、「TortoiseGit」→「名前を変更...(Rename)」を選択して変更すれば、TortoiseGitが内部で安全にgit mv相当の処理を実行してくれます。
📌 本日のまとめと最重要ポイント
- Windows(NTFS)は大文字小文字を無視し、Linux/GitHubは厳格に区別する! このギャップが「ローカルで動くのにCIで爆発する」元凶。
- WindowsのGitはデフォルトで
core.ignorecase = true! エクスプローラーでの改名はGitに無視される。 - 絶対に
core.ignorecase = falseにしてはいけない! 二重ファイル認識でリポジトリが破壊される。 - 改名は必ず
git mvまたは「一時名を経由する2段階改名」で行う! - GitHub ActionsでLinuxビルド(ubuntu-latest)を回し、事故を未然に完全ブロックせよ!
【Day 35】巨大バイナリ・DLL・画像管理の決定版(Git LFS)
「リポジトリの容量が数GBに達してクローンが激重に…!」「インストーラーや重いアセットはどう管理する?」
SVNが得意だったバイナリ管理をGitで美しく実現する神ツール Git LFS(Large File Storage) の導入と運用を徹底解説します! 次回もお楽しみに!
コメント
コメントを投稿