Git 進(jìn)階 | 3分鐘學(xué)會(huì)優(yōu)雅處理換行符

https://git-scm.com/

我猜點(diǎn)進(jìn)來看的客官,至少都是使用過 Git 的衅胀。另外有些客官骄恶,可能知道琉苇,如果 Git 的換行符處理不當(dāng),就會(huì)產(chǎn)生某些問題娇钱。還有一些客官伤柄,也許正在忍受由于換行符處理不當(dāng)所帶來的各種問題。

究竟會(huì)有啥問題文搂,我咋不知道适刀?

在描述問題之前,先簡(jiǎn)要的說一些背景知識(shí)煤蹭。技術(shù)大牛大可直接略過笔喉。

由于歷史原因,不同的操作系統(tǒng)硝皂,在處理換行符時(shí)常挚,使用了不同的方案。Windows 操作系統(tǒng)使用了 CRLF稽物,而 Unix 陣營的操作系統(tǒng)則使用了 LF奄毡。Mac OS 最起初使用了 CR,后來到了 Mac OS X 后贝或,改成了使用 LF吼过,與 Unix 陣營保持了一致锐秦。雖然目前很多代碼編輯器都支持自動(dòng)識(shí)別和切換換行符風(fēng)格,然而那先,總有那么一些不合群的編輯器农猬,無法達(dá)到相應(yīng)的兼容性赡艰。

所以售淡,作為一個(gè)使用不限于 Git 的版本控制系統(tǒng)的程序猿,你很可能會(huì)遇到與換行符相關(guān)的尷尬局面慷垮。

第1層問題:被毀掉的 diff 工具

  • 執(zhí)行git status 命令揖闸,凡是打開過的文件,無論是否改過料身,其狀態(tài)都是已修改的狀態(tài)……
  • 同事說他只改了一行代碼汤纸,然而我看 diff,發(fā)現(xiàn)整個(gè)文件都被改了……

以上兩個(gè)問題芹血,在跨平臺(tái)的項(xiàng)目開發(fā)過程中贮泞,會(huì)經(jīng)常出現(xiàn)。問題背后的原理幔烛,其實(shí)很簡(jiǎn)單啃擦。你的代碼編輯器兼容性不強(qiáng),在打開一個(gè)與自身默認(rèn)的換行符風(fēng)格不符的文件時(shí)饿悬,編輯器就自作主張令蛉,自動(dòng)地將換行符風(fēng)格轉(zhuǎn)換成自己的默認(rèn)風(fēng)格,并保存了下來狡恬。于是便出現(xiàn)了明明沒有改動(dòng)珠叔,卻被標(biāo)記為修改狀態(tài)的現(xiàn)象。

這個(gè)問題確實(shí)討厭弟劲。因?yàn)橐坏┏霈F(xiàn)該問題祷安,diff 工具的功效便瞬間大打折扣。當(dāng)然通過使用 git diff -w兔乞,自動(dòng)忽略掉空白字符修改辆憔,可以很大程度減少問題帶來的影響,但是报嵌,Git 提交歷史中充斥著的大量毫無意義的換行符修改記錄虱咧,同樣會(huì)持續(xù)性的消耗開發(fā)人員有限而寶貴的精力。

所以锚国,有人想出了一個(gè)方案:只要我們?nèi)勘3纸y(tǒng)一腕巡,只用一種換行風(fēng)格就可以了。Git 其實(shí)也給出了解決方案血筑。在 Windows 平臺(tái)安裝 Git 時(shí)绘沉,肯定遇到過這樣一個(gè)選項(xiàng):

CRLF or LF

無論我們選擇第一種還是第二種選項(xiàng)煎楣,我們都可以保證,提交至版本庫的代碼都會(huì)被統(tǒng)一轉(zhuǎn)化成 LF 風(fēng)格车伞。背后的原理择懂,從圖中也可以看出來,其實(shí)就是把 core.autocrlf 選項(xiàng)設(shè)置成為了 trueinput另玖。當(dāng)然困曙,如果你是一個(gè)非常自律且注重細(xì)節(jié),或者不喜歡被條條框框的規(guī)則約束谦去,甚至有些潔癖的程序猿慷丽,你大可勇敢的選擇第三個(gè)選項(xiàng),然后優(yōu)雅的配置好你的代碼編輯器鳄哭,保證所有提交的風(fēng)格一致要糊。

