60天企业 AI 实战训练营 · 阶段 1 · 第 1 周

Git 与企业
软件开发规范

建立版本管理、多人协作与可追溯交付意识

Day 04 讲师 · 包学斌 2026 · 09 含实操 · 请自带电脑

按 → 或 空格键 翻页 · F 全屏

今天这堂课解决什么问题

课程目标

01
版本管理
每一次修改都有快照、能对比、能回滚,不再依赖"最终版_真最终版"。
02
多人协作
分支 + Pull Request + Code Review,团队并行开发而不互相覆盖。
03
可追溯交付
谁在何时改了什么、为什么改,都能回答;任意历史版本可复现。
它在 60 天课程中的位置
Day 2-3 · Python 企业开发基础已完成
Day 4 · Git 与企业软件开发规范今天
Day 5 · Docker 与 AI 运行环境下一课
Day 6 · HTTP、REST API 与 FastAPI
阶段 1 交付底线:Git 管理 · Docker 部署 · 结构化日志 · 健康检查 —— Git 是第一道门槛
互动 · 举手投票 + 点击选择

你现在是怎么管理代码的?

凭直觉点一个,别想太久 —— 没有标准答案,只有真实习惯

点一个选项,看看它意味着什么 →
Part 01 · 为什么需要 Git

没有 Git 的世界:文件地狱

一个真实的分享目录
采购对账方案.pptx
采购对账方案_v2.pptx
采购对账方案_v2_修改.pptx
采购对账方案_v2_最终版.pptx
采购对账方案_v2_最终版_真最终版.pptx

三个月后:哪个是对的?哪个敢删?

有 Git 的世界:一条时间线
v1 · feat: 初版对账方案
v2 · feat: 加入供应商分组
v3 · fix: 修正金额口径

每一版都有名字、说明和作者 —— 想回到任何一版,一条命令。

Git 的答案:给每一次修改拍"快照",历史不可变,随时精确回退 —— 不靠文件名,靠提交记录。
Part 01 · 版本控制思想

版本控制的前世今生:四代进化

Git 不是凭空发明的 —— 它是 50 年"管住时间"的答案

史前 — 今天
① 手工备份
复制文件夹、改名存档,文件名就是版本号
信息全靠文件名,不敢删、回不去
1972 · RCS / CVS
② 本地版本控制
单机数据库记录每次改动,能回滚了
只有一个人、一台机器,无法协作
2000 · SVN
③ 集中式版本控制
一台中央服务器,大家 checkout / commit
服务器单点故障;断网 = 停工
2005 · Git
④ 分布式版本控制
每人一个完整仓库镜像,历史即数据
断网照常提交 · 天然多备份
版本控制的本质:把"时间"变成可查询的数据 —— 快照可回溯、分支可并行、历史可审计。Git 把这条思想推到了极致。
Part 01 · Git 的设计哲学

起源决定设计:为 Linux 内核而生

2005 年 Linus 定下的硬指标,如今成了 Git 的灵魂: 比一切现有工具快 支持上千并行分支 完全分布式 历史不可篡改
快照,而非差异
每次 commit 记录整个项目的完整目录树状态,像给项目拍照,而不是存"改了哪几行"。
分布式
每个人本地都有完整历史,断网也能提交、回退、看历史;远程仓库只负责同步,不是中心。
历史不可变
用 SHA-1 哈希寻址,任何内容改动都会产生新的提交 —— 历史被"新建",不是被"改写"。
再深一层:Unix 哲学
每个命令只做一件事,自由组合成工作流;Git 底层是一个"内容寻址的文件系统",版本控制只是它上面的一个应用 —— 所以它几乎什么都能管:代码、文档、配置、模型。
Part 01 · 人物故事

Linus Torvalds:一次被逼出来的传奇

