# 如何在 Windows 使用 Git 版控時可以區分檔案名稱大小寫

今天被問到一個關於 Git 版控的問題，同事提到我們有個網站專案的 Repo 裡面有個 `*.svg` 檔案，之前 git commit 的時候使用 `helloWorld.svg` 這樣的檔名，但是部署的時候發生錯誤，客戶告知必須用**英文小寫**的檔名才能正常運作，所以他就來修改一下檔案名稱，結果卻發現 Git 怎樣都無法修改檔名大小寫部分，問我該怎麼辦。這篇文章我就來跟各位說明這個問題的來龍去脈與解決方法。

### 關於忽略檔名大小寫的預設值

你只要輸入以下命令，就可以查詢 Git 所有設定的說明：

```bash
git config --help
```

其中有個 `core.ignoreCase` 設定，可以用來告訴 Git 是否要區分檔名路徑的大小寫，其預設值為 `false`，但可能有些特殊情況會讓你的預設值並不是 `false`！

### 問題發生的原因

有許多 **檔案系統** (Filesystems) 本身就是不區分大小寫的 (case insensitive)，像是 APFS ([Apple File System](https://en.wikipedia.org/wiki/Apple_File_System)), HFS+ ([HFS Plus](https://en.wikipedia.org/wiki/HFS_Plus)), FAT ([File Allocation Table](https://en.wikipedia.org/wiki/File_Allocation_Table)), NTFS ([New Technology File System](https://en.wikipedia.org/wiki/NTFS)) 等等，都是不區分大小寫的檔案系統。其實並非 "Windows" 就是不區分大小寫，而是「檔案系統」本身決定了檔名是否要區分大小寫，其實 Windows 是可以區分檔名大小寫的。

事實上，當你透過 `git clone` 或 `git init` 的時候，Git 會自動判斷你現在所在的**檔案系統**是否支援**區分大小寫**，如果你使用 Windows 常見的 NTFS 或 FAT 檔案系統，那麼預設你的 `.git\config` 會有以下預設值：

```ini
[core]
        repositoryformatversion = 0
        filemode = false
        bare = false
        logallrefupdates = true
        ignorecase = true
```

這裡有個 `core.ignorecase` 核心設定預設被寫入了 `true` 的值，但你若在 Linux 或 macOS 底下執行 `git clone` 或 `git init` 的話，預設的 `.git/config` 並不會有 `ignorecase = true` 這條設定！

也因此，當你在 Windows 透過 Git 版控時，自然就會忽略所有檔名路徑的大小寫差異，這會讓你**永遠無法更新**版控中的**檔名路徑大小寫**。

### 解決方案 1

如果你去查了設定，很有可能你會這樣設定：

```bash
git config --global core.ignorecase false
```

因為一般來說，我們通常會把 Git 組態設定在 `--global` 全域等級，不過我們在 Windows 作業系統執行 `git config core.ignorecase` 的話，你會發現永遠得到 `true` 的結果，也就是怎麼設定都無效。

其實 Git 組態有三個等級，依照優先權排序分別是 `--local` > `--global` > `--system` 等等，而這裡的 `--local` 就是設定在 **本地儲存庫** (Local Repo) 的 `.git\config` 檔案中，其優先權是最高的。因此，解決這個問題的方法，則是執行以下命令才對：

```bash
git config --local core.ignorecase false
```

如此一來你在執行 `git status` 的時候，就會判斷檔名大小寫了！ 👍

> 請注意：千萬不要在有區分大小寫的檔案系統中，使用 `core.ignorecase=true` 設定，這可能會造成許多未知的錯誤！ 🔥

除此之外，還有一個冷門的 Git 命令可以用，那就是「一次性」的組態調整，你只要在 `git` 命令後面立刻接上一個 `-c core.ignorecase=false` 參數，該命令無論你要執行 `status` 或 `checkout` 都可以自動區分檔名大小寫，但這個設定對 `git clone` 與 `git init` 是無效的。如下範例：

```bash
git -c core.ignorecase=false status
```

### 解決方案 2

另一種解決方法，則是使用 `git mv` 命令，強迫 Git 變更「檔案名稱」，而且此命令會自動忽略 `core.ignorecase` 組態設定，直接使用你在命令列使用的大小寫來當作檔名。其命令執行的範例如下：

```bash
git mv README.md ReadMe.md
```

上述命令可以強迫將 `README.md` 修改為 `ReadMe.md` 檔名，而且儲存在 Git 儲存庫中的檔名也會因此改變！👍

### 解決方案 3

還有一種可能會發生的問題，那就是你的同事已經更新了檔名大小寫，但是你的 Windows 電腦上的檔名卻一直都沒有變更大小寫。

此時你可以透過以下步驟重置工作目錄：

1.  請確認工作目錄是乾淨的 (所有檔案都已經加入版控)
2.  刪除工作目錄下所有檔案 (切勿刪除 `.git` 資料夾)
3.  執行 `git reset --hard` 重置工作目錄

如此一來，所有檔名大小寫都會符合原始碼版控中的所有檔名，這個技巧在 Windows 只對 `NTFS` 檔案系統才有效喔！ 👍

### 解決方案 4

還有一種可能會發生的問題，那就是你的 Git Repo 中同時有兩個**相同檔名**但**不同大小寫**的檔案，這樣的 Repo 在 Windows + NTFS 是無法正確顯示檔案的，只有一個檔案會被取出到工作目錄，另一個則會自動標示為 `deleted` 狀態。假設我們有兩個檔案在版控中，分別是 `README.md` 與 `ReadMe.md`，假設你想刪除 `ReadMe.md` 檔案，其解決問題的步驟如下：

1.  你無法確認工作目錄是乾淨的，因為永遠會少一個檔案 (因為有兩個同名但檔名大小寫不同的檔案)
2.  請直接執行 `git rm ReadMe.md` 命令，此時你用 `git status` 會被告知刪除了兩個檔案，一個是 `staged` 狀態，另一個是 `not staged` 狀態
3.  接著直接執行 `git commit -m "移除重複檔案"`，並立刻執行 `git reset --hard` 重置工作目錄，問題就可以迎刃而解！

### 後記

其實跨平台開發已經成為常態，一個開發團隊中有成員使用 Windows、有成員使用 macOS 屢見不鮮，不單單後端開發經常都可以跨平台，前端的開發團隊更是如此。如果你有使用 CI/CD 對專案進行自動化建置與部署，也有非常高的機率會遇到各種不同作業系統平台的狀況。因此，我建議大家要開始重視「檔名路徑大小寫」的問題，如果可以，請盡量以「小寫」命名，否則很容易會遇到鬼打牆的狀況。

像是本篇文章提到的問題，在我們公司最近一年就遇到了兩次，我原本以為這個問題應該很簡單，但是第一次遇到此問題的人，還是可能會卡關卡很久，所以才出現撰寫這篇文章的契機，希望大家下次遇到這種狀況可以更快的解決問題！ 😊

### 相關連結

-   [使用者設定 - 為你自己學 Git | 高見龍](https://gitbook.tw/chapters/config/user-config)
-   [Git - git-config Documentation](https://git-scm.com/docs/git-config)