然而,你以為這樣就萬事大吉的了嗎妆丘?不好意思锄俄,惡夢(mèng)可能剛剛開始……

第2層問題:惡魔般的已修改狀態(tài)

自從團(tuán)隊(duì)統(tǒng)一設(shè)置了 core.autocrlf 選項(xiàng)后,好長一段時(shí)間勺拣,我們的代碼提交奶赠,都顯得十分的干凈明了。在 commit 和 patch 中宣脉,再也沒有出現(xiàn)過大規(guī)模無意義的換行符修改车柠。然而,直到有一天塑猖,在我們團(tuán)隊(duì)來了一名新員工后竹祷,這種和諧便被打破了。

我們真的不知道他到底做了些什么羊苟,我們只知道塑陵,當(dāng)我們通過 git pull 命令與服務(wù)器同步后,有幾個(gè)文件蜡励,在剛剛 checkout 至工作區(qū)且沒有被任何編輯器打開或修改的狀態(tài)下令花,它們就已經(jīng)處于了 modified 狀態(tài)。更可怕的是凉倚,對(duì)該文件重新執(zhí)行 checkout 命令兼都,問題依然存在:

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
    modified:   test.c
no changes added to commit (use "git add" and/or "git commit -a")
$ git checkout ./test.c 
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   test.c

no changes added to commit (use "git add" and/or "git commit -a")

這種問題,可能不少人都遇到過稽寒。是不是覺得有些抓狂扮碧,甚至有些擔(dān)心,我們的版本庫是不是被那個(gè)毛孩子搞壞了?可是問題究竟是如何產(chǎn)生的呢慎王?

這種問題經(jīng)常會(huì)出現(xiàn)在跨平臺(tái)項(xiàng)目中的 Windows 開發(fā)環(huán)境中(喂蚓土,那些鄙視 Windows 的程序員不要鬧哈,這個(gè)鍋還真不能讓 Windows 背)赖淤。雖然團(tuán)隊(duì)規(guī)定了換行符的統(tǒng)一風(fēng)格蜀漆,甚至還要求每個(gè)人都對(duì) Git 做同樣的設(shè)置。然而咱旱,總會(huì)有一些狂奔不羈愛自由的野馬程序員确丢,或者是一些舉止粗曠的粗心程序員沒有按章行事。于是莽龟,就有這樣一位隊(duì)友蠕嫁,在某次提交中锨天,提交了一個(gè)混合了兩種換行符的文件毯盈。接下來,剩下的那些乖孩子病袄,由于之前設(shè)置過 core.autocrlf = true搂赋,在從服務(wù)器更新這個(gè)文件時(shí),Git 會(huì)自動(dòng)將文件轉(zhuǎn)化為統(tǒng)一的 CRLF 風(fēng)格益缠。此時(shí)脑奠,如果對(duì)工作區(qū)里面的文件進(jìn)行 CRLFLF 風(fēng)格的轉(zhuǎn)換,然后將轉(zhuǎn)換后的文件與版本庫中的對(duì)應(yīng)文件對(duì)比幅慌,Oops宋欺!就會(huì)發(fā)現(xiàn)這兩個(gè)文件竟然不一致!Git 就是通過這種方式胰伍,認(rèn)定一定是你自己把文件修改了……

如果你真的遇到了這個(gè)問題齿诞,也不要慌。通過先把 core.autocrlf 設(shè)置為 false骂租,然后人工將該文件的換行符統(tǒng)一修改為 LF 并提交祷杈,最后再把 core.autocrlf 重新設(shè)置成 true 即可解決問題。