Linus Torvalds
1969 · 芬兰赫尔辛基 —— Linux 与 Git 之父
"我用自己的名字命名项目:先是 Linux,现在是 Git。"(git = 英式俚语"讨厌鬼")
1991
21 岁的大学生发布 Linux 内核 —— "Just a hobby, won't be big and professional."(只是个爱好,成不了大器)
2002
内核团队用专有软件 BitKeeper 管理代码 —— 全球最大的开源项目,核心工具却不开源,社区里埋着一根刺。
2005
BitKeeper 免费授权被收回。Linus 闭关两周写出 Git:更快、支持上千并行分支、完全分布式 —— 随后接管整个内核源码管理。
2008
GitHub 上线 —— Git + 托管平台,全球开源协作自此引爆。
今天
Git 成为全球开发的事实标准;Linux 则真的"运行着世界"——手机、服务器、超级计算机。
"Talk is cheap. Show me the code." —— Linus Torvalds
Part 01 · 开源生态

Git 编织的开源世界

Git 2005 Linux GitHub Docker Python PyTorch VS Code
开源世界的事实标准 —— 一切协作从一次 commit 开始
规模:全球默认的协作基础设施
GitHub 1 亿+ 开发者、数亿仓库;npm / PyPI / Docker Hub 全部围绕 Git 仓库运转。
AI 时代:你的工具链也来自这里
PyTorch、Transformers、vLLM 都以 Git 仓库形态协作;你未来的第一份工作,大概率从 git clone 团队仓库开始。
规则:开源 ≠ 免费拿
遵守 License(MIT / Apache-2.0);走 issue → fork → PR 的贡献流程;尊重社区礼仪。向开源项目提交 PR,是最好的简历。
你已经每天都在使用开源。学会 Git,就是拿到加入这张协作网络的门票。
Part 01 · 为什么需要 Git

企业视角:为什么离不开 Git

这个 bug 是谁、什么时候引入的?
git blame / git log -p —— 每一行代码都能追溯到具体提交
可追溯
线上出问题,如何 5 分钟回到上一个版本?
git revert / git tag —— 版本之间可以精确切换
可回滚
三个月前发给客户的那版,还能原样复现吗?
tag + commit hash —— 任意版本可完整重建
可复现
在企业里,代码不是"写完就结束":能追溯、能回滚、能审计,才叫可交付。本课程的验收底线 —— 代码、文档、配置、测试均入 Git。
02
Part 02

Git 核心原理

不懂原理的命令是咒语,懂了原理才是工具

Git 对象数据库 三区模型 不可变快照
Part 02 · Git 核心原理

.git 目录:一个对象数据库

.git/ ├── HEAD # 当前在哪个分支 ├── config # 仓库配置 ├── objects/ # 对象数据库(核心!) └── refs/ # 分支 / 标签指针

仓库的本质:.git 目录里的对象数据库,外加一堆"指针"。删除工作区文件不丢历史,删掉 .git 才真的丢。

blob 文件内容快照 —— 只存内容,不存文件名
tree 目录快照 —— 记录"哪个文件名指向哪个 blob"
commit 版本快照 —— 指向一个 tree + 父提交 + 作者 / 时间 / 说明
tag 里程碑别名 —— 给某个 commit 起名字(如 v1.0.0)
SHA-1 = 内容指纹,40 位:e83c5163316f89bfbde7d9ab23ca2e25604af290
内容相同 → 哈希相同 → 全库自动去重;哈希不同 → 内容一定不同,这就是"不可变"的数学保证。
Part 02 · Git 核心原理

三区模型:add 与 commit 之间发生了什么

每点一次,观察文件 train.py 的位置变化(核心动画,请盯紧)

工作区
Working Directory
git add
暂存区
Index / Staged
git commit
版本库
.git · 不可变快照
train.py
git add train.py 挑选改动放进"购物车":决定哪些修改进入下一次提交。
git commit -m "feat: ..." 购物车内容打包成一个不可变快照,永久入库。
继续修改 → 循环 工作区出现新改动,重复 add / commit。每个阶段都可用 git status 查看。
Part 02 · Git 核心原理

Commit:一条不可变的时间线

