最近在使用 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’