通過上面說的辦法渗饮,雖然問題解決了但汞。但是有人要問了:我這次雖然是解決了,但是團(tuán)隊(duì)這么多人互站,要是每天都有幾個(gè)豬隊(duì)友不小心私蕾,手一抖,再搞出幾個(gè)這樣的幺蛾子出來胡桃,我豈不是要被他們煩死踩叭?

不錯(cuò),對(duì)于這種情況标捺,Git 確實(shí)也提供了一個(gè)選項(xiàng)懊纳,叫做 core.safecrlf揉抵。當(dāng)你同時(shí)設(shè)置了 core.autocrlfcore.safecrlf 參數(shù)后,如果你的提交混合了兩種換行符嗤疯,Git 就會(huì)根據(jù)你的設(shè)置發(fā)出警告冤今,或者拒絕提交。

所以茂缚,這樣戏罢,就又可以萬事大吉了?抱歉脚囊,還是不行龟糕!

第3層問題:被毀掉的數(shù)據(jù)和工程

上面提到的被毀掉的 diff ,以及偶爾冒出來的小惡魔可能并沒有那么嚴(yán)重悔耘。然而你要小心讲岁,換行符問題還可能毀掉你的代碼和工程。在 Git manpages 中衬以,有這樣一段描述缓艳。

CRLF conversion bears a slight chance of corrupting data. autocrlf=true will convert CRLF to LF during commit and LF to CRLF during checkout. A file that contains a mixture of LF and CRLF before the commit cannot be recreated by git. For text files this is the right thing to do: it corrects line endings such that we have only LF line endings in the repository. But for binary files that are accidentally classified as text the conversion can corrupt data.

簡(jiǎn)單來說,Git 的換行符自動(dòng)切換功能雖然好用看峻,但是是有一定風(fēng)險(xiǎn)的阶淘。因?yàn)椋创a文件之所以允許換行符的隨意切換互妓,是因?yàn)樵创a對(duì)換行符這一數(shù)據(jù)是不敏感的溪窒。對(duì)于某些特定的數(shù)據(jù),例如二進(jìn)制文件冯勉。如果該文件被意外的識(shí)別成了文本文件澈蚌,并執(zhí)行了換行符轉(zhuǎn)換,那么這個(gè)文件的數(shù)據(jù)可能就會(huì)被永久的損壞了珠闰。

什么惜浅,聽起來有些聳人聽聞?那我們就舉兩個(gè)口味稍微清淡點(diǎn)的例子:

  • bash 腳本文件應(yīng)當(dāng)使用 LF 作為換行符伏嗜,如果使用 CRLF 風(fēng)格的換行符坛悉,bash 解釋器可能無法正常工作;
包含CRLF風(fēng)格換行符的Bash腳本
$ ./crlf_bash.sh
-bash: ./crlf_bash.sh: /bin/sh^M: bad interpreter: No such file or directory
  • Windows 批處理 bat 文件最好使用 CRLF 作為換行符承绸,如果使用LF風(fēng)格的換行符裸影,且代碼中包含了中文字符,那么解釋器可能無法正常工作军熏;
中文+LF換行符的批處理腳本
批處理解釋器出錯(cuò)

這兩個(gè)例子轩猩,影響都比較有限。最多就是面對(duì)剛剛 clone 的代碼無法運(yùn)行的事實(shí),略顯崩潰均践。之后晤锹,你仍可以使用類似于 unix2dosdos2unix 之類的工具對(duì)文件的格式進(jìn)行轉(zhuǎn)換來解決問題。然而彤委,如果出問題的文件是一個(gè)二進(jìn)制數(shù)據(jù)鞭铆,那你只有哭了……

不過,幸好焦影,Git 專門為我們提供了 gitattributes 功能车遂,可以很好的解決這個(gè)問題。

優(yōu)雅處理換行符的終極方法

以上提到的問題斯辰,其實(shí)可以總結(jié)為兩類:

  1. Git 的換行符轉(zhuǎn)換策略配置需要與項(xiàng)目同步舶担,不能僅僅是依靠團(tuán)隊(duì)規(guī)范來約束;
  2. 文件是否需要做換行符轉(zhuǎn)換處理彬呻,是由文件本身的屬性決定的衣陶,需要對(duì)每個(gè)文件分別對(duì)待;