HEAD → main
a1b2c3dc1
d4e5f6ac2
9b8c7d6c3
c1 · a1b2c3d
feat: 项目初始化
parent:无(第一个提交)
c2 · d4e5f6a
feat: 新增 sum()
parent:a1b2c3d
c3 · 9b8c7d6
test: 补充 sum 用例
parent:d4e5f6a
每个 commit = 内容(tree) + 父提交(parent) + 作者 / 时间 / 说明
HEAD 是"你现在在哪"的指针,main 是主分支的名字 —— 它们都只是指向某个 commit 的指针
为什么说"不可变"?
改动任何一个字符 → 生成全新 hash;所谓"修改历史"(amend / rebase)其实是造出新提交。铁律:已推送到远程的历史,不去修改。
Part 02 · Git 核心原理

日常命令速查

左列推动流程,右列回答"刚才发生了什么 / 怎么回头"

# 把流程向前推进 git status # 看状态(最常用!) git add train.py # 单个文件进暂存区 git add -A # 全部改动进暂存区 git commit -m "feat: x" # 暂存区 → 快照入库 git log --oneline # 一行一条看历史 git log --graph --oneline --all # 带分支图的历史
# 查看差异 · 后悔与恢复 git diff # 工作区 vs 暂存区 git diff --staged # 暂存区 vs 最近提交 git restore app.py # 丢弃工作区修改(慎用) git restore --staged f # 移出暂存区(不加 -A 时) git show a1b2c3d # 看某次提交改了什么 git reflog # 后悔药:找回"消失"的提交
本地仓库
你的电脑 —— add / commit / branch 随便折腾,断网也能提交
push ↑   pull ↓
远程仓库
企业内网 GitLab / GitHub / Gitee —— 团队协作与备份中心
新人心法:迷路时只做两件事 —— git statusgit log --oneline,先看清现状再决定下一步。90% 的"Git 恐慌"都靠这两条命令化解。
实操 ① · 8 分钟 · 每人一台电脑

第一次提交

1新建并进入仓库:git init day04-lab && cd day04-lab
2新建 hello.py,打印你的名字
3git status —— 观察红色 untracked 状态
4git add hello.py → 再 git status,看颜色变化
5git commit -m "feat: 项目初始化"(用规范 message)
6git log --oneline 验证你的第一条历史
08:00
验收标准
git log --oneline 有一条 feat: 开头的提交
git status 显示 working tree clean
新人心法
迷路时只做两件事:git statusgit log --oneline —— 先看清现状,再决定下一步。

卡住就举手 —— 实操环节助教会巡场

互动 · 快问快答

对错判断:三道送命题

先举手投票,再点击揭晓答案

1. commit 之后,我的代码就绝对安全了?
没 push 之前,历史只存在你自己电脑上 —— 硬盘一坏,照样全丢。
✗ 错
2. add 之后又改了文件,直接 commit 会包含最新修改?
暂存区记录的是 add 那一刻的内容;新修改需要再次 add。
✗ 错
3. rm 删了文件后,git add -A 再 commit,这次删除会进入历史?
对 —— 删除也是一次变更;Git 如实记录"哪个提交删了它"。
✓ 对
03
Part 03

Commit 规范

让三个月后的你,还看得懂历史

Git 反面教材 Conventional Commits 找茬游戏
Part 03 · Commit 规范

两种历史,两种命运

这样写,等于没写
update
修改
fix bug
改好了
asdfgh

排查问题时,你无法从历史里获得任何线索。

三个月后依然看得懂
fix: 修复金额合计 float 精度丢失
feat: 供应商对账单导出 Excel
docs: README 补充启动步骤
refactor: 抽出公共求和函数

类型 + 一句话说清"做了什么",历史即文档。

核心原则:一次 commit = 一个逻辑变更(代码 + 对应测试 + 相关文档一起提交)—— 这是为了 code review 方便,也是为了能精确回滚。
Part 03 · Commit 规范

Conventional Commits:团队通用格式

<type>(<scope>): <subject>
例:feat(ocr): 新增发票字段抽取
feat
新功能
fix
缺陷修复
docs
文档
refactor
重构
test
测试
chore
构建·杂务
为什么企业强制要求?
工具可以按 type 自动生成 CHANGELOG、按版本检索历史、在 code review 里快速判断改动性质 —— message 不是写给自己看的,是写给团队和工具看的。
互动 · 找茬游戏

