Skip to content

Git 全局 ignore 配置原理 ​

想让某类文件(如 AI 编码工具生成的 CONTEXT.md、.codegraph)不出现在任何仓库里,为每个 repo 的 .gitignore 各写一遍并不现实。Git 提供跨仓库生效的全局 ignore,但「全局」到底配在哪、core.excludesFile 与网上常说的 XDG 路径是什么关系,容易混淆。本文说明三个忽略来源的分工、全局 ignore 不配置时的默认行为、ignore 模式中的路径深度陷阱,以及用 git check-ignore 验证规则来源的方法。

gitignore 的三种来源 ​

Git 读取 ignore 模式共有三个来源,按「作用范围」和「是否共享」区分:

来源 作用范围 是否提交到仓库 典型用途
.gitignore 当前项目 是(团队共享) 项目约定的忽略文件
.git/info/exclude 当前仓库本地 否(仅本地) 个人的、不想写进项目的
core.excludesFile 全部仓库 否(个人全局) 跨所有 repo 的个人偏好

关键认知:三者是叠加关系,不是互斥。三个文件里的模式都会生效,文件只要命中其中任意一条就被忽略。

core.excludesFile 与 XDG 默认路径 ​

这里常被误解。core.excludesFile 不是「要么显式配置、要么不生效」——它是一个带默认值的配置项:

  • 显式配置:git config --global core.excludesFile ~/.gitignore_global,指向指定的文件。
  • 不配置时,Git 回退到 XDG 默认路径:$XDG_CONFIG_HOME/git/ignore;若 $XDG_CONFIG_HOME 未设置或为空,则使用 ~/.config/git/ignore。

也就是说,哪怕从未执行过 git config --global core.excludesFile,只要 ~/.config/git/ignore 这个文件存在,Git 就会自动把它当作全局 ignore 文件读取(Git 2.0+ 的行为)。

验证:未配置时 XDG 路径仍生效 ​

bash
$ git config --global core.excludesFile    # 查询,无输出
$ echo $?
1                                           # exit 1 = 未设置

# 但 check-ignore 证明 ~/.config/git/ignore 仍被读取
$ mkdir -p a/b && touch a/b/CONTEXT.md a/b/keep.txt
$ git check-ignore -v a/b/CONTEXT.md
/home/user/.config/git/ignore:5:CONTEXT.md    a/b/CONTEXT.md   # 命中全局文件第5行
$ git check-ignore -v a/b/keep.txt
[未命中]                                      # 符合预期

check-ignore -v 的输出格式是 来源文件:行号:模式 被检查的路径,是验证「某个文件被哪条规则忽略」的标准手段。

结论:如果已经在用 ~/.config/git/ignore,就不需要再执行 git config --global core.excludesFile。一旦显式配置了别的路径,XDG 默认路径就不再被读取(配置覆盖默认)。

三种来源叠加生效 ​

把同一个 repo 里分别由三个来源忽略的文件混在一起,check-ignore -v 会清晰显示各自命中的来源:

bash
# 准备:全局、仓库本地、.gitignore 各忽略一个,再留一个不忽略的
# ~/.config/git/ignore    -> global.bin
# .git/info/exclude       -> local.txt / proj/repo.txt
# .gitignore              -> shared.log

$ for f in global.bin local.txt proj/repo.txt shared.log keep.md; do
    git check-ignore -v "$f" || echo "  [未忽略]"
  done
/home/user/.config/git/ignore:6:global.bin    global.bin       # 来自全局
.git/info/exclude:1:local.txt                  local.txt        # 来自仓库本地
.git/info/exclude:2:proj/repo.txt              proj/repo.txt    # 来自仓库本地
.gitignore:1:shared.log                        shared.log       # 来自项目 .gitignore
keep.md                                        [未忽略]

三个来源各司其职、互不干扰,都会生效。

匹配语法:路径深度是个坑 ​

往全局 ignore 里添加模式时,前缀写法直接决定匹配深度。以忽略各级目录下的 CONTEXT.md 为例:

写法 匹配范围 示例命中
CONTEXT.md 任意层级(推荐) CONTEXT.md、a/b/CONTEXT.md 全命中
*/CONTEXT.md 仅一层深 one/CONTEXT.md 命中;a/b/CONTEXT.md 不命中
**/CONTEXT.md 任意层级(等价无前缀) 同 CONTEXT.md

验证 */ 只匹配一层的问题:

bash
$ printf '*/CONTEXT.md\n' > /tmp/star-ignore
$ git -c core.excludesFile=/tmp/star-ignore check-ignore -v one/CONTEXT.md
/tmp/star-ignore:1:*/CONTEXT.md    one/CONTEXT.md          # 一层深:命中
$ git -c core.excludesFile=/tmp/star-ignore check-ignore -v two/three/CONTEXT.md
[未命中]                                                   # 两层深:漏掉

$ printf 'CONTEXT.md\n' > /tmp/plain-ignore
$ git -c core.excludesFile=/tmp/plain-ignore check-ignore -v two/three/CONTEXT.md
/tmp/plain-ignore:1:CONTEXT.md     two/three/CONTEXT.md    # 无前缀:任意深度都命中

原理:模式里只要含斜杠(不在末尾),就锚定到 ignore 文件所在目录的相对层级,而 * 不匹配斜杠;不含斜杠的裸文件名才会「在任意层级匹配」。所以要忽略各级同名文件,直接写裸名即可,不要写 */CONTEXT.md——它只覆盖一层深度的目录。

已跟踪文件不受 ignore 影响 ​

ignore 规则只对未跟踪的文件生效。如果一个 CONTEXT.md 之前已被 git add 跟踪过,加进 ignore 后它的改动照常显示。要让它真正脱离跟踪:

bash
git rm --cached <path/to/CONTEXT.md>   # 从索引移除,保留本地文件
git commit -m "stop tracking CONTEXT.md"

要点 ​

  • 全局 ignore 有三个层次:.gitignore(项目共享)、.git/info/exclude(仓库本地)、core.excludesFile(跨仓库个人),三者叠加生效。
  • core.excludesFile 不显式配置时,默认读 XDG 路径 ~/.config/git/ignore——文件存在就生效,无需额外配置。
  • 验证忽略来源用 git check-ignore -v <path>,输出会指明命中的是哪个文件的哪一行。
  • 忽略各级同名文件写裸名(CONTEXT.md),不要写 */CONTEXT.md,后者只匹配一层深。
  • ignore 不影响已跟踪文件,需要 git rm --cached 才能停止跟踪。

更多 Git 常用操作与历史整理,见 Git 常用命令速查。

最近更新

基于 VitePress 构建