Appearance
第47课:Git
🎯 学习目标
- 理解 Git 三个区(工作区/暂存区/版本库)与数据流转
- 掌握基本操作(add/commit/push/pull/clone)
- 掌握分支、merge 与 rebase 的区别与选择
- 掌握冲突解决
- 了解 GitFlow / GitHub Flow 工作流
📖 一、概念讲解:Git 三个区
1. 三个区
工作区(Working Directory) 你能看到的文件(编辑器改的)
↓ git add
暂存区(Staging / Index) 准备提交的快照(购物车)
↓ git commit
版本库(Repository) 提交历史(.git 目录)
↓ git push
远程仓库(Remote) GitHub/GitLab类比:
- 工作区 = 工作台(你在上面改东西)
- 暂存区 = 购物车(选好要结账的商品放这)
- 版本库 = 仓库货架(结账入库,成为历史记录)
- 远程 = 总部仓库(同步过去)
2. 为什么有暂存区?
让你挑选要提交的改动。比如改了 3 个文件,只想提交其中 2 个 → git add 那 2 个。没有暂存区只能一次提交所有改动,不灵活。
📖 二、基本操作
bash
# 初始化/克隆
git init # 新建本地仓库
git clone <url> # 克隆远程仓库
# 配置(首次)
git config --global user.name "你的名字"
git config --global user.email "邮箱"
# 日常三连(最常用)
git add <file> # 工作区 → 暂存区(git add . 全加)
git commit -m "提交说明" # 暂存区 → 版本库
git push # 版本库 → 远程
# 查看
git status # 看当前状态(改了啥、暂存了啥)
git log --oneline # 看提交历史(简洁)
git diff # 工作区 vs 暂存区(未暂存的改动)
git diff --staged # 暂存区 vs 版本库(已暂存未提交)
# 撤销
git restore <file> # 丢弃工作区改动(未 add)
git restore --staged <file> # 把暂存区文件移回工作区(已 add 未 commit)
git reset --soft HEAD~1 # 撤销上次提交(保留改动到暂存区)
# 同步
git pull # 拉取远程并合并(= fetch + merge)
git fetch # 只拉取,不合并(更安全,先看再决定)📖 三、分支
分支是 Git 的灵魂——在不影响主线的情况下并行开发。
bash
git branch # 查看分支(* 是当前)
git branch dev # 创建 dev 分支
git checkout dev # 切换到 dev(或 git switch dev)
git checkout -b feature-x # 创建并切换(一步)
git branch -d dev # 删除分支
git merge dev # 把 dev 合并到当前分支常见分支策略:
main/master:稳定主线(生产代码)develop:开发集成feature/*:功能开发hotfix/*:紧急修复release/*:发布准备
📖 四、merge vs rebase(重点)
两种合并方式,区别在于历史形状:
merge(保留分叉历史)
bash
git checkout main
git merge featureA---B---C (main)
\
D---E (feature) → merge → A---B---C---M (main)产生一个合并提交 M,保留 feature 的历史轨迹。
rebase(线性历史)
bash
git checkout feature
git rebase main # 把 feature 的提交"挪到" main 最新点之后
git checkout main
git merge feature # fast-forward,线性A---B---C (main) → rebase → A---B---C---D'---E' (线性)rebase 把 feature 的提交重新应用到 main 最新,历史变成一条直线。
怎么选:
- merge:保留完整历史(谁在分支上做了啥),适合公共分支(团队协作)。
- rebase:历史干净线性,适合个人分支整理(提交前美化历史)。
- 黄金法则:rebase 已 push 的公共分支会改写历史,坑队友,别用! 只 rebase 自己的本地分支。
类比:merge 像"两条河汇合"(保留支流轨迹),rebase 像"把支流接到主流下游"(变成一条河,但改变了支流的位置)。
📖 五、冲突解决
两人改了同一文件同一区域,合并时冲突:
bash
git merge feature
# Auto-merging file.txt
# CONFLICT (content): Merge conflict in file.txt文件里出现冲突标记:
<<<<<<< HEAD
我的改动(当前分支)
=======
feature 的改动
>>>>>>> feature解决:手动编辑,保留正确内容,删标记,然后:
bash
git add file.txt # 标记冲突已解决
git commit # 完成合并git mergetool 或 IDE(IntelliJ/VSCode)有可视化冲突解决工具。
📖 六、工作流
GitHub Flow(简单,常用)
- 从 main 拉分支
feature-x - 在分支开发,频繁 commit/push
- 完成 → Pull Request(PR)
- Code Review 通过 → 合并到 main
- 删分支,部署
GitFlow(复杂,企业)
- main(生产)/develop(开发)/feature/release/hotfix 多分支
- 适合版本发布的产品(有明确版本号)
现代实践:多数团队用简化版 GitHub Flow + 持续部署,少用复杂 GitFlow。
⚠️ 七、常见陷阱
陷阱1:rebase 公共分支
rebase 已 push 的分支会改写历史,别人 pull 冲突混乱。只 rebase 本地未共享分支。
陷阱2:commit 太大
一个 commit 塞太多改动,难 review/回滚。小步 commit,一个功能一个提交。
陷阱3:乱用 force push
git push --force 覆盖远程历史,会删队友提交。用 --force-with-lease(更安全,远程被改了会拒绝)。
陷阱4:commit 信息差
"fix"、"update" 无法判断改了啥。规范:类型(范围): 描述(如 feat(user): 添加登录)。
陷阱5:忘记 pull
本地落后远程就 push,被拒。先 git pull --rebase 同步再 push。
陷阱6:大文件/敏感信息入库
密码、密钥、大文件别提交(用 .gitignore,密码用环境变量)。
🆚 八、Git vs SVN / 给 C 程序员
| 特性 | SVN(集中式) | Git(分布式) |
|---|---|---|
| 仓库 | 中央服务器单一 | 每人本地完整仓库 |
| 离线 | 不能 commit | 能 commit/push 时再同步 |
| 分支 | 重(复制目录) | 轻(指针) |
| 速度 | 慢(联网) | 快(本地) |
对 C 程序员:Git 替代 SVN,分布式让每人有完整历史,离线可工作,分支轻量。.git 目录存全部历史(类似一个微型数据库)。学会 Git 是协作基础。
💡 九、最佳实践
- 小步 commit:一个功能一个提交,信息清晰(
类型: 描述)。 - 分支开发:不在 main 直接改,拉分支→PR→review→合并。
- .gitignore:忽略编译产物、IDE 配置、敏感文件。
- pull 用 rebase:
git pull --rebase保持线性历史。 - 只 rebase 本地分支,公共分支用 merge。
- force-with-lease 替代 force。
- Code Review:PR 强制 review,提升质量。
- 提交前 git status/diff 检查,避免误提交。
📝 练习预告
完成 练习/Ex47_Git.java 中的 6 道题:
- 三个区与数据流转
- 基本命令(add/commit/push/pull)
- 分支创建与切换
- merge vs rebase
- 冲突解决
- 综合:模拟 Git 提交历史(Java 实现版本图)
完成后对比 答案/Sol47.java,查看逐行讲解与多解法。
📖 十、撤销操作体系
Git 撤销要先判断改动处在哪个区。
| 场景 | 命令 | 结果 |
|---|---|---|
| 工作区改错,未 add | git restore file | 丢弃工作区改动 |
| 已 add,想取消暂存 | git restore --staged file | 回到工作区 |
| commit 写错,未 push | git reset --soft HEAD~1 | 撤销提交,保留暂存 |
| commit 要整体反做 | git revert <commit> | 新增一个反向提交 |
| 想拿某个提交 | git cherry-pick <commit> | 应用指定提交 |
重要区别:
text
reset 会移动分支指针,可能改写历史。
revert 不改写历史,适合公共分支。
restore 面向文件内容。
cherry-pick 面向提交。公共分支优先 revert,不要轻易 reset 后 force push。
📖 十一、工作流选择
小团队常用 GitHub Flow:
text
main 永远可部署。
每个需求从 main 拉 feature 分支。
提交 PR。
Code Review。
CI 通过。
合并回 main。需要版本发布的团队可使用 release 分支:
text
main:生产。
develop:日常集成。
feature/*:功能。
release/*:发布候选。
hotfix/*:线上修复。选择建议:
text
持续部署团队:GitHub Flow 更简单。
移动端或桌面端版本发布:release 分支更合适。
多人长期大功能:feature 分支要频繁同步 main,避免最后大冲突。流程越复杂,执行成本越高。不要为了“看起来专业”引入团队维护不了的 GitFlow。
🧪 十二、冲突解决流程
冲突不是错误,而是 Git 无法自动判断保留哪段内容。
推荐流程:
text
1. git status 查看冲突文件。
2. 打开文件定位 <<<<<<< ======= >>>>>>>。
3. 理解双方改动意图。
4. 手动编辑成最终正确结果。
5. 运行测试或至少编译。
6. git add 标记已解决。
7. git commit 或继续 rebase。如果在 rebase 中:
bash
git rebase --continue
git rebase --abort如果在 merge 中:
bash
git merge --abort解决冲突时不要机械选择“当前”或“传入”,要理解业务语义。
🔐 十三、敏感信息和大文件
不要提交:
text
密码。
API Key。
私钥。
.env。
数据库 dump。
大二进制文件。
编译产物。
IDE 临时文件。.gitignore 示例:
gitignore
target/
build/
.idea/
*.iml
.env
*.log如果敏感信息已经提交:
text
立即轮换密钥。
删除仓库历史中的敏感文件。
通知相关人员。
不要只在新 commit 里删除文件,因为历史仍然存在。大文件应使用对象存储、发布系统或 Git LFS,而不是直接塞进普通 Git 历史。
🧭 十四、PR 检查清单
提交 PR 前自查:
text
分支是否基于最新 main。
commit 是否聚焦。
commit message 是否清晰。
是否包含无关格式化。
是否提交了敏感文件。
是否运行测试。
是否更新文档。
是否说明风险和回滚方式。Review 时重点看:
text
行为是否正确。
边界条件是否覆盖。
是否有测试。
是否引入不必要复杂度。
是否破坏兼容性。
是否影响性能或安全。Git 是协作工具,不只是提交工具。PR 描述和提交历史会直接影响团队理解成本。
🛠 十五、常用排查命令
bash
git status
git log --oneline --graph --decorate --all
git reflog
git diff
git diff --staged
git blame file
git show <commit>
git branch -vv
git remote -vreflog 很重要:
text
它记录本地 HEAD 移动历史。
误 reset、误 rebase 后,常能通过 reflog 找回提交。
reflog 是本地记录,不等于远程历史。找回示例:
bash
git reflog
git switch -c recover <commit>✅ 十六、掌握标准
学完本课后,应能做到:
text
能解释工作区、暂存区、版本库。
能独立完成 add、commit、push、pull。
能创建分支并合并分支。
能解释 merge 和 rebase 的区别。
能解决常见文本冲突。
能使用 restore、reset、revert、cherry-pick。
能用 reflog 找回本地误操作。
能遵守不提交敏感信息和大文件的原则。
能按团队工作流完成 PR 协作。Git 的高阶能力不是记住更多命令,而是知道每个命令会不会改写历史、会影响谁、如何回滚。
🎓 下一步
- 第48课:JUnit — 断言、生命周期、参数化测试