这些 commit message 合格吗?

先心里给个判断,再点击揭晓

update code 零信息量,等同于没写
fix: 登录接口偶发 500,调整超时重试 类型 + 现象 + 动作,教科书式
feat + fix + 文档修改(37 个文件) 一个 commit 只应包含一个逻辑变更
feat(user): 支持供应商批量导入 类型 + 范围 + 动作,清晰
改好了 三周后没人知道改了什么
refactor: 抽出对账金额计算公共函数 重构类型使用正确,不影响行为
04
Part 04

分支与协作

多人并行开发,而不互相践踏

Git 分支 = 指针 merge · rebase 冲突处理
Part 04 · 分支与协作

分支:一个会移动的指针

每点一次,看下图发生什么

创建分支 = 写入一个 41 字节的指针文件,瞬间完成,零成本
c1
c2
c3
c4
c5
c6
main
main
feature/ocr
feature/ocr
① 建分支 git switch -c feature/ocr —— 只是复制了一个指针,仍指向 c4。
② 分支上开发 新提交 c5 挂在 feature 上;main 纹丝不动,互不干扰。
③ 合并回 main merge 产生合并提交 c6,main 指针前移到 c6。
④ 删分支 main 永远保持"可发布";合并后 feature 分支删除,不留坟场。
Part 04 · 分支与协作

merge vs rebase:两条整合路线

merge:保留分叉,生成合并提交
c1
c2
f1
f2
M
历史如实反映"分了又合"的协作过程;团队合并 PR 的默认方式
rebase:把提交"搬家"到主干末尾
c1
c2
c3
f1
f2
f1'
f2'
f1'、f2' 是全新提交(新 hash);历史变成一条直线,更干净。仅用于自己的本地分支
铁律
已经推送到远程、别人可能已拉取的公共分支 —— 绝不 rebase。否则每个人的本地历史都会错乱,团队一起爆炸。
Part 04 · 分支与协作

冲突:Git 无法替你做业务决定

main 分支
app.py 第12行
discount = 0.8 # 大促 8 折
feature/discount 分支
app.py 第12行
discount = 0.75 # 长期协议 7.5 折
合并时
CONFLICT !
# app.py —— 合并时 Git 插入的冲突标记 def calc_price(price):<<<<<<< HEAD discount = 0.8 # main:大促活动 8 折======= discount = 0.75 # feature:长期协议价 7.5 折>>>>>>> feature/discount return price * discount
冲突不是 Git 的 bug
同一区域被两条分支修改,Git 不猜 —— 它把两个版本摆到你面前,业务取舍由人来做。这恰恰是它可靠的原因。
Part 04 · 分支与协作

解冲突四步法

STEP 1
定位
git status 列出 both modified 的文件。
STEP 2
读懂两边
逐块比较双方业务意图 —— 是合并两边的逻辑,还是取一方?
STEP 3
手工合并
改出正确代码,删除三行冲突标记,不是机械地"选一边"。
STEP 4
提交并验证
add + commit 完成"一次合并";重跑测试才算真正解决。
高危操作警示
git checkout --theirs app.py 表示"全盘拿对方的版本"—— 你自己的修改全部丢失;反过来 --ours 则丢掉对方的。 去年某项目解冲突时误用,两名同事各丢两天工作量。解冲突用编辑器,不用覆盖命令。
Part 04 · 分支与协作

分支避坑清单

1
所有人直接在 main 上开发
没有隔离,随时互相踩踏,main 永远处于"半成品"状态 —— 不可发布。
2
长命 feature 分支,偏离主干几周
分支与 main 严重发散,最后合并即地狱:冲突上百处,没人敢解。
3
分支用完不删,变成坟场
一个月后 50 个分支,没人知道哪些还活着、哪个敢删。
小步分支:一个分支只做一件事 快合快删:生命周期以天计,不以月计 PR 走 Review:不直接 push main main 永远可发布
实操 ② · 10 分钟 · 两人一组

