.gitattributes換行規則:為什麼只改一行卻整檔顯示差異

改網站的 JavaScript,只動了一行,Git 卻把整份檔案標成刪除再新增。可能是編輯器順便換了行尾,也可能是格式化、編碼或真正的內容變動。別急著還原整份檔案,先把工作檔與 Git 索引分開看。

先問:差異真的只在行尾嗎?

LF 是一個換行字元;CRLF 則是 CR 加 LF。它們在編輯器裡都像換行,但檔案的位元組不同。在專案根目錄查看一個檔案:

git diff -- app.js
git diff --cached -- app.js
git ls-files --eol -- app.js

i/ 表示索引裡的行尾,w/ 是工作檔,attr/ 是適用屬性。索引是下一次提交的內容,不是你目前開著的檔案。若兩邊行尾不同,可用 git diff --ignore-space-at-eol -- app.js 輔助比較;它也會忽略其他行尾空白,不能當作「沒有真正修改」的證明。

一份設定檔,兩個生效時機

在根目錄的 .gitattributes 寫入這組示例:

*.js text eol=lf
*.bat text eol=crlf

text 讓這些文字檔在加入索引時正規化為 LF;eol 決定 Git 寫出工作檔時的行尾。這裡要求 JavaScript 用 LF、批次檔用 CRLF。它不是儲存檔案就自動執行的編輯器設定,也別把圖片或壓縮包硬標成文字檔。

以下是兩行 JavaScript 的前後對照。原檔先以 CRLF 提交,之後才新增上述屬性:

操作 索引 工作檔
新增 .gitattributes,尚未重新加入 CRLF CRLF
對 app.js 重新正規化 LF 仍是 CRLF
提交後建立新的 clone LF LF

同一份新 clone 的 build.bat 則是索引 LF、工作檔 CRLF。因此,寫了 eol=lf 卻看到舊工作檔還是 CRLF,不一定是規則沒生效:Git 尚未重新寫出這個檔案。

要正規化,先隔開手上的改動

先備份工作檔,檢查未暫存與已暫存差異,保存尚未提交的修改;再從乾淨狀態開一個處理分支。先對單一檔案試:

git check-attr text eol -- app.js
git add --renormalize -- app.js
git diff --cached -- app.js

--renormalize 會依目前規則重新處理已追蹤檔案並放進索引;檔案裡若還有功能修改,也可能一併暫存。範例只改了索引行尾,工作檔的位元組原樣保留。確認範圍後,再把 .gitattributes 與正規化結果一起提交,避免和網站功能修改混成一筆。

不要直接對整個專案執行 git add --renormalize .,再略過差異。若尚未提交,且原先沒有這個檔案的暫存改動,可用 git restore --staged -- app.js 撤回索引變更;工作檔不會因此被覆寫,屬性檔的修改仍要另查。提交後要檢查實際工作檔行尾,另建乾淨 clone 比強制覆寫正在編輯的目錄更容易核對。

參考資料

原文鏈接:https://wntheme.com/gitattributes-lf-crlf-diff/,轉載請註明出處。
0

評論0

顯示驗證碼
沒有帳號?註冊  忘記密碼?