你不知道的.gitignore

小拓先森發表於2018-02-06

你不知道的.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-filesgit read-tree,只從命令列引數||命令列引數指定的檔案中讀取gitignore規則。上層的Git工具,比如git statusgit add,會從上述規則源中讀取gitignore規則

3. gitignore規則

  • 空行不匹配任何檔案,所以可以用空行來增強gitignore規則的可讀性
  • 註釋行以#開頭。可以在#前加一個反斜槓轉義之,使之能夠匹配包含#字元的資料夾或檔案
  • 如果每一行最後尾隨的空格沒有用反斜槓轉義,那麼這些空格是無效的,不會作為規則的一部分
  • 使用!字首來否定之前的規則。如果一個檔案被前面的gitignore規則給匹配到了,那麼該檔案不會被Git追蹤,但是如果後面的規則使用!匹配到了該檔案,那麼該檔案又會被Git追蹤。當然,如果一個檔案的父目錄都被Git忽略了,那麼無論如何,這個檔案都不會被Git追蹤。出於效能考慮,Git不會遍歷被忽略的目錄,因此,定義在被忽略目錄下的gitignore規則都是無效的。有時候,我們真的是想忽略以感嘆號!開頭的一個檔案或者目錄,這時,可以在感嘆號!前面加一個反斜槓轉義之,比如:\!important.txt會匹配檔案!important.txt
  • 如果一個規則以斜槓結尾,在實際匹配的時候,最後的斜槓會被移除掉,但是這個規則只會匹配目錄,而不會匹配檔案。換句話說,foo/會匹配到目錄foofoo下的子目錄,但不會匹配到檔案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/ba/x/ba/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. 補充示例

譯文到此完畢,我再補充幾個小例子吧

6.1 示例一

6.2 示例二

6.3 示例三

相關文章