惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

雷峰网
雷峰网
博客园 - 叶小钗
博客园_首页
阮一峰的网络日志
阮一峰的网络日志
D
Docker
J
Java Code Geeks
B
Blog
G
Google Developers Blog
小众软件
小众软件
博客园 - 聂微东
罗磊的独立博客
大猫的无限游戏
大猫的无限游戏
IT之家
IT之家
量子位
WordPress大学
WordPress大学
美团技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
宝玉的分享
宝玉的分享
腾讯CDC
Martin Fowler
Martin Fowler
V
Visual Studio Blog
D
DataBreaches.Net
Stack Overflow Blog
Stack Overflow Blog
C
Check Point Blog

gankudadiz的个人小站

今天试了下 Gemini 3.8 flash,被谷歌的工具线给整不会了 - gankudadiz blog 离职后的自由:带女儿开启人生第一次自驾游(一) - gankudadiz blog 理想主义程序员的告别与告白:看完《年会不能停2》后的职场十年感怀 - gankudadiz blog 网页字体:TTF、WOFF2 和子集化 - gankudadiz blog 一次异常上行排查:揪出一个后台抓包的服务 - gankudadiz blog Opencode Go套餐的API玩法 - gankudadiz blog 本地开发服务, 端口与网络访问基础 - gankudadiz blog PHP 与 Redis:从原理到日常维护 - gankudadiz blog 用 Claude Code 和 Mimo 翻译 Filament 文档的一点记录 2026年了,AI横行的现在,markdown还是文档的主流格式吗? - gankudadiz blog 坚果云同步目录理解错误记录 - gankudadiz blog GitHub Pages 的运作机制与最新规则 - gankudadiz blog 不是,没人和我说这么感人啊 - gankudadiz blog OpenCode 配置 Oh My OpenAgent(OMO) 记录 装了ccusage,总算知道 Claude Code 和 Codex 吃了多少 token --一切尽在 Everything - gankudadiz blog 女儿第一次国际象棋团队赛 - gankudadiz blog AI的积极意义---用GPT-image2制作女儿单词卡 - gankudadiz blog 这个时代要变了 GPT-image2使用感想 - gankudadiz blog 再见,大布 - gankudadiz blog 关于 MySQL 整数显示宽度的排查:为什么同一张表会出现 `bigint(20) unsigned` 和 `bigint unsigned` 做博客几个月,我终于了解了这些站长工具 - gankudadiz blog 博客的未来规划 - gankudadiz blog 给评论区加个草稿箱:sessionStorage 的标签页级缓存 - gankudadiz blog WSL2和Windows同步claude code配置记录 - gankudadiz blog WSL2 安装 Claude Code 全攻略 给博客加上异步邮件通知:Laravel Queue 实践小记 - gankudadiz blog 头像上传裁剪这件小事 - gankudadiz blog
Git 忽略文件的几种藏法 - gankudadiz blog
2026-09-16 · via gankudadiz的个人小站

今天整理一个项目的文档目录时, 顺手把一个 plans/ 目录藏了起来. 藏完才发现, 自己以前一直用的是最笨的那种写法, 而 git 其实给了三层机制, 只是我从没认真看过中间那一层.

事情的起点很具体: 这个目录是我和 agent 用的开发计划, 里面写满了本机的绝对路径和没做完的打算, 它永远不该进公开发布的那个仓库. 但 .gitignore 本身是要提交的, 一旦在里面写上 plans/, 等于对着所有看仓库的人说"这里有个 plans 目录, 只是我不想给你看".

最外层: 提交进仓库的 .gitignore

这是大家最熟的做法, 在仓库根目录的.gitignore写一条:

plans/

它能用, 但有个前提: 你得接受这条规则本身是公开信息. 对大多数项目这没关系, 毕竟 node_modules/ 也没什么好藏的. 可如果这个目录的存在本身就是"内部痕迹", 那这条写法就把你想藏的东西写在了门牌上.

藏在目录里面: 一个只有一行的 .gitignore

第二种是我以前常用的, 在 plans/ 里放一个 .gitignore:

*

这个写法的结果我第一次见的时候觉得挺神奇: git status什么都看不到, 连那个 .gitignore 自己都不出现. 目录在磁盘上明明存在, 有十几个文件, 但 git 对它像没看见一样.

为什么? 我原来以为是"忽略规则不作用于忽略文件本身"之类的特殊照顾, 其实不是. 真实原因是 *.gitignore 自己也匹配掉了 —— git 在决定"这个未跟踪文件要不要报给我看"时, 用的就是同一套忽略规则. 规则文件也是一个文件, 也会被自己的规则吃掉.

我用一个空仓库实测过, 对照很清楚. 目录里放一个 .gitignore 和一个 a.md, 让 .gitignore 内容为空:

?? plans/.gitignore
?? plans/a.md

.gitignore 内容改成 * 之后:

(空)

一个字符的差别, 目录就从"两个未跟踪文件"变成"不存在".

中间那一层: .git/info/exclude

上面两种都属于"仓库里的规则". 而这次藏 plans/ 我用的其实是第三种, 也是我这次才认真用上的机制: 在 .git/info/exclude 里写一行.

plans/

.git/ 目录本身不会被提交, 所以这个文件:

  • 随仓库生效, 效果和根目录 .gitignore 一模一样
  • 不上传, GitHub 上看不到, 别人 clone 你的仓库也拿不到
  • 工作区里零痕迹, 没有多出来的文件

这正好是"这个目录只在我这台机器上有意义、永远不该进仓库"的场景需要的. 对比一下藏目录的几种做法:

做法 效果 代价
.gitignoreplans/ 全仓库生效 规则公开, 等于声明这里有个目录
plans/.gitignore* 全仓库生效, 且自吞 多一个文件; 换机器要重建
plans/.gitignore/* 只藏一层 深层文件照样露出来
.git/info/exclude 全仓库生效, 不留痕迹 只对本机生效, clone 不到

还有一个更外层的: 全局的 core.excludesFile. 那个作用于你这台机器上的所有仓库, 我用得少, 因为过半年我就会忘了自己设过它, 然后对着一个"莫名其妙被忽略"的文件查半天.

我怎么选的

plans/.git/info/exclude. 理由是这份东西跟机器绑定, 不存在"别人 clone 下来也需要"的情况, 而它的存在本身又不适合写在公开规则里.

代价是它变得不好发现: 后来的人在仓库里翻不到任何线索, 光看 ls plans/ 也看不出这个目录被忽略了 —— 而这恰恰是"忽略"这个动作的题中之义, 有点自相矛盾. 所以我在项目的 AGENT.md 里补了一句说明, 写清楚哪些目录是由本地 exclude 排除的. 规则可以隐形, "规则在哪儿"这件事最好别隐形, 不然下一个接手的人(或者下一个 agent)会重新踩一遍.

一个小项目上的小决定, 顺手把 git 这一层机制搞清楚了. 以前我只会一种写法, 现在知道有三层, 而且知道最里面的那层在哪、什么时候该用它.