跳转至

Gitignore 与 Hooks

告诉 Git 哪些文件别进仓库,以及在 commit / push 前自动跑检查。命令细节见 Git 命令集(如 git rm --cached)。

相关文档Git 命令集 · 提交规范 · 协作流程


阅读指引

  1. 概念.gitignore 是什么 — 先建立直觉
  2. 总览:规则、命令、Hook 类型表,了解本篇能查什么
  3. 场景:按实际问题深入,含完整示例
  4. 文末速查:现象 → 动作,应急用

.gitignore 是什么

.gitignore 是仓库里的纯文本规则文件。匹配到的路径不会被 git add 默认纳入,也不会出现在 git status 的 untracked 里(除非已经被跟踪过)。

一句话:「这份名单上的文件/目录,Git 当没看见。」

效果:只影响 未跟踪取消跟踪后 的文件;**不会**自动把已经 commit 过的文件从仓库删掉。


总览

.gitignore 规则速查

规则 / 写法 一句话 场景
foo/ 忽略目录 规则写法
*.log 任意层级匹配后缀 规则写法
/build 仅仓库根目录 规则写法
**/tmp 任意深度路径 规则写法
!path 从忽略中挖回 规则写法目录留一文件
# 注释 规则写法
子目录 .gitignore 与父级规则叠加 monorepo 子目录
core.excludesfile 本机全局忽略 全局忽略

本篇相关 Git 命令

命令 / 配置 干什么 场景 命令集
git check-ignore -v <path> 看哪条规则命中 子目录
git rm --cached 取消跟踪,文件留磁盘 已跟踪 git rm
git rm -r --cached 取消跟踪整个目录 已跟踪 git rm
git restore --staged 从暂存区撤下 误 add 密钥 git restore
git config --global core.excludesfile 设全局 ignore 文件 全局忽略 git config
git config core.hooksPath 用仓库内 hooks 目录 团队共享 git config
git commit --no-verify 跳过 commit 相关 hook 跳过 hook git commit
git push --no-verify 跳过 push 相关 hook 跳过 hook git push
chmod +x .git/hooks/* 让 hook 脚本可执行 pre-commit

Hooks 类型速查

Hook 触发时机 典型用途 场景
pre-commit git commit lint、格式化、禁大文件 pre-commit
commit-msg 提交说明确定后 校验 message、拦 trailer commit-msg
pre-push git push 单元测试、分支检查 pre-push
post-checkout 切换分支后 装依赖、清缓存
post-merge merge 完成后 npm install、迁移提示

完整列表:git-scm hooks 文档

团队 Hook 工具

方式 一句话 场景
core.hooksPath .githooks/ 纳入版本库 团队共享
Husky / Lefthook npm install 时自动挂 hook 团队共享
CI / PR 检查 --no-verify 漏网 团队共享

场景:新项目写第一份 .gitignore

常见场景

  • Node / 前端:依赖、dist、构建缓存
  • Python:虚拟环境、__pycache__
  • 编辑器:.idea.vscode(有时保留部分推荐配置)
  • 系统垃圾:.DS_Store
# 依赖与构建产物
node_modules/
dist/
.next/
out/

# Python
.venv/
**/__pycache__/

# 日志
*.log

# 编辑器(保留团队共享的 VS Code 扩展推荐)
.vscode/*
!.vscode/extensions.json
.idea/

# macOS
.DS_Store

Note

规则写进 .gitignore先 commit 这个文件本身,队友 clone 下来才有一致的忽略策略。


场景:规则怎么写才生效

写法 含义 示例
foo/ 忽略名为 foo 的目录 node_modules/
*.log 任意目录下后缀为 .log 的文件 npm-debug.log
/build **仅仓库根目录**下的 build 不匹配 src/build
**/tmp 任意层级下的 tmp 目录或文件 a/b/tmp
!path 否定:前面忽略了,但这条要保留 见下节
# 注释
# 所有 .env 本地覆盖(示例:团队用 .env.example 进仓库)
.env
.env.local
.env.*.local

