改網站的 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 比強制覆寫正在編輯的目錄更容易核對。

評論0