双人冲突演练

1A、B 都从 main 出发:git switch -c feat-a / git switch -c feat-b
2两人改 config.py同一行:A 写 MODE = "TRAIN",B 写 MODE = "EVAL",各自 add + commit
3A 先合并回 main(顺利);B 再合并 → 冲突出现
4B 手工解冲突:讨论后改成 MODES = {"TRAIN": ..., "EVAL": ...},add + commit
5git log --graph --oneline 观察合并提交;跑 python config.py 验证
10:00
验收标准
合并提交出现在 --graph 历史里
脚本可运行,双方逻辑都保留
没有残留冲突标记
05
Part 05

企业协作流程

从"我一个人写"到"一个团队有纪律地交付"

GitLab feature 分支 Pull Request tag · SemVer
Part 05 · 企业协作流程

Feature 分支工作流:一道流水线

① 建分支
feature/xxx
② 本地开发
规范 commit
③ push 远程
推送到 GitLab
④ Pull Request
描述改动 + 关联需求
⑤ Code Review
同事批注讨论
⑥ CI 自动测试
pytest + 构建
⑦ Merge + 删分支
进入 main
两道质量闸门
Review 挡住"写错的代码",CI 挡住"测不过的代码"。任何改动直接进 main,在企业里都是事故。
被 Review ≠ 被否定
批注是团队的知识共享;企业新人代码被改得"满屏批注",是成长最快的方式。
Part 05 · 企业协作流程

Tag 与语义化版本:给里程碑起名字

tag:commit 的静态别名
commit 哈希难记,tag 用来标记对人有意义的节点:UAT 版本、正式发布、模型迭代。
git tag -a v1.0.0 -m "首个发布版本"
配合 CI 自动发布
推送 tag → CI 自动构建 Docker 镜像、生成发布产物 —— Day 05 就会用到。
v2.4.1
MAJOR · 破坏性变更 接口或数据格式不再兼容(如删了配置字段)→ +1,后两位归零。
MINOR · 新特性 向后兼容的新功能(如新增导出)→ +1,PATCH 归零。
PATCH · 修复 不改接口的 bug 修复 → +1。
常见坑:只在本地打 tag 忘记 git push --tags;命名混乱(v1、V1.0、release-1)—— 统一 v{MAJOR}.{MINOR}.{PATCH}
06
Part 06

AI 项目仓库规范

让任何同事一天之内跑通你的项目

Bash 六大目录 .gitignore · LFS 密钥安全 README
Part 06 · AI 项目仓库规范

AI 项目的标准目录结构

ai-project/ ├── src/ # 源代码 ├── tests/ # pytest 测试 ├── docs/ # 文档(与代码同版本) ├── data/ # 数据目录(不入库) ├── models/ # 模型权重(LFS / 外链) ├── config/ # 配置(example 入库) ├── .gitignore # 忽略规则 └── README.md # 项目门面
src 源代码,全部入库
tests 测试入库 —— CI 的粮食
docs 文档入库,与代码同版本,关键文档配图
data 不入库;README 写清"数据如何获取"
models 权重走 LFS 或对象存储 + 版本记录
config config.example.yaml 入库,真实值不入
原则:代码、文档、配置、测试均入 Git;"大的和密的"不入库,只留一份"如何获取"的说明。
Part 06 · AI 项目仓库规范

.gitignore:把"大与密"挡在门外

# Python __pycache__/ *.pyc .venv/ venv/ # 数据与模型 data/ models/*.pt # 环境与密钥(重点!) .env # 其他 *.log .ipynb_checkpoints/ .DS_Store
判断标准
仓库里只放文本与源码,不放大数据与二进制。每次 add 前问一句:这文件三个月后还有意义吗?
Git LFS:大文件的正规通道
模型权重、数据集用 git lfs track "*.safetensors",仓库里只存指针文件,真实文件进 LFS 存储。
经验阈值
超过 10MB 想一想;超过 100MB 不要放普通 Git —— 一旦提交,历史里永远都在。
Part 06 · AI 项目仓库规范 · 互动讨论