# 但保留示例模板
!.env.example

一句话:「* 管后缀,/ 管是不是只限根目录,! 是从忽略里挖回来。」


场景:子目录有自己的 .gitignore

monorepo 里常见:**根目录**管全局,**子包**管自己的产物。

repo/
├── .gitignore          # node_modules、.DS_Store
├── apps/web/.gitignore # apps/web/.next
└── packages/api/.gitignore

效果:子目录规则与父级 叠加;更深层、更具体的规则优先。冲突时用 git check-ignore -v path哪一条规则 命中。

# 看某个文件为什么被忽略(或为什么没忽略)
git check-ignore -v apps/web/.next/cache/foo

场景:全局忽略(所有仓库生效)

本机所有项目都不想看到 .DS_Store,可以设 全局 gitignore

# 指定全局忽略文件路径
git config --global core.excludesfile ~/.config/git/ignore

# 编辑 ~/.config/git/ignore,写入:
# .DS_Store
# *.swp

一句话:「个人机器上的垃圾文件,别每个仓库写一遍。」

效果:只影响 你这台电脑 的 Git 行为;文件不会进任何仓库(除非已被跟踪)。


场景:.gitignore 写了但不生效 —— 文件已经被跟踪

.gitignore **不能**取消已经 commit 过的跟踪。典型踩坑:先把 dist/ 提交进仓库,后来才加 .gitignore

一句话:「文件已经在 Git 账上了,光写 ignore 不够,要从索引里摘掉。」

效果git rm --cached 只动 暂存索引,磁盘文件还在;下次 commit 后远程也不再跟踪该路径。

# 单个文件:停止跟踪,本地文件保留
git rm --cached config.local.json

# 整个目录
git rm -r --cached dist/

# 确认 status 里是 deleted(索引)但工作区文件还在
git status

git commit -m "chore: stop tracking dist and local config"

Warning

git rm --cached 之后若队友 pull,他们本地 已跟踪的副本 可能被删(取决于 merge 结果)。团队应先沟通,必要时在 PR 说明「停止跟踪某目录」。


场景:误把密钥、大文件 add 了

常见场景

  • .env 含 API Key 被 stage
  • git add .node_modules 或几 GB 数据加进暂存区
# 先从暂存区撤下(改动还在工作区)
git restore --staged .env

# 立刻写进 .gitignore
echo ".env" >> .gitignore

# 若已经 commit 但未 push:改 commit 或 reset(见 git-commands 的 reset / amend)
# 若已经 push:rotate 密钥 + 历史清理(BFG / filter-repo),别只删文件当没事

Warning

密钥一旦 push 到远程,视为 已泄露。轮换密钥比「删 commit 假装没发生」更重要。


场景:想忽略目录里某一个文件

# 忽略整个 logs 目录
logs/

# 但保留 logs/.gitkeep 让空目录能进仓库
!logs/
!logs/.gitkeep

一句话:「目录全忽略,再用 ! 把要留的那一两个文件捞回来。」


场景:monorepo 里 .gitignore 怎么写

参考常见 Node + Turbo monorepo(与许多开源模板类似):

node_modules/
dist/
.next/
.turbo/
*.tsbuildinfo

# 编译产物别进 src
**/src/**/*.js
**/src/**/*.js.map
# 白名单:手写或配置类 JS 保留
!**/src/**/*.config.js

# Python 文档站 venv
.venv/

# 打包输出目录
releases/

常见场景

  • pnpm install 后不想把 .pnpm-store 误提交 → 根 .gitignore.pnpm-store
  • 某包要 跟踪 生成物例外 → 子目录用 !packages/foo/generated/ 精细控制

Git hooks 是什么

Hooks 是 Git 在特定动作 之前或之后 自动执行的脚本,放在 .git/hooks/(文件名如 pre-commitcommit-msg无扩展名,且需 chmod +x)。

一句话:「commit / push 的自动化门卫。」

