The Will Will Web | Visual Studio Tools for Git 處理斷行字元 (CRLF) 的注意事項

The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

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

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

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

image

不過,從 Visual Studio Tools for Git 裡面的「變更」視窗所看到的,是完全沒有變更的提示:

image

簡言之,都是「斷行字元」(換行字元) 搞的鬼!

各位在看我之前的【Git 在 Windows 平台處理斷行字元 (CRLF) 的注意事項】文章時,會看到一句話:

當執行 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) 的注意事項】文章中提到的詭異現象。

這時我意識到,雖然我們在 .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 建立的工作目錄狀態快取

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

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

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

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

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

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

相關連結