三大血泪坑

1
把 venv / data / .env 提交进库
仓库膨胀几个 GB,clone 半小时 —— 新人体验直接劝退。
2
git rm 删了大文件,仓库却没变小
历史提交里仍然存在;要 filter-repo 重写历史,代价极大 —— 所以别让它进来才是重点。
3
.env 里的 API Key 被提交,日志里又打印了完整环境变量
密钥泄漏 —— 企业里等同于生产事故,后面是漫长的轮换与审计。
课堂讨论 · 2 分钟
密钥已经 push 到远程仓库,现在删掉 .env 再 push 一次,泄漏就消除了吗?
答案:不能
删除只影响最新版本;历史里密钥仍在,checkout 旧提交就能拿到。唯一正确做法:立刻作废并轮换密钥。密钥一旦进过 Git,就当作已泄漏。
Part 06 · AI 项目仓库规范

README:仓库的门面

1项目说明 —— 一句话说清做什么、为什么做
2环境要求 —— Python 版本、依赖、需要的模型 / API
3快速开始 —— 复制即可运行的命令序列
4目录结构 —— 每个目录干什么
5测试方法 —— pytest 怎么跑、看什么指标
6常见问题 —— 新人最常卡的三个点
验收标准(硬性)
新同事 clone 后,一天之内跑通 demo —— 做不到就是 README 不合格。
AI 项目额外必写
模型 / API 依赖及版本、数据获取方式、评测怎么跑 —— 这是 AI 项目与普通项目 README 的最大区别。
常见坑
README 停留在 TODO;启动命令与代码脱节;不写测试运行方法。
实操 ③ · 15 分钟 · 综合演练

从零搭一个规范仓库

1git init ai-demo,建立六大目录 + .gitignore + README
2config.example.yaml 入库;本地 .env 被 ignore 规则挡住
3src/sum.py + tests/test_sum.py,规范 commit 分步提交
4git switch -c feature/readme 补全 README → 合并回 main
5git tag -a v0.1.0 -m "第一个可运行版本"
15:00
验收清单
git log --oneline 全部是规范 message
git status 干净,.env 未被跟踪
git log --graph 合并历史清晰
git tag -n 看到带说明的 v0.1.0
新人视角:README 能照着跑通
总结

今天你带走了什么

起源
2005 年 Linus 为 Linux 写下 Git;如今 1 亿+ 开发者用它协作
原理
对象数据库 · blob / tree / commit / tag · SHA-1 寻址
原理
三区模型:工作区 → 暂存区 → 版本库
原理
commit = 不可变快照 + 父指针链
规范
Conventional Commits:类型 + 一句话
协作
分支 = 指针;小步分支、快合快删
协作
冲突四步法;绝不 --theirs 覆盖
协作
PR + Review + CI 两道闸门
版本
tag + SemVer:v{MAJOR}.{MINOR}.{PATCH}
仓库
六大目录 + .gitignore + LFS
仓库
密钥永不入库;进过即泄漏
视野
Git 编织开源生态 —— 你已是这张网络的候补成员
"Git 不是命令背诵,而是让每一次交付可追溯、可回滚、可协作。"
课后作业 · 明日预告

今天到此,工程起步

作业 1 · 迁移
把 Day 2-3 的 Pandas 脚本迁入规范仓库:六大目录 + .gitignore + 规范 commit 历史。
作业 2 · 文档与版本
README 写到"新人可跑通"标准,打 tag v0.1.0(带说明)。
作业 3 · 阅读
Pro Git 中文版第 1、2、3 章:git-scm.com/book/zh/v2;顺便读读 Git 诞生那两周的故事。
明日预告 · Day 05
Docker 与 AI 运行环境
今天我们把代码放进规范仓库;明天把整个环境打包成可复制的镜像 —— 让"在我电脑上是好的"这句话永远消失。
谢谢 · Q&A —— 实操问题随时在群里 @ 包学斌
Day 04 · Git 与企业软件开发规范 · 包学斌
1 / 37