提交规范与 .gitignore
养成规范提交(feat/fix/refactor 前缀)与忽略编译产物的习惯
- 来源
- 补充
标记说明:【来源】来自上传资料 · 【补充】课程新编 · 【纠错】按勘误表修正 · 【更新】过时内容已现代化 · 【待确认】无法可靠还原
完成标准(本章)
- 📖 已阅读:滚动 ≥ 80% 且有效阅读 ≥ 120 秒
- ✏️ 已练习:小练习正确率 ≥ 60%
- 📝 已通过测验:分数 ≥ 60 分
- 🛠️ 已掌握还需完成实践任务
0.3.5 提交规范与 .gitignore
本章来源:提交规范、分支管理模型与 tag 概念来自《第一阶段讲义》1.6/1.7 节【来源】,经重新组织表述;C/C++ .gitignore 模板、core.autocrlf 说明、本机实验与全部练习为新编【补充】。平台适用性 universal。
① 学习目标
- 按
type: 说明格式写出规范的提交信息(feat/fix/docs/refactor 等前缀); - 说出 main/dev/feature/hotfix 分支模型里各分支的职责;
- 会用轻量与附注两种方式打 tag,并说出 tag 与分支的区别;
- 为 C/C++ 项目编写 .gitignore,忽略
*.o/*.exe/build/等编译产物; - 理解 core.autocrlf 换行符配置的意义(Windows/Linux 协作)。
② 前置知识
- 必需:0.3.4 远程仓库 Gitee(规范提交是为了让协作方看懂你的历史);
- 建议:0.3.1-0.3.3(init/add/commit、分支与合并);本章实验只需 gcc 与本机 git。
③ 核心概念【来源】
| 概念 | 说明 |
|---|---|
| 提交规范 | type: 一句话说明:feat(新功能)、fix(修复缺陷)、docs(文档)、refactor(重构,不改行为)、test(测试)、style(格式)、chore(杂项);如 feat: 添加环境检测脚本 |
| message 三问 | 写清楚为什么改、改了什么、是否影响行为;第一行保持简短(50 字内),细节放正文 |
| 分支模型 | main(稳定发布)、dev(集成开发)、feature/(新功能,合并回 dev)、hotfix/(紧急修复,合并回 main 与 dev);这是团队约定,不是 git 强制 |
| tag | 给某个提交打不会移动的标签:git tag v0.1(轻量);git tag -a v0.1 -m "说明"(附注,额外记录打标人/时间/说明);用于标记发布版本 |
| tag 与分支 | 分支是"会前进的指针"(新提交自动跟随);tag 是"钉在历史上的图钉"(永远指向打标那次提交) |
| .gitignore | 让 git 忽略不需要版本化的文件;语法要点:*.o 通配、build/ 结尾斜杠表示目录、! 否定、/ 开头锚定根目录 |
| core.autocrlf | Windows 换行符是 CRLF、Linux/Mac 是 LF;该配置控制仓库内外的换行符转换,避免"整个文件都显示被修改"的噪音 |
④ 通俗解释【补充】
- 提交信息 = 快递面单:feat/fix 是"货物类别",一句话说明是"里面装了什么"。写"改了一下"等于面单上只写"东西"两个字——一个月后没人知道这箱货是什么;
- 分支 = 并行生产线:main 是货架上的稳定版,feature/* 是试验车间,hotfix/* 是紧急维修线,dev 是总装车间;
- tag = 里程碑印章:v0.1 盖在历史时间轴的某个点上,发布、回滚都按章找;
- .gitignore = 门禁清单:
.o/.exe是"半成品",从不入库——它们随时能重新编译出来,存进版本库只会让仓库臃肿、让 diff 全是噪音。
⑤ 示例代码
代码示例与验证记录
- examples/gitignore-template.txtC/C++ 项目 .gitignore 模板(实测验证)✓ 已实测(git 2.53.0.windows.2 / Windows 11, 2026-08-15)编译:
复制文件内容为项目根目录的 .gitignore适用环境:LinuxWindows(MinGW)macOS展开预期输出(实测)
=== .gitignore 生效前(git status --short)=== ?? build/ ?? hello.c ?? hello.exe ?? hello.o === 加入 .gitignore 后 === ?? .gitignore ?? hello.c === 提交后 === (无输出 = 产物已被忽略,工作区干净)
差异说明:本输出为 git 2.53.0.windows.2 / Windows 11 本机实测(实验在系统临时目录进行,结束后已删除);.o/.exe 由 gcc 15.2.0 编译 hello.c 产生,build/ 为手建目录
完整源码见 /code 代码示例页
- examples/commit-convention-commands.txt规范提交 + tag 实测序列(feat/fix/docs → v0.1)✓ 已实测(git 2.53.0.windows.2 / Windows 11, 2026-08-15)编译:
按文件内顺序在终端逐行执行适用环境:LinuxWindows(MinGW)macOS展开预期输出(实测)
[main (root-commit) 8aa3215] feat: 添加 hello.c [main 19b1a9a] fix: 修正返回值 [main 433c516] docs: 补充 README === git tag === v0.1 === git log --oneline === 433c516 docs: 补充 README 19b1a9a fix: 修正返回值 8aa3215 feat: 添加 hello.c
差异说明:哈希值(8aa3215 等)每台机器不同;本输出为 git 2.53.0.windows.2 / Windows 11 本机实测
完整源码见 /code 代码示例页
⑥ 编译与运行方法
本章为文本模板与命令清单,无编译步骤:
- ex1 是 C/C++ 项目 .gitignore 模板,复制到项目根目录即可;验证方式:
git status --short不再列出*.o/*.exe/build/; - ex2 是"规范提交 + 打 tag"的实测序列(feat → fix → docs → tag v0.1 → 查看历史);
- 两个示例均在本机 git 2.53.0 实测,实验目录在系统临时目录、结束后已删除(见示例卡实测输出)。
⑦ 常见错误
| 症状 | 原因 | 解决 |
|---|---|---|
.gitignore 加了 *.o,git status 仍显示它 | 文件此前已被跟踪(.gitignore 只影响未跟踪文件) | git rm --cached 文件 移出跟踪后重新提交 |
| 想只忽略根目录 build,结果别处 build 也没了 | 写 build(无斜杠)会匹配任意层级 | 按需写 build/ 或 /build(前者只匹配目录,后者锚定根) |
! 否定规则不生效 | 父目录整体被忽略时,无法重新包含其子文件 | 先放开父目录再否定子项(进阶用法,见 man gitignore) |
| git log 里全是"改了一下/update" | 提交信息无语义,历史无法回溯 | 按 feat:/fix:/docs: 重写习惯——历史即文档 |
警告 LF will be replaced by CRLF | Windows 与仓库换行符的自动转换提示 | 正常警告;团队统一 core.autocrlf 配置即可 |
⑧ 小练习
小练习
学习自测:提交后才显示答案与解析(前端判分,不作为正式考试)ex-0-3-5-1.下面哪条提交信息最规范?(单选)
◌ 未作答ex-0-3-5-2.想让 git 忽略项目里的 build 目录,.gitignore 里写哪个最合适?(单选)
◌ 未作答ex-0-3-5-3.hello.o 之前已被跟踪,加入 *.o 后 git status 仍显示它被修改,原因是?(单选)
◌ 未作答
⑨ 章节测验
章节测验
⑩ 实战任务
实践任务
给 C 项目配 .gitignore 并打 tag v0.1
用 0.2.4 的 hello 程序(或任意一个 C 项目)编译出 .o/.exe 产物,编写 .gitignore 让 git status 保持干净,按 feat:/fix:/docs: 规范提交 2-3 次,最后打 tag v0.1 标记第一个可用版本。
输入与输出
无程序输入输出。交付物:① 项目目录(含 .gitignore 与至少 2 条规范提交);② git tag 列表含 v0.1。
功能要求
- 编译自己的 hello 程序产生 .o/.exe 等产物(gcc 15.2.0 或本机任一 C 编译器)
- 编写 .gitignore 忽略 *.o、*.exe 与 build 目录
- 按规范前缀完成至少 2 次提交(如 feat: 添加 hello.c、fix: 修正返回值)
- 用 git tag v0.1 打标签(可进阶使用 -a 附注标签)
限制条件
- 实验只在本地仓库进行,不创建远程仓库
- 实验在专用目录进行,不污染课程仓库与已有项目
验收步骤(自检清单 0/4)
验收标准
- git status --short 无 *.o/*.exe/build 产物(验收步骤 2)
- git log --oneline 至少 2 条且均以 feat:/fix:/docs: 等前缀开头(验收步骤 3)
- git tag 列表包含 v0.1(验收步骤 4)
常见失败原因
- 产物此前已被跟踪:.gitignore 不生效(需 git rm --cached 移出跟踪)
- 提交信息没有前缀(直接写内容,历史无语义)
- tag 名与已有 tag 重复(git tag 先查看再打)
- 忘了 git add 就直接 commit(提交里没有文件)
可选扩展
- 用 git tag -a v0.1 -m 说明 打附注标签,对比 git show v0.1 的信息差异
- 在 build 目录放一个空文件,确认 build/ 规则同时忽略目录内全部内容
完成必要清单后才能计入"已完成实践"(学习状态自动推导,不提供一键完成)
⑪ 面试问题
面试问题
好的 commit message 有什么标准?团队为什么要统一提交规范?高频C/C++ · easy
要点:标准:type 前缀 + 一句话说明(feat/fix/docs/refactor 等),第一行简短说清改了什么,需要时正文补充为什么改。统一规范让历史可检索、可回溯、可自动生成发布说明。
一条好的提交信息回答两个问题:改了什么、为什么改。常见约定(Conventional Commits 风格): feat(新功能)、fix(修复)、docs(文档)、refactor(重构不改行为)、test、style、chore; 第一行保持在 50 字以内,必要时用空行分隔正文补充动机与影响面。团队统一规范的价值: ① git log 即文档——新成员看历史就能理解演进;② 检索与统计——按前缀过滤 git log --grep=fix 快速定位缺陷修复;③ 自动化——按类型自动生成 CHANGELOG、 判断版本号(fix 升 patch、feat 升 minor)、触发对应流水线;④ code review 效率—— 提交粒度与语义清晰,评审者不用猜。反例是"update""改了一下",这类信息对回溯毫无价值。
追问:- 追问:一次提交应该包含多少改动?(单一职责:一个提交只做一件事)
- 追问:refactor 与 fix 怎么区分?(行为不变是重构;行为变化是修复或新功能)
评分要点:- type 前缀与一句话说明
- 历史可回溯/可检索/可自动化
- 提交粒度单一职责
.gitignore 的语法要点有哪些?实际使用中最常见的坑是什么?高频C/C++ · medium
要点:要点:* 通配、以 / 结尾匹配目录、! 否定、/ 开头锚定根目录。最常见坑:已跟踪文件不受 .gitignore 影响,需要 git rm --cached。
语法要点:① *.o 这类通配匹配任意层级的同名文件;② build/ 结尾斜杠只匹配目录 (build 不带斜杠则文件目录都匹配);③ ! 前缀表示否定(重新包含),但父目录已被整体 忽略时子文件无法用 ! 找回,需先放开父目录;④ / 开头把模式锚定到 .gitignore 所在目录。 常见坑:文件已经被 git 跟踪后,再把它写进 .gitignore 不会生效——.gitignore 只作用于 未跟踪文件;要先 git rm --cached <文件> 把它移出跟踪(保留磁盘文件)并提交。 另一个高频问题是把编译产物(.o/.exe/build)长期留在仓库里,导致每次 diff 全是 产物噪音、仓库膨胀;正确做法是建仓第一版就提交 .gitignore。
追问:- 追问:全局 .gitignore 怎么配置?(core.excludesFile,如 ~/.gitignore_global)
- 追问:如何查看某个文件为什么被忽略?(git check-ignore -v 文件)
评分要点:- 通配/目录/否定/锚定四点
- 已跟踪文件不受影响的坑
- 产物入库的工程危害
git tag 与分支有什么区别?发布版本时一般怎么做?C/C++ · medium
要点:分支是可移动的指针,随新提交自动前进;tag 是固定在某个提交上的标签,不会移动。发布流程:在要发布的提交上打 tag(如 v1.0.0),必要时推送 tag 到远程。
从实现看两者都是指向提交的引用(refs/heads/* 与 refs/tags/*),但语义完全不同: 分支是"活"的——在其上继续提交时指针自动向前移动;tag 是"死"的——永远指向打标时的 那次提交,用于标记里程碑(发布版本、验收基线)。tag 分轻量(仅一个指针)与附注 (git tag -a,额外记录打标人、时间、说明与 GPG 签名,发布推荐附注标签)。 发布流程:main 上代码稳定后 git tag -a v1.0.0 -m "发布 1.0.0",再 git push origin v1.0.0 推送到远程;之后无论代码怎么演进,都能 git checkout v1.0.0 精确回到发布状态。 反例是把 tag 当分支用(在 tag 上继续提交),会破坏"里程碑不可变"的团队共识。
追问:- 追问:tag 打错了怎么办?(git tag -d 本地删除;已推送的需 git push origin :refs/tags/名字)
- 追问:语义化版本号 v1.2.3 三段分别代表什么?(major.minor.patch)
评分要点:- 可移动与固定的本质区别
- 轻量/附注两种 tag
- 发布与回滚场景
⑫ 延伸阅读
- Conventional Commits 规范(conventionalcommits.org,只引名称不复制内容);
- 《Pro Git》第 2 章"记录每次更新到仓库"(官方免费书);
- 本地手册:
git help commit/git help tag/man gitignore; - 下一章预告:1.1.1 GCC 编译四阶段与常用选项——正式进入 C 语言正文。
迁移训练(migration training)
把本章技能迁移到 Linux 与 Mac:
| 环节 | Windows | Linux | Mac |
|---|---|---|---|
| 命令 | 一致 | 一致 | 一致 |
| 换行符 | CRLF(core.autocrlf=true 入库转 LF) | LF,无需转换 | 同 Linux |
不变的:提交规范、分支模型、tag、.gitignore 语法完全一致;要配的:Windows 的换行符转换。
内容来源映射
| 内容部分 | 资料 | 位置 | 标记 | 说明 |
|---|---|---|---|---|
| 提交规范(type 前缀与 message 结构)、分支管理模型概览、tag 概念、.gitignore 语法要点 | 第一阶段讲义 | 1.6/1.7 节 | 【来源】 | 正文在原资料基础上重新组织表述,未大段复制原文 |
| C/C++ .gitignore 模板、core.autocrlf 换行符说明、全部本机实验与练习 | 无 | 【补充】 | 原资料无 C/C++ 专用 .gitignore 模板与换行符配置说明;模板与实验输出为本机实测(git 2.53.0 / Windows 11,实验在系统临时目录进行) |