Alan Tsai 的學習筆記


學而不思則罔,思而不學則殆,不思不學則“網貸” 為現任微軟最有價值專家 (MVP)、微軟認證講師 (MCT) 、Blogger、Youtuber:記錄軟體開發的點點滴滴 著重於微軟技術、C#、ASP .NET、Azure、DevOps、Docker、AI、Chatbot、Data Science

[git]為什麼常出現有修改但是比對不卻顯示不出差異?談談檔案斷行問題和如何達到不同平台正確一致化

2017-07-02 Sunday
用git之後,有時候遇到在檢查狀態的時候明明顯示有修改,但是實際比對的時候發現完全沒有差異

通常來說發生這種情況就是檔案的斷行跑掉了,如果沒有好好解決到時候log會常常出現修改,但是實際上並不是真的修改,未來查找log很辛苦。

在這篇將會對於如何一勞永逸處理掉這個斷行問題做個說明。

問題描述

有時候檢查檔案狀態,發現有變更,但是實際比對會發現其實什麼變更都沒有:

log有修改,但是比對發現沒有任何差異
log有修改,但是比對發現沒有任何差異

當然,如果各位有在每次commit的時候認真比對的話,那麼一定不會把這個加到commit裡面,但是有時候如果漏看的話,不小心把這個commit上去, 只會造成未來在看的時候有更多雜訊,浪費時間。

解決方式

首先要設定好讓git自動做斷行處理,再來如果專案裡面以前沒有設定過,很有可能斷行已經亂掉,這時候做一個一致化(Normalize line ending)的動作。

設定

要一次解決這個問題,可以透過兩個設定:

git config --global core.autocrlf
  1. 如果是windows設定為true

    git config --global core.autocrlf true

  2. 如果是Unix系列設定為 input

    git config --global core.autocrlf input

為專案加入 .gitattributes檔案

檔案裡面要加入 * text=auto

更多gitattribute關於斷行的設定可以參考這個github repo 一些不同語言的gitattributes範本

或者可以看我自己每一個專案都會放的gitattribute檔案:我的gitattribute

一致化

做好了以上設定,要把現行的斷行一致化就變得很簡單,只需要:

假設有任何修改尚未commit,先commit
git commit -am "儲存目前修改"
把所有檔案刪掉(不包含.git)
git rm --cached -r .
重新從repo把刪除的修改revert掉
git reset .
加入index
git add -u - 如果有看到很多warning提示會從crlf轉成lf是正常的。
commit上去
git commit -m "斷行一致化處理"

如果對於這兩個設定的目的和用途為何,請繼續往下看。

問題發生的原因

Windows 和 Unix(包含Linux和mac)代表斷行的字符是不一樣的。

Windows
crlf - 兩個字符代表斷行
Unix
lf - 字符代表斷行

還記得git是版控檔案內容,所以假設本來是crlf,如果被改成了lf那麼對於git來說就是一個修改。

但是因為斷行字符是看不到的內容,所以有些比對工具不會顯示出任何差異,造成混亂。

而本質上這些不應該算是修改,而是因為作業系統不同而導致的問題,所以git本身有考慮進去怎麼處理。

要解決這個問題其實很簡單,假設不管哪個作業系統都是用一樣的斷行作為儲存,那麼就不會有因為斷行不一樣的原因出現修改。

假設一開始沒有設定好,那麼很有可能有些檔案存成lf有些存成crlf,這個時候就需要做斷行一致化的動作(normalize line ending)

git 如何處理這個問題 - 自動轉換的時間點

在git裡面,其實內建的時候就有考慮到這個事情,所以他有允許做自動轉換。

這個自動轉換發生在兩個時間點:

當把檔案寫到repo的時候
發生的時間點如:git addgit commit
當把檔案從repo讀出來的時候
發生的時間點如:git checkout

所以我們可以透過設定告訴git在這兩個時間點的時候,應該要怎麼轉換。

git 斷行自動轉換相關設定

在git裡面有兩個設定是告訴git如何做這個斷行轉換:

  1. git config 的 core.autocrlf
  2. .gitattributes設定

這邊git config的設定對所有專案都有作用,但是只能夠對自己有起作用,換句話說如果在團隊裡面,有些人的電腦沒有設定好 那麼只靠這個設定是不夠的,因為很有可能就是有個別的電腦設定有問題導致斷行錯亂。

因此,.gitattributes的作用就出來了,這個是能夠跟著專案走的斷行處理設定,設定了就不用擔心有個別電腦沒有設定 core.autocrlf的問題。

git config core.autocrlf

這個其實就是在設定讀和寫的時候自動轉換斷行的處理,總共有三個設定:

  • auto
  • input
  • false

這三個設定分別為:

的時候轉換的時候轉換建議
autolf 轉 crlf crlf 轉 lfWindows作業系統使用
input不轉換crlf 轉 lfUnix作業系統使用
false不轉換不轉換不建議使用

.gitattributes

這個檔案放在同.git資料夾層級,裡面可以設定一些檔案屬性,其中一個就是轉換的部分

一個範例檔案可能長成如下:

  * text=auto
  *.md text
  *.csproj text eol=crlf

自動轉換的設定是* text=auto - 這個意思是只要是text類型的檔案git會自動轉換(類似於core.autocrlf但是更加智能)。

這個時候可能會好奇,到底哪些是屬於text檔案?git會自動判斷,不過也可以透過設定強制告訴git,例如:*.md text就是說md結尾的都是 屬於text類型 - 換句話說就是會做轉換斷行的處理。

也有可能有些檔案要保持某一個斷行,例如*.csrpoj(C# 專案檔案,要保持crlf),這個時候也可以透過設定:*.csproj text eol=crlf

如果想參考一些不同檔案的設定,可以參考這個repo: 一些不同語言的gitattributes範本

我自己也有一個常用的設定,也可以參考一下:我的gitattribute

要注意一下,假設今天你不是使用指令的方式在執行git - 那麼有些gui其實不一定會吃.gitattributes的設定(有些ide的git套件不會吃這個設定 - 不確定是否目前還是如此,不過曾經有過像egit不吃這個設定) 所以最好還是要搭配git config的設定。

結語

斷行的部分其實還有幾個相關設定,不過那些設定基本上不會碰到,因此我就沒有提。

希望透過這篇也讓大家了解到斷行在不同作業系統會帶來的問題,並且怎麼樣能夠一勞永逸的解決。

gitattributes其實還有別的進階用途,未來有機會在和大家介紹

參考資料

Mind the End of Your Line(英文)
一篇很詳細介紹關於斷行問題的文章
gitattributes的官方說明(英文)
介紹整個gitattributes的其他應用
github介紹一致化斷行的處理(英文)
標籤:

如果文章對您有幫助,就請我喝杯飲料吧
街口支付QR Code
街口支付QR Code
台灣 Pay QR Code
台灣 Pay QR Code
Line Pay 一卡通 QR Code
Line Pay 一卡通 QR Code
街口支付QR Code
支付寶QR Code
街口支付QR Code
微信支付QR Code
2017-07-02 Sunday
comments powered by Disqus