使用 .gitattributes 就完美解決了以上兩個(gè)問題:

  • .gitattributes 具有最高的優(yōu)先級(jí)废岂,無論你是否設(shè)置相關(guān)的換行符風(fēng)格轉(zhuǎn)化屬性祖搓,你都可以和團(tuán)隊(duì)保持一致狱意;
  • 使用 * text=auto 可以定義開啟全局的換行符轉(zhuǎn)換湖苞;
  • 使用 *.bat text eol=crlf 就可以保證 Windows 的批處理文件在 checkout 至工作區(qū)時(shí),始終被轉(zhuǎn)換為 CRLF 風(fēng)格的換行符详囤;
  • 使用 *.sh text eol=lf 就可以保證 Bash 腳本無論在哪個(gè)平臺(tái)上财骨,只要被 checkout 至工作區(qū),始終被保持 LF 風(fēng)格的換行符藏姐;
  • 使用 *.jpg -text 可以禁止 Git 將 jpg 文件識(shí)別為文本文件隆箩,從而避免由于換行符轉(zhuǎn)換引入的數(shù)據(jù)損壞;

關(guān)于 .gitattributes 的詳細(xì)使用方法羔杨,可以參考Git Documentation:gitattributes捌臊。

如果你覺得太麻煩,可以直接到這里兜材。鏈接中的網(wǎng)站理澎,提供了一系列針對(duì)各種開發(fā)環(huán)境,已經(jīng)寫好了的 .gitattributes 文件曙寡。

所以糠爬,趕緊給你的項(xiàng)目添加一個(gè) .gitattributes 文件吧!

參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末举庶,一起剝皮案震驚了整個(gè)濱河市执隧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖镀琉,帶你破解...
    沈念sama閱讀 210,835評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件峦嗤,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡屋摔,警方通過查閱死者的電腦和手機(jī)寻仗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,900評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來凡壤,“玉大人署尤,你說我怎么就攤上這事⊙窍溃” “怎么了曹体?”我有些...
    開封第一講書人閱讀 156,481評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長硝烂。 經(jīng)常有香客問我箕别,道長,這世上最難降的妖魔是什么滞谢? 我笑而不...
    開封第一講書人閱讀 56,303評(píng)論 1 282
  • 正文 為了忘掉前任串稀,我火速辦了婚禮,結(jié)果婚禮上狮杨,老公的妹妹穿的比我還像新娘母截。我一直安慰自己,他們只是感情好橄教,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,375評(píng)論 5 384
  • 文/花漫 我一把揭開白布清寇。 她就那樣靜靜地躺著,像睡著了一般护蝶。 火紅的嫁衣襯著肌膚如雪华烟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,729評(píng)論 1 289
  • 那天持灰,我揣著相機(jī)與錄音盔夜,去河邊找鬼。 笑死堤魁,一個(gè)胖子當(dāng)著我的面吹牛喂链,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播姨涡,決...
    沈念sama閱讀 38,877評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼衩藤,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了涛漂?” 一聲冷哼從身側(cè)響起赏表,我...
    開封第一講書人閱讀 37,633評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤检诗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后瓢剿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逢慌,經(jīng)...
    沈念sama閱讀 44,088評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,443評(píng)論 2 326
  • 正文 我和宋清朗相戀三年间狂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了攻泼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,563評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鉴象,死狀恐怖忙菠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情纺弊,我是刑警寧澤牛欢,帶...
    沈念sama閱讀 34,251評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站淆游,受9級(jí)特大地震影響傍睹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜犹菱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,827評(píng)論 3 312
  • 文/蒙蒙 一拾稳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧腊脱,春花似錦访得、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,712評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至捆姜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間迎膜,已是汗流浹背泥技。 一陣腳步聲響...
    開封第一講書人閱讀 31,943評(píng)論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留磕仅,地道東北人珊豹。 一個(gè)月前我還...
    沈念sama閱讀 46,240評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像榕订,于是被迫代替她去往敵國和親店茶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,435評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容