Medium 好讀版點此。
如果希望某個檔案不要被 git 追蹤,共有兩種方式,一種是讓協作的所有成員都知道這個檔案不要被追蹤,就放在 .gitignore;另一種是只在自己的本機端不要被追蹤,可放在 .git/info/exclude(我們終於要來看 .git/info/ 裡面的東西啦!)
本文將進行以下實驗,探討兩者差別:
簡單說明如下:
code/ 有被 git 追蹤的檔案推到 GitHub。another_code/;在 another_code/ 新建剛剛沒被推上 GitHub、但 code/ 有的檔案。another_code/ 有被 git 追蹤的檔案推到 GitHub。code/ 把當前 GitHub 上的檔案拉下來。透過這些實驗,我們將能感受到 .gitignore 與 .git/info/exclude 的差別。
先透過以下指令,在經過 git init 的 code/ 資料夾中,準備兩個想被忽略的檔案。
echo "Ignore in .gitignore" > ignore_gitignore.txt
再來是想被 .git/info/exclude 忽略的檔案:
echo "Ignore in exclude" > ignore_exclude.txt
ignore_gitignore.txt 要被加進 .gitignore 中:
echo "ignore_gitignore.txt" > .gitignore
而 ignore_exclude.txt 則是要被放在 .git/info/exclude 中:
echo "ignore_exclude.txt" > .git/info/exclude
另外做一個沒有要被忽略的檔案為對照組:
echo "Normal" > normal.txt
這時整體資料夾結構長這樣:
code/
├── .git/
│ ├── hooks/
│ ├── info/
│ │ └── exclude # 內文含"ignore_exclude.txt"
│ └── ...
│
├── .gitignore # 內文為"ignore_gitignore.txt"
├── ignore_exclude.txt # 內文為"Ignore in exclude"
├── ignore_gitignore.txt # 內文為"Ignore in .gitignore"
└── normal.txt # 內文為"Normal"
現在透過以下指令,把 code/ 所有檔案都加到預存區:
git add .
使用 git status 檢查,會發現 .gitignore 跟 normal.txt 被放到預存區、被 git 追蹤:
相較之下,被寫在 .git/info/exclude 的 ignore_exclude.txt 與被寫在 .gitignore 的 ignore_gitignore.txt 則沒有被追蹤。
後續再形成一個 commit:
git commit -m "Initial commit"
再輸入推到 GitHub:
git push -u <GitHub repo URL>
可發現只有 .gitignore 與 normal.txt 兩個剛剛在預存區的檔案有被推上去:
現在在另外一臺電腦(或者同一臺電腦 code/ 以外的地方)把這個 GitHub 倉儲拉下來:
git clone <GitHub repo URL>
註:使用 git clone 會讓新目錄名稱與 GitHub 上的倉儲相同,為 Day19,但我在本機端改名為 another_code/。
這時在新電腦或新目錄中,結構如下:
another_code/ # 從Day19改名為another_code
├── .git/
│ ├── hooks/
│ ├── info/
│ │ └── exclude # 內文「不」含"ignore_exclude.txt"
│ └── ...
│
├── .gitignore # 內文為"ignore_gitignore.txt"
└── normal.txt # 內文為"Normal"
跟原本的目錄相比,主要有兩個差別:
.git/info/exclude:不再有原本目錄寫在裡面的 ignore_exclude.txt。.gitignore,沒有被 git 追蹤的 ignore_exclude.txt 不在裡面。如果我們在 another_code/ 中,手動加上被放在 .gitignore 的檔案:
echo "Ignore in .gitignore again" > ignore_gitignore.txt
另加上原目錄中被放在 .git/info/exclude 的檔案:
echo "Ignore in exclude again" > ignore_exclude.txt
如果這時候再把所有檔案加進預存區:
git add .
會發生什麼狀況呢?
ignore_exclude.txt 居然被加進去了!
如果再下 commit:
git commit -m "Add ignore_exclude.txt"
接著推上 GitHub:
git push -u <repo URL>
這時候觀察 GitHub 倉儲:
ignore_exclude.txt 被推上去了!綜合實驗二、三可發現:在 another_code/ 裡,沒寫在 .gitignore 的 ignore_exclude.txt 是不會被忽略的。
那如果原目錄去 git pull 呢?
ignore_exclude.txt 有被拉下來,而且還蓋過了原目錄中 ignore_exclude.txt 的內容!
但如果在原目錄 code/ 又改了這份 ignore_exclude.txt 檔案:
echo "hello" > ignore_exclude.txt
然後再把全部檔案加到預存區:
git add .
這份被寫在 .git/info/exclude 的檔案會被加進去嗎?
咦?怎麼有?趕緊來看看 code/ 裡的 .git/info/exclude 到底是不是還有這份檔案?
居然有,可是怎麼會沒效呢?
這是因為不管是 .gitignore 或 .git/info/exclude 都只對「尚未被 git 追蹤的檔案」有效,當我們在原目錄 git pull 時,拉到的 ignore_exclude.txt 已經是被 git 追蹤的狀態,所以就算被寫在 .git/info/exclude,也還是有被持續追蹤的。
在下一篇文章中,我們將以這兩者中較為常用的 .gitginore 為例,探討到底什麼樣的情況會讓檔案被 git 追蹤,以及被追蹤後,要如何讓其被退追蹤。
由上述的實驗,我們可以比較出:
.gitignore 中的檔案,經 git push 與 git clone/pull 到其他目錄後,同名檔案依然可保持不被 git 追蹤。.git/info/exclude 中的檔案,只在本機不會被 git 追蹤,經 git push 與 git clone/pull 到其他目錄後,這些同名檔案就會被追蹤。這兩者都只對「尚未被 git 追蹤」的檔案有效,因此,如果某檔案於本機中有被放在 .git/info/exclude、但後來拉下來的 GitHub 倉儲也有同名檔案,表示該檔案已被 git 追蹤,拉到本機之後,即便 .git/info/exclude 仍寫著這檔案名稱,此檔案還是會繼續被追蹤。
同樣的道理在 .gitignore 也適用,我們將在下篇文章詳細探討。