GitHub 第一坑:換行符自動轉換

CSS魔法發表於2013-08-19

源起

一直想在 GitHub 上釋出專案、參與專案,但 Git 這貨比較難學啊。買了一本《Git 權威指南》,翻了幾頁,媽呀,那叫一個複雜,又是 Cygwin 又是命令列的,嚇得我不敢學了。

終於某天發現 GitHub 還有一個 Windows 客戶端,試了一下還挺好用。不需要掌握太多的 Git 原理和命令,也可以在 GitHub 上麻溜建專案了,甚是歡喜。可是好景不長,第一次參與開源專案就出洋相了。

經過

小心翼翼地 Fork 了樸靈大大 (@JacksonTian) 的 EventProxy 專案,本地改好提交,同步到伺服器,懷著激動的心情發出 Pull Request……這時發現問題了。我發現 diff 圖表顯示的更新並不僅是我修改的那幾行,而是整個檔案都顯示為已修改。(下圖為示意圖)

這看起來很奇怪啊,於是趕緊撤回 Pull Request,自己悶頭找原因。

初步定位是檔案的換行符問題,因為我發現本地的檔案是 Windows 換行符,但很顯然大家現在做專案都是用 UNIX 換行符啊。這是一大疑點,於是在反覆對比 Web 端和本地的各個檔案、各個版本之後,基本定位到了問題所在。

背景

在各作業系統下,文字檔案所使用的換行符是不一樣的。UNIX/Linux 使用的是 0x0A(LF),早期的 Mac OS 使用的是0x0D(CR),後來的 OS X 在更換核心後與 UNIX 保持一致了。但 DOS/Windows 一直使用 0x0D0A(CRLF)作為換行符。(不知道 Bill Gates 是怎麼想的,雙向相容?)

這種不統一確實對跨平臺的檔案交換帶來麻煩。雖然靠譜的文字編輯器和 IDE 都支援這幾種換行符,但檔案在儲存時總要有一個固定的標準啊,比如跨平臺協作的專案原始碼,到底儲存為哪種風格的換行符呢?

Git 作為一個原始碼版本控制系統,以一種(我看起來)有點越俎代庖、自作聰明的態度,對這個問題提供了一個“解決方案”。

Git 由大名鼎鼎的 Linus 開發,最初只可執行於 *nix 系統,因此推薦只將 UNIX 風格的換行符儲存入庫。但它也考慮到跨平臺協作的場景,並且提供了一個“換行符自動轉換”功能。

這個功能預設處於“自動模式”,當你在簽出檔案時,它試圖將 UNIX 換行符(LF)替換為 Windows 的換行符(CRLF);當你在提交檔案時,它又試圖將 CRLF 替換為 LF。

(看明白了嗎?一個版本控制系統會在你不知不覺的情況下修改你的檔案。這 TM 簡直酷斃了,對吧?)

缺陷

Git 的“換行符自動轉換”功能聽起來似乎很智慧、很貼心,因為它試圖一方面保持倉庫內檔案的一致性(UNIX 風格),一方面又保證本地檔案的相容性(Windows 風格)。但遺憾的是,這個功能是有 bug 的,而且在短期內都不太可能會修正。

問題具體表現在,如果你手頭的這個檔案是一個包含中文字元的 UTF-8 檔案,那麼這個“換行符自動轉換”功能 在提交時是不工作的(但簽出時的轉換處理沒有問題)。我猜測可能這個功能模組在處理中文字元 + CRLF 這對組合時直接崩潰返回了。

這可能還不是唯一的觸發場景(畢竟我沒有太多精力陪它玩),但光是這一個坑就已經足夠了。

踩坑

這是一個相當大的坑,Windows 下的中文開發者幾乎都會中招。舉個例子,你在 Windows 下用預設狀態的 Git 簽出一個檔案,寫了一行中文註釋(或者這個檔案本來就包含中文),然後存檔提交……不經意間,你的檔案就被毀掉了。

因為你提交到倉庫的檔案已經完全變成了 Windows 風格(簽出時把 UNIX 風格轉成了 Windows 風格但提交時並沒有轉換),每一行都有修改(參見本文開頭的示意圖),而這個修改又不可見(大多數 diff 工具很難清楚地顯示出換行符),這最終導致誰也看不出你這次提交到底修改了什麼。

這還沒完。如果其他小夥伴發現了這個問題、又好心地把換行符改了回來,然後你又再次重演上面的悲劇,那麼這個檔案的編輯歷史基本上就成為一個謎團了。

由於老外幾乎不可能踩到這個坑,使得這個 bug 一直隱祕地存在著。但在網上隨便搜一下,就會發現受害者絕對不止我一個,比如 這位大哥的遭遇 就要比我慘痛得多。

防範

