你不知道的.gitignore
0. 幾個概念
- 被gitignore規則命中,即被gitignore規則匹配到的檔案或目錄,不會被Git追蹤,即不會被Git給track
1. 簡介
- .gitignore,是用來顯式指定哪些檔案或資料夾應該被Git忽略的一個檔案
$HOME/.gitignore_global
,$HOME/.config/git/ignore
,$GIT_DIR/info/exclude
,.gitignore
這些地方指定的ignore規則都會在Git倉庫中生效
2. 描述
一個.gitignore
檔案顯式地指定了哪些檔案不應被Git追蹤,即被Git忽略掉。在被gitignore之前已經被Git追蹤的檔案不受gitignore規則的影響。關於gitignore規則的詳情請繼續往下看。
.gitignore
檔案中的每一行都指定了一種匹配模式。通常來說,Git會從多個可能的規則源獲取gitignore
規則來決定Git是否要忽略某一個具體的路徑path,如下按照優先順序列出了各種規則源,越靠前的規則優先順序越高(在一個規則源內部,如果有多個gitignore
匹配,以最後匹配的為準)
- 從命令列輸入的規則
- 該路徑下的
.gitignore
檔案,或者父級目錄的.gitignore
中定義的規則。其中,越靠近具體路徑的.gitignore
檔案的優先順序越高,同目錄下的.gitignore
檔案優先順序最高。專案倉庫中通常都有.gitignore
檔案,裡面會包含忽略專案build自動生成的檔案的規則 $GIT_DIR/info/exclude
中定義的規則- Git配置變數
core.excludeFile
指定的規則
具體講gitignore規則定義在哪個檔案中取決於該規則的作用(域):
- 如果一個
gitignore
規則應該被Git追蹤,別人clone倉庫後規則也生效,那麼它就應該被定義在.gitignore
檔案中 - 如果某個規則只想在某一個特定的倉庫中生效,就把它定義在
$GIT_DIR/info/exclude
中吧 - 如果你的
gitignore
規則要在任何情況下都生效,這種規則最好放在core.excludesFile
這一變數中,這個變數定義在使用者目錄下-~/.gitconfig
,該變數的預設值是$XDG_CONFIG_HOME/git/ignore
,如果$XDG_CONFIG_HOME
是空的,Git會使用$HOME/.config/git/ignore
Git的底層管道工具,比如git ls-files
和git read-tree
,只從命令列引數||命令列引數指定的檔案中讀取gitignore
規則。上層的Git工具,比如git status
和git add
,會從上述規則源中讀取gitignore
規則
3. gitignore規則
- 空行不匹配任何檔案,所以可以用空行來增強
gitignore
規則的可讀性 - 註釋行以
#
開頭。可以在#
前加一個反斜槓轉義之,使之能夠匹配包含#字元
的資料夾或檔案 - 如果每一行最後尾隨的空格沒有用反斜槓轉義,那麼這些空格是無效的,不會作為規則的一部分
- 使用
!
字首來否定之前的規則。如果一個檔案被前面的gitignore
規則給匹配到了,那麼該檔案不會被Git追蹤,但是如果後面的規則使用!
匹配到了該檔案,那麼該檔案又會被Git追蹤。當然,如果一個檔案的父目錄都被Git忽略了,那麼無論如何,這個檔案都不會被Git追蹤。出於效能考慮,Git不會遍歷被忽略的目錄,因此,定義在被忽略目錄下的gitignore
規則都是無效的。有時候,我們真的是想忽略以感嘆號!
開頭的一個檔案或者目錄,這時,可以在感嘆號!
前面加一個反斜槓轉義之,比如:\!important.txt
會匹配檔案!important.txt
- 如果一個規則以斜槓結尾,在實際匹配的時候,最後的斜槓會被移除掉,但是這個規則只會匹配目錄,而不會匹配檔案。換句話說,
foo/
會匹配到目錄foo
和foo
下的子目錄,但不會匹配到檔案foo
或者軟連結foo
- 如果規則中不包含斜槓
/
,Git就會就會把該規則當成萬用字元規則來進行處理,從該規則所在.gitignore
檔案所在路徑開始匹配。當然,如果這個規則不是放在.gitignore
檔案中的,就會從work tree
的頂部開始匹配 - 如果規則不符合以上的情況,那麼Git就會把這個規則當成shell萬用字元規則來進行解析,是以帶
FNM_PATHNAME
標記的fnmatch(3)規則進行解析。但是,規則中的萬用字元不會匹配路徑名中的斜槓/
。舉個栗子,Documentation/*.html
匹配Documentation/git.html
,但不會匹配Documentation/ppc/ppc.html
或者tools/perf/Documentation/perf.html
- 以斜槓開頭的萬用字元規則從路徑開頭開始匹配。比如,
/*.c
匹配cat-file.c
,但不匹配mozilla-sha1/sha1.c
兩個連續的星號**
在匹配全路徑名的時候可能有特殊含義:
- 規則以兩個星號
**
開頭,後接一個斜槓,這樣的規則會在所有路徑或子路徑中嘗試進行匹配。比如,**/foo
會匹配到檔案foo
或者目錄foo
,無論它在哪個目錄;foo
這條規則同樣會嘗試匹配所有路徑中的檔案foo
或者目錄foo
。**/foo/bar
規則會匹配任意檔案或目錄foo
下直接跟的檔案bar
或目錄bar
- 如果規則中間有連續的兩個星號
**
,那這條規則會匹配下面的所有東西。比如abc/**
會匹配目錄abc
下的所有檔案或目錄,當然,這裡的目錄abc
是相對於.gitignore
檔案位置而言的,無限遞迴 - 如果規則是
斜槓/
後跟兩個星號,然後再跟一個斜槓的形式,這裡的兩個星號就會匹配0+
個目錄,這裡的0+
是指可以沒有,也可以是多個。再舉個例子,比如a/**/b
會匹配a/b
、a/x/b
、a/x/y/b
這些 - 其他形式的連續星號都認為是非法的
4. 筆記
gitignore檔案的目的是確保某些不應該被Git追蹤的檔案確實沒有被track。如果要停止track一個已經被Git追蹤的檔案,請使用git rm --cached Xxx
命令
5. 示例
$ git status
[...]
# 暫未被Git追蹤的檔案:
[...]
# Documentation/foo.html
# Documentation/gitignore.html
# file.o
# lib.a
# src/internal.o
[...]
$ cat .git/info/exclude
# 忽略,即不再追蹤倉庫中所有的objects和壓縮檔案.
*.[oa]
$ cat Documentation/.gitignore
# 忽略自動生成的html檔案,
*.html
# 排除手動維護的foo.html,即不忽略foo.html,即Git會追蹤foo.html
!foo.html
$ git status
[...]
# 暫未被Git追蹤的檔案:
[...]
# Documentation/foo.html
[...]
複製程式碼
一個例子不夠,再來一個:
$ cat .gitignore
vmlinux*
$ ls arch/foo/kernel/vm*
arch/foo/kernel/vmlinux.lds.S
$ echo '!/vmlinux*' > arch/foo/kernel/.gitignore
複製程式碼
在這個例子中,第二個.gitignore檔案arch/foo/kernel/.gitignore
的優先順序更高,它阻止了第一個.gitignore檔案試圖忽略arch/foo/kernel/vmlinux.lds.S
的行為,從而,Git不會忽略arch/foo/kernel/vmlinux.lds.S
,會嘗試追蹤它
最後再來一個例子收尾。舉個忽略目錄下所有檔案或目錄,除了某個特定的目錄foo/bar
的例子吧(注意下面的/*
,就算沒有前面的斜槓,萬用字元也會匹配包括foo/bar
在內的所有檔案或目錄,所以斜槓/
是可有可無的):
$ cat .gitignore
# exclude everything except directory foo/bar
/*
!/foo
/foo/*
!/foo/bar
複製程式碼
6. 補充示例
譯文到此完畢,我再補充幾個小例子吧