Git status 顯示太多不必要的檔案遭到修改?一招教你如何忽略已追蹤檔案:skip-worktree

最近在使用 git 的時候遇到一個問題,某些專案裡面的文件會隨著每次編譯而改動,但是這些改動我並不想要推上 git。舉例來說,每次編譯程式結束後,某個 .xml 副檔名的文件都會因此改動,照理來說這個改動應該保留在 local 端,因此我們想要忽略追蹤該 .xml檔,但現實是每次下 git status 的時候,都會顯示該 .xml 檔遭到修改。若只有一兩個文件倒是還好,可以選擇性忽略,但是當這種文件出現上百個的時候,就讓人覺得很厭煩了,所以我花了一些時間研究怎麼忽略這些惱人的已追蹤檔案。

.gitignore 沒有用!

首先,我原本以為將這些文件的附檔名加入到 .gitignore 一切都解決了,可惜的是 .gitignore 只會忽略新增加的檔案,如果有些檔案已經 push 上 git,即便它們被包含在 .gitignore 的規則裡,經過 local 端修改後,這些檔案還是會被 tracked,下 git status 之後依然會出現。

solution

於是為了解決這個惱人的問題,我花了一些時間研究,才發現 git 原來有 untrack 的功能,關鍵字就是 update-index,以及 --skip-worktree--assume-unchanged。簡單來說,--skip-worktree 會 untrack 指定的文件,即使在 local 端修改該文件,git status 也不會顯示這個檔案有做修改。

$ git update-index --skip-worktree path/to/only/one/filename
$ git update-index --assume-unchanged path/to/only/one/filename

但是需要注意的是,這個指令只能用在單一文件上,因此類似這種的泛型檔名 **/*.xml 會無法順利運作。如果想要把某個資料夾裏面所有的文件都 untrack 的話,就必須進入該資料夾,再 parse 所有文件並輸入 --skip-tree 的指令。舉例來說,如果在 windows 系統下操作,可以執行以下指令將 dir 內所有的文件 untrack:

$ cd dir
$ git ls-files -z | xargs -0 git update-index --skip-worktree
$ cd ..

往後如果開發者想要重新追蹤某文件的話,只要下 --no-skip-worktree 或是 --no-assume-unchanged,就可以讓 git 開始追蹤指定的檔案了。如果想要重新追蹤資料夾內的所有檔案,只要將上面 parsing 指令的 --skip-worktree 修改成 --no-skip-worktree 就可以了。

$ git update-index --no-skip-worktree path/to/only/one/filename
$ git update-index --no-assume-unchanged path/to/only/one/filename

最後我們可以依據以下 windows 指令,爬出所有被 untracked 的檔案:

$ git ls-files -v . | findstr "^S" // parse --skip-worktree files
$ git ls-files -v . | findstr "^h" // parse --assume-unchanged files

淺談 –skip-worktree 和 –assume-unchanged 差異

最後,我們稍微討論一下兩者的差異。我想最大的差別在於,用 --skip-worktree 忽略追蹤的檔案,即使遇到 git reset --hard,還是不會遭到遠端的檔案覆蓋,因此這個指令適用於會隨時遭到 local 端修改,卻又不想被追蹤的檔案。而利用 --assume-unchanged 忽略追蹤的檔案,遇到 git reset --hard 的指令後,會被遠端的原始檔案覆蓋,因此該指令適用於不應該遭到 local 端修改,卻又想取消追蹤的檔案。

舉例來說,會隨著每次編譯產生出來的檔案應該要用 --skip-worktree 取消追蹤,因為每次編譯出來的檔案都會有些許不一樣。或是有些環境變數相關的檔案也可以用 --skip-worktree 取消追蹤。而某些通用的 library 則是比較適合用 --assume-unchanged,因為這些檔案屬於不應該被修改的檔案,如果不小心動到這些檔案,reset 時也可以復原到原始的狀態。

--skip-worktree 用於會被 local 端修改的檔案

--assume-unchanged 用於不應該被 local 端修改的檔案

References

Git – Difference Between ‘assume-unchanged’ and ‘skip-worktree’
How to list files ignored with ‘skip-worktree’