﻿# Visual Studio Tools for Git 處理斷行字元 (CRLF) 的注意事項

今天遇到一個奇怪的問題，我在開發一個使用 Git 版本控管的 ASP.NET MVC 專案時，發現無法在 Visual Studio 2013 中使用「同步處理」功能，該專案明明就沒有異動過，但就跟我說有檔案變更，我怎樣都無法同步，完全鬼打牆。另一方面，在 [Visual Studio Tools for Git](http://visualstudiogallery.msdn.microsoft.com/abafc7d6-dcaa-40f4-8a5e-d6724bdb980c) 裡面的「變更」視窗所看到的檔案變更，經常與使用 Git for Windows 或 TortoiseGit 視窗中看到的檔案不一樣，這也是很煩人的事，因為這樣就很難在同一個專案下交替使用不同的 Git 版控工具，本篇文章將來介紹各種不同使用 [Visual Studio Tools for Git](http://visualstudiogallery.msdn.microsoft.com/abafc7d6-dcaa-40f4-8a5e-d6724bdb980c) 的注意事項。

我們先來看看執行「**同步命令**」時，到底發生什麼問題。我們開啟「輸出」視窗，看到的是【無法完成作業，因為下列檔案有變更】：

![image](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/989eddd57bc78af5d672-image_2415.png "image")

不過，從 [Visual Studio Tools for Git](http://visualstudiogallery.msdn.microsoft.com/abafc7d6-dcaa-40f4-8a5e-d6724bdb980c) 裡面的「變更」視窗所看到的，是完全沒有變更的提示：

![image](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/99b113bd969749f209ec-image_2416.png "image")

簡言之，都是「**斷行字元**」(**換行字元**) 搞的鬼！

各位在看我之前的【[Git 在 Windows 平台處理斷行字元 (CRLF) 的注意事項](/post/2013/09/15/Git-for-Windows-Line-Ending-Conversion-Notes.aspx)】文章時，會看到一句話：

> 當執行 git add 命令時，文字檔案中出現的 CRLF 斷行字元會自動被轉換成 LF 字元。而利用 git checkout 取出檔案到**工作目錄**時，則會自動將 LF 字元，轉換成 CRLF 字元。

另外還有一句話：

> 因為 .gitAttributes 檔案定義了要對所有文字檔案進行正規化處理，所以儲存在版本庫中檔案內容必須以 LF 結尾，在工作目錄中的文字檔案必須以 CRLF 結尾，這就是「正規化」要做的事。而我們雖然工作目錄中的檔案是 CRLF 結尾，但版本庫中的檔案卻也是以 CRLF 結尾，那就不對了！

如果你今天透過 Visual Studio 2012/2013 將專案加入到 Git 版本控制之中，那麼可能遇到問題的機會不大，但還是有可能會遇到問題，本文稍後會提到。不過我想大部分的情況下，你會從其他地方取得使用 Git 版控的專案，例如 Visual Studio Online 或 GitHub 等等。因此，對所有「文字檔案」進行「正規化」處理的動作，不見得是做的夠到位，導致經常會有這種鬼打牆的情況發生！為了要能徹底解決這個問題，我整理了以下步驟，各位照著步驟檢查 (依照順序)，應該能解決 99% 以上的詭異的 Git 版控問題：

**1\. 確保在專案中加入 .gitattributes 設定檔案**

以下我列出 Visual Studio 預設的 **.gitattributes** 設定檔版本，直接拿去用應該是沒問題的，請把該檔案直接置於 Git 工作目錄的最上層目錄下。

```
################################################################################ Set default behavior to automatically normalize line endings.###############################################################################* text=auto################################################################################ Set default behavior for command prompt diff.## This is need for earlier builds of msysgit that does not have it on by# default for csharp files.# Note: This is only used by command line###############################################################################*.cs     diff=csharp################################################################################ Set the merge driver for project and solution files## Merging from the command prompt will add diff markers to the files if there# are conflicts (Merging from VS is not affected by the settings below, in VS# the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat# these files as binary and thus will always conflict and require user# intervention with every merge. To do so, just uncomment the entries below###############################################################################*.sln       merge=binary*.csproj    merge=binary*.vbproj    merge=binary*.vcxproj   merge=binary*.vcproj    merge=binary*.dbproj    merge=binary*.fsproj    merge=binary*.lsproj    merge=binary*.wixproj   merge=binary*.modelproj merge=binary*.sqlproj   merge=binary*.wwaproj   merge=binary################################################################################ behavior for image files## image files are treated as binary by default.###############################################################################*.jpg   binary*.png   binary*.gif   binary################################################################################ diff behavior for common document formats# # Convert binary document formats to text before diffing them. This feature# is only available from the command line. Turn it on by uncommenting the # entries below.###############################################################################*.doc   diff=astextplain*.DOC   diff=astextplain*.docx  diff=astextplain*.DOCX  diff=astextplain*.dot   diff=astextplain*.DOT   diff=astextplain*.pdf   diff=astextplain*.PDF   diff=astextplain*.rtf   diff=astextplain*.RTF   diff=astextplain
```

**2\. 特別在 .gitattributes 設定檔案中加入特定幾個副檔名，並明確表示為「純文字」檔案**

我們就真的遇過這樣的問題，在 ASP.NET MVC 專案中有個名為 EmailTemplates.cshtml 檔案，某同事在某次提交版本的過程中，該 CSHTML 檔案竟然被視為「**二進位**」檔案，以至於被提交到 Git 儲存庫中的斷行字元變成了 CRLF，照理說應該要被自動轉換為 LF 斷行字元才對！然而，我在取出該變更後，卻把該檔案視為「純文字」檔案，所以發生了【[Git 在 Windows 平台處理斷行字元 (CRLF) 的注意事項](/post/2013/09/15/Git-for-Windows-Line-Ending-Conversion-Notes.aspx)】文章中提到的詭異現象。

這時我意識到，雖然我們在 **.gitattributes** 設定了 **\* text=auto**，讓 Git 自動判斷檔案的類型，而且大部分的情況下也都判斷正確，但還是有那麼一點機率會誤判。所以，我決定在 **.gitattributes** 設定檔多新增一條 **\*.cshtml** 的設定，讓所有 \*.cshtml 都被視為文字檔案，其設定如下，請加在 **\* text=auto** 的下一行：

```
*.cshtml text
```

如果你想定義特定副檔名為「二進位」檔案，那麼你可以設定如下：（我以 \*.eot 為例）

```
*.eot  -text
```

**3\. 重置專案目錄下的所有版控中的檔案，重新執行一次「正規化」的動作**

由於那些在版控中的檔案，檔案非常多，而且被許多人提交過不同版本，且可能有些版本是從 SVN 遷移過來的檔案，所以可能並不是所有檔案都被正規化過。這時你可以參考以下指令，將專案目錄下的**所有版控中的檔案**，重新執行一次「正規化」的動作：  
**注意:** 執行以下指令前，請先確保工作目錄是乾淨的，並且開啟命令提示字元到工作目錄的最上層目錄！

```
del .git\index git reset git status git add -u . git add . git commit -m "Introduce end-of-line normalization"
```

如此一來，所有版控中的檔案，就會重新執行一次「正規化」的動作！

上述指令，我們先將 .git\\index 索引檔刪除，再透過 git reset 指令會將所有檔案會重新取出，然後對於那些重新被正規化的檔案會被標示為被修改 ( git status )，這時再將這些檔案都加入版控並提交新版，就大功告成了！

**請注意**：你一定要先設定好 **.gitattributes** 再執行這批指令！

**4\. 清除** [**Visual Studio Tools for Git**](http://visualstudiogallery.msdn.microsoft.com/abafc7d6-dcaa-40f4-8a5e-d6724bdb980c) **建立的工作目錄狀態快取**

[Visual Studio Tools for Git](http://visualstudiogallery.msdn.microsoft.com/abafc7d6-dcaa-40f4-8a5e-d6724bdb980c) 比 Git for Windows 工具多了一個功能，那就是「**排除的變更**」，有別於 **.gitignore** 檔案，這些明確列在這裡的檔案僅適用於 Visual Studio 開發環境，所以請不要誤以為這跟 **.gitignore** 檔案有任何關係。你在 Visual Studio 中透過 [Visual Studio Tools for Git](http://visualstudiogallery.msdn.microsoft.com/abafc7d6-dcaa-40f4-8a5e-d6724bdb980c) 操作的版控狀態，事實上都被寫入到 **.git\\ms-persist.xml** 檔案中，要清除詭異的檔案狀態，建議可以將此檔案刪除，許多在 Visual Studio 裡面操作 Git 版控時遇到的鬼打牆事件都會煙消雲散。( 此檔案會自動重建，所以各位可以放心刪除 )

**5\. 通知所有團隊成員重新取出專案**

如果你發現真的有許多檔案沒有正規化，請通知所有團隊成員，重新取出一次該專案，確保工作目錄都是乾淨的，以免日後再次發生詭異的 Git 版控事件。

**6\. 建議同時設定 AutoCRLF 與 SafeCRLF 這兩個設定選項**

```
git config --global core.autocrlf truegit config --global core.safecrlf true
```

**7\. 確保團隊成員都使用相同版本的 Git 工具，並隨時更新到最新版**

**相關連結**

-   [Git 在 Windows 平台處理斷行字元 (CRLF) 的注意事項](/post/2013/09/15/Git-for-Windows-Line-Ending-Conversion-Notes.aspx)
-   [Git - gitattributes Documentation](http://git-scm.com/docs/gitattributes)
-   [Visual Studio Tools for Git extension](http://visualstudiogallery.msdn.microsoft.com/abafc7d6-dcaa-40f4-8a5e-d6724bdb980c) (Visual Studio 2012)
-   [Git中的AutoCRLF与SafeCRLF换行符问题](http://boliquan.com/git-and-autocrlf-in-safecrlf-line-break-problem/)