效果:脚本返回 非 0 退出码 → Git 中止 当前操作(commit 失败、push 被取消等)。类型一览见上文 Hooks 类型速查


场景:commit 前自动格式化 / lint(pre-commit)

常见场景

  • 提交前跑 ESLint、Prettier
  • 禁止带 console.log 或调试断点进仓库
# .git/hooks/pre-commit(示例,路径在本地 .git 内)
#!/bin/sh
set -e

# 只检查已 staged 的 JS/TS(需已安装 lint-staged 或手写 git diff)
npm run lint --if-present

# 或简单:全仓类型检查
# pnpm exec tsc --noEmit
chmod +x .git/hooks/pre-commit

触发时机:执行 git commit 时,写入 commit 对象之前

团队常用 HuskyLefthook 把 hook 脚本 放进仓库 并在 npm install 时链接到 .git/hooks,避免每人手写。


场景:校验 commit 说明(commit-msg)

常见场景

  • 必须符合 提交规范(如 feat:fix: 开头)
  • 拦截 Cursor 等工具自动注入的 Co-authored-by: Cursor ... trailer
# .git/hooks/commit-msg
#!/bin/sh
MSG_FILE="$1"
MSG=$(cat "$MSG_FILE")

# 禁止 Cursor 署名 trailer(示例)
echo "$MSG" | grep -q 'Co-authored-by: Cursor' && {
  echo "error: Cursor Co-authored-by trailer is not allowed"
  exit 1
}

# 简单格式:首行非空
head -1 "$MSG_FILE" | grep -q . || {
  echo "error: empty commit subject"
  exit 1
}

exit 0
chmod +x .git/hooks/commit-msg

触发时机:编辑器写好 message 之后、commit 最终落盘之前

Note

项目若提供 scripts/git-hooks/install.sh(或 .ps1),通常会把上述脚本 复制或链接.git/hooks;clone 新仓库后记得跑一次安装。


场景:push 前跑测试(pre-push)

常见场景

  • 禁止把 failing tests 推上共享分支
  • 推送前快速 smoke test
# .git/hooks/pre-push
#!/bin/sh
set -e
pnpm test --if-present
# 或只跑受影响包:pnpm exec turbo run test --filter=...

触发时机git push 向远程 发送对象之前


场景:hook 失败了,能不能跳过?

# 跳过 pre-commit、commit-msg 等(慎用)
git commit --no-verify -m "hotfix: emergency patch"

git push --no-verify

一句话:「门卫不让进,你硬闯 --no-verify。」

常见场景

  • 本地 WIP 临时保存(仍不建议在共享分支常用)
  • hook 误报,紧急 hotfix 需先 push

Warning

共享分支、CI 前的正式提交 不要 习惯性 --no-verify。跳过 hook 等于跳过团队约定;若 hook 有问题应修 hook 或提 issue。


场景:团队共享 hooks(不只靠每人手写)

.git/hooks 默认 不进 clone,所以团队常用:

  1. core.hooksPath:指向仓库内可版本化的目录
    git config core.hooksPath .githooks
    chmod +x .githooks/*
    
  2. Husky / Lefthook:在 package.json 里声明,安装依赖时注册 hook
  3. CI 兜底:hook 是本地第一道关;PR 上仍跑 lint/test,防止有人 --no-verify
repo/
├── .githooks/
│   ├── pre-commit
│   └── commit-msg
├── .gitignore          # 不要忽略 .githooks
└── package.json        # husky 配置(若使用)

速查:问题 → 动作

现象 先做什么
新文件不该进仓库 .gitignore → 确认 git status 干净
写了 ignore 仍显示 modified 文件已被跟踪 → git rm --cached
不知道哪条规则命中 git check-ignore -v <path>
commit 被 hook 拦住 看终端报错修代码;确需跳过才 --no-verify
队友没有 hook 文档写安装步骤;CI 再拦一层
密钥已 push 轮换密钥 + 历史清理,见安全流程

更多命令:git add / git restore / git commitGit 命令集