有次在项目里放了个 dist.zip,顺手在 .gitignore 里写了 .zip,接着写代码去了。过了几天提交时,发现 git status 里那个 dist.zip 一直躺着。我第一反应是规则没保存,反复确认后又怀疑 .gitignore 文件放错了目录,折腾了一轮才发现:.zip 和 *.zip 在 Git 的忽略规则里根本不是一回事。很多人(包括当时的我)会把 .zip 想当然地理解成“所有 zip 压缩包”,但 Git 没那么智能,它做的只是字符串模式匹配,差一个 *,行为就差得很远。
这个问题看起来小,实际是 .gitignore 初学者最容易踩的坑之一。因为你的“意图”和 Git 的“匹配规则”完全是两套东西:你脑子里想的是“忽略所有 zip 压缩包”,Git 脑子里只有“这个路径名跟我记的模式对不对得上”。搞清楚这一点,不只是能解决 .zip 的问题,.env、.idea、*.log 这一串相关写法你都会豁然开朗。今天就把这个坑从头到尾拆一遍。
1. 写了 .zip 却压不住:一次压缩包忽略失败的排查
1.1 问题现场:git status 里的压缩包一直删不掉
先还原一下场景。项目目录结构大概是这样的:
text复制project/
├── .gitignore
├── src/
└── dist.zip
.gitignore 里的内容是:
gitignore复制.zip
执行 git status,看到:
bash复制$ git status
Untracked files:
dist.zip
也就是说,dist.zip 依然没有被忽略。这时候我去检查 .gitignore 有没有生效,于是直接试一下规则本身:
bash复制$ git check-ignore -v .zip
.gitignore:1:.zip .zip
输出说明:.gitignore 第 1 行的 .zip 规则,确实匹配到了一个叫 .zip 的文件。可问题是,项目里没有哪个文件的名字是 .zip,我们真正想忽略的是 dist.zip。再测一下:
bash复制$ git check-ignore -v dist.zip
# 没有任何输出
没有输出,就是没有匹配上。规则写在文件里,但只对一个不存在的 .zip 生效,对真正的 dist.zip 视而不见。这就是问题本身。
1.2 用 git check-ignore 定位规则为何没生效
这里用到的排查命令是 git check-ignore -v <path>。它专门用来反问 Git:这个路径到底有没有被忽略?如果被忽略了,是哪条规则生效的;如果没有,它就不输出任何内容。
把模式改成 *.zip 之后再看:
bash复制$ cat .gitignore
*.zip
$ git check-ignore -v dist.zip
.gitignore:1:*.zip dist.zip
输出里明确告诉我们:*.zip 规则命中了 dist.zip。同时 git status 里也不会再出现 dist.zip。
所以排查这类问题,千万不要靠肉眼盯着 .gitignore 猜。直接拿 git check-ignore -v 验证要定位的文件,几秒钟就能判断是规则写法问题,还是文件本身不在规则覆盖范围内。
1.3 复盘:把 .zip 当成“所有压缩包”是惯性思维
为什么我会在第一反应里写 .zip?复盘下来有两个原因。
第一个原因,是把 Git 的匹配规则默认成了“后缀匹配”。很多人以为写 .zip 就等于“以 .zip 结尾的文件”,这其实是文本编辑器、文件管理器里面“按扩展名筛选”的习惯。Git 不在乎“扩展名”这个概念,它只认路径和 basename。
第二个原因,是正则表达式的惯性。在正则里,. 是通配符,能匹配任意一个字符,所以 .zip 可能被误解成“任意字符 + zip”。但 .gitignore 用的不是正则,而是 glob 模式,这里的 . 就是字面量的点号,没有通配能力。真正能表示“任意内容”的是 *。
把这两点拆开,你就能理解:.zip 在 glob 语义里就是一个普通字符串,它的含义是“名字恰好叫 .zip”,而不是“扩展名是 zip 的文件”。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. .zip 和 *.zip 在 Git 匹配规则里的真实差异
2.1 先理解 gitignore 的两种基本模式
Git 的 `.
