Git 工程实践:本地门禁、CI 流水线与 PR 合并管控 📅 2026/7/1 6:35:16 一、先搞清楚三个目录很多人刚接触 Git 工程化时会被这三个长得像的目录搞混demo/├── .git/ ← git 本地数据库├── .githooks/ ← 自定义 hook 脚本存放处└── .github/└── workflows/└── ci.yml ← GitHub CI/CD 配置它们是三个完全不同的东西。1.1.git/— 本地 Git 数据库项目说明管什么本地仓库的一切commit 历史、分支、暂存区存在哪只在你自己电脑上会上传吗不会git 天然排除它.git/和远程仓库没有直接关系它只是本地的「存档记录本」。GitHub 的地址只是里面config文件里的一行配置告诉git push往哪里推。1.2.githooks/— 自定义 hook 脚本存放处项目说明管什么git 操作前后自动执行的脚本存在哪项目目录会进版本控制会上传吗会但上传后只是普通文件需手动安装才生效关键问题为什么不直接放到.git/hooks/里因为.git/是本地私有目录不会被git add追踪也不会推到 GitHub。新克隆仓库的人.git/hooks/里什么都没有。解决方案项目里放 .githooks/进版本控制所有人克隆后都有↓每人克隆后执行一次 make setup↓脚本复制到 .git/hooks/git 才真正识别并触发1.3.github/workflows/— GitHub CI/CD 配置项目说明管什么GitHub Actions 流水线配置存在哪项目目录会进版本控制会上传吗会push 后 GitHub 自动读取并执行.github/这个目录名是 GitHub 平台规定的推上去后 GitHub 会自动识别workflows/里所有.yml文件并运行流水线。GitLab 对应的是.gitlab-ci.yml各平台规则不同。1.4 三者关系一张图一句话记忆.git/— 本地 Git 的「大脑」不上传.githooks/— 自定义 hook 存放处上传但需手动安装.github/workflows/— GitHub 的 CI 配置上传后自动生效二、两套门禁各司其职整个质量保障体系由两套独立的门禁组成运行在不同地方.githooks/pre-commit → 运行在你的电脑上本地门禁.github/workflows/ → 运行在 GitHub 服务器上远程门禁它们互相独立不联动但保护同一个目标让有问题的代码进不了main分支。为什么要两套不是重复了吗.githooks/本地.github/workflows/远程运行时机git commit之前push/ PR 之后谁能绕过可以用--no-verify跳过绕不过GitHub 强制执行速度秒级分钟级环境你自己的电脑干净的标准化虚拟机核心作用在问题离开电脑前就拦住最终强制兜底.githooks/ 你家门口的门禁自愿装可以翻墙.github/workflows/ 小区大门的门禁必须过没有例外本地 hook 是方便你自己提前发现问题不用等 CI 报错再改。远程 CI 是团队强制执行任何人都绕不过去。三、完整的三个触发时机准确说是三个时机两套系统【时机 1】git commit ↓ .git/hooks/pre-commit 触发本地 hook不叫 CI 编译检查 单元测试 密钥扫描 ❌ 失败 → commit 被拒代码还在你电脑GitHub 完全不知道 ✅ 通过 → commit 写入本地 .git/ ↓ 【时机 2】git push ↓ 代码到达 GitHub ci.yml 触发push 到 feature/* 分支时→ 叫CI ❌ 失败 → 分支页面标红但还没到 PR不卡合并 ✅ 通过 → CI 绿 ↓ 【时机 3】开 Pull Request ↓ ci.yml 再次触发PR 到 main/develop 时→ 叫CI / PR 门禁 ❌ 失败 → Merge 按钮变灰无法合入 ✅ 通过 → CI 全绿等待人工 Approve 后才能 Merge时机 3 是最关键的 — 只有这次 CI 全绿 人工 Approve才能把代码合进main。时机 1 和 2 更多是「提前发现问题、减少等待」。时机 2 和 3 用的是同一个ci.yml由on:触发条件区分on: push: branches: [feature/*, fix/*] # 时机 2push 到功能分支时跑 pull_request: branches: [main, develop] # 时机 3PR 到主干时跑这次才卡 Merge四、本地门禁详解.githooks/pre-commit#!/bin/bash set -e # 任何一步失败立刻停止commit 被拒绝 # 第一关编译检查 gcc -Wall -Wextra -stdc11 -lm calculator.c -o /tmp/calc_check # 第二关单元测试 make test # 第三关硬编码密钥扫描 if grep -rn password\s*\|api_key\s*\|secret\s* -- *.c 2/dev/null; then echo ERROR: hardcoded credential detected, commit blocked. exit 1 fi echo All checks passed. Proceeding with commit.触发效果三关全过 → commit 正常 [pre-commit] Step 1: Compiling... OK [pre-commit] Step 2: Running tests... All Tests Passed OK [pre-commit] Step 3: Scanning... OK [main abc1234] feat: add power operator第二关失败 → commit 被拒 [pre-commit] Step 2: Running unit tests... test_calculator: Assertion fabs((power(2,10)) - (1024)) 1e-9 failed. make: *** [test] Error 1 # commit 被阻止必须先修好测试安装方式克隆仓库后执行一次make setup # 等价于 # cp .githooks/pre-commit .git/hooks/pre-commit # chmod x .git/hooks/pre-commit五、远程 CI 流水线详解.github/workflows/ci.yml四个 job 串行执行前一个失败后面全部跳过build ──→ unit-test ──→ sast-scan ──→ deploy仅 main 分支jobs: build: # 编译检查产物上传供后续 job 使用 unit-test: needs: build # build 成功后才启动 sast-scan: needs: unit-test # 用 cppcheck 做静态安全扫描 deploy: needs: [build, unit-test, sast-scan] if: github.ref refs/heads/main # 只在 main 分支部署PR 页面上你会看到Checks✅ build — 编译成功✅ unit-test — 单元测试通过✅ sast-scan — cppcheck 无报错❌ unit-test — 覆盖率不足 ← Merge 按钮变灰六、Pull Request 是什么为什么必须走 PRPRPull Request 就是向主分支提交合并申请。你在feature/add-power分支写完代码不能直接改main而是发一个申请「我在功能分支写了幂运算请审查后合并进main。」为什么不直接 push 到 main方式风险直接 pushmain危险没有 Review没有 CI代码直接进主干出问题影响所有人走 PR安全CI 自动检查 同事人工 Review问题在合入前暴露main分支在 GitHub 上设为「保护分支」后强制只能通过 PR 合入直接 push 会被拒绝。完整 PR 流程# 1. 创建功能分支 git checkout -b feature/add-power # 2. 写代码、commit git commit -m feat: add power operator # 3. 推送分支到 GitHub git push -u origin feature/add-power # 4. GitHub 网页 → Compare pull request → Create Pull Request # CI 自动触发同事收到 Review 通知 # 5. CI 全绿 同事 Approve → 点 Merge 合入 main七、从本地到上线的完整链路你写代码本地 ↓ git commit ↓ pre-commit hook 触发本地门禁 编译 ✅ 测试 ✅ 密钥扫描 ✅ ↓ git push origin feature/add-power ↓ GitHub CI 触发时机 2 build ✅ unit-test ✅ sast-scan ✅ ↓ 开 Pull Request ↓ GitHub CI 再次触发时机 3最重要 所有 job ✅ ↓ 同事 Code Review → Approve ✅ ↓ 点 Merge → 代码合入 main ↓ deploy job 自动触发仅 main ↓ 上线