首先,不要著急去整 Git,先整好自己。你的團隊需要確定一個統一的換行符標準(推薦使用 UNIX 風格)。然後,團隊的成員們需要分頭做好準備工作——配置好自己的程式碼編輯器和 IDE,達到這兩項要求:

  • 在新建檔案時預設使用團隊統一的換行符標準
  • 在開啟檔案時保持現有換行符格式不變(不要做自動轉換)

這樣一方面可以最大程度保證專案程式碼的規範一致,另一方面,即使現有程式碼中遺留了一些不規範的情況,也不會因為反覆轉換而導致混亂。(當然,作為一個強迫症患者,我還是祝願所有的專案從一開始就步入嚴謹有序的軌道。)

接下來,我們就可以開始調教 Git 了。我的建議是, 完全關掉這個自作聰明的“換行符自動轉換”功能。關閉之後,Git 就不會對你的換行符做任何手腳了,你可以完全自主地、可預期地控制自己的換行符風格。

下面主要針對不同的 Git 客戶端,分別介紹一下操作方法。

Git for Windows

這貨由 Git 官方出品,在安裝時就會向你兜售“換行符自動轉換”功能,估計大多數人在看完華麗麗的功能介紹之後會毫不猶豫地選擇第一項(自動轉換)。請千萬抵擋住誘惑,選擇最後一項(不作任何手腳)。

如果你已經做出了錯誤的選擇,也不需要重新安裝,可以直接使用命令列來修改設定。很簡單,直接開啟這貨自帶的命令列工具 Git Bash,輸入以下命令,再敲回車即可:

TortoiseGit

很多從 TortoiseSVN 走過來的同學很可能會選用 TortoiseGit 作為主力客戶端,那麼也需要配置一下。在 Windows 資源管理器視窗中點選右鍵,選擇“TortoiseGit → Settings → Git”,做如下設定。

(由於 TortoiseGit 實際上是基於 Git for Windows 的一個 GUI 外殼,你在上一節所做的設定會影響到上圖這些選項的狀態,它們可能直接就是你所需要的樣子了。)

GitHub 的 Windows 客戶端

它是今天的第二被告。這貨很容易上手,很適合小白,我主要用它來一鍵克隆專案到本地。可能正是為了維護簡潔易用的親切形象,這貨並沒有像 TortoiseGit 那樣提供豐富的選項(對“換行符自動轉換”這樣的細節功能完全諱莫如深啊,我這樣的小白死了都不知道怎麼死的……)。因此,我們需要手動修改一下它的配置。

GitHub 的 Windows 客戶端實際上也是一個殼,它自帶了一個便攜版的 Git for Windows。這個便攜版和你自己安裝的 Git for Windows 是相互獨立的,不過它們都會使用同一個配置檔案(實際上就是當前使用者主目錄下的 .gitconfig 檔案)。

所以如果你已經配置好了自己安裝的 Git for Windows,那就不用操心什麼了。但如果你的機器上只裝過 GitHub 的 Windows 客戶端,那麼最簡單的配置方法就是手工修改配置檔案了。

修改 Git 的全域性配置檔案

進入當前使用者的主目錄(通常 XP 的使用者目錄是 C:\Documents and Settings\yourname,在 Vista 和 Win7 下是C:\Users\yourname),用你最順手的文字編輯器開啟 .gitconfig 檔案。

在 [core] 區段找到 autocrlf,將它的值改為 false。如果沒找到,就在 [core] 區段中新增一行:(最終效果見圖)

事實上上面介紹的所有命令列或圖形介面的配置方法,最終效果都是一樣的,因為本質上都是在修改這個配置檔案。

還有

關掉了 Git 的“換行符自動轉換”功能就萬事大吉了嗎?失去了它的“保護”,你心裡會有點不踏實。你可能會問:如果我不小心在檔案中混入了幾個 Windows 回車該怎麼辦?這種意外可以防範嗎?

事實上 Git 還真能幫你制止這種失誤。它提供了一個換行符檢查功能(core.safecrlf),可以在提交時檢查檔案是否混用了不同風格的換行符。這個功能的選項如下:

  • false – 不做任何檢查
  • warn – 在提交時檢查並警告
  • true – 在提交時檢查,如果發現混用則拒絕提交

我建議使用最嚴格的 true 選項。

和 core.autocrlf 一樣,你可以通過命令列、圖形介面、配置檔案三種方法來修改這個選項。具體操作就不贅述了,大家自己舉一反三吧。

最後

你可能還會問,如果我的編輯器一不小心把整個檔案的換行符都轉換成了另一種格式怎麼辦?還能預防嗎?

這……我就真幫不了你了。所以還是建議大家在提交檔案之前多留心檔案狀態:

如果發現變更行數過多,而且增減行數相同,就要警惕是不是出了意外狀況。被圖形介面慣壞的孩子往往缺乏耐心,對系統資訊視而不見,看到按鈕就點,容易讓小疏忽釀成大事故。所以高手們青睞命令列,並不是沒有道理的。

好了,小夥伴們,今天的《踩坑歷險記》就到這兒,我們下集再見!祝大家編碼愉快!

相關文章