本节目标
这一节开始进入 Gerrit 工作流的核心。
你要理解这些问题:
什么是 git commit --amend?
为什么 Gerrit 修改评审意见时经常用 amend?
什么是 Change?
什么是 Patch Set?
什么是 Change-Id?
为什么 Change-Id 不能随便改?
如何更新同一个 Gerrit Change,而不是创建新的 Change?
本节重点命令:
git commit --amend
git log --oneline
git log -1
git push origin HEAD:refs/for/master
git push origin HEAD:refs/for/develop
1. 先理解一个真实工作场景
假设你在公司开发一个登录功能。
你完成代码后提交:
git add .
git commit -m "实现用户登录功能"
git push origin HEAD:refs/for/master
Gerrit 上生成了一个代码评审页面。
评审者看完后评论:
1. 用户名为空时需要提示错误信息。
2. 密码校验逻辑建议拆成独立方法。
3. 这个变量名不够清楚,请改一下。
这时你要修改代码。
新手容易这样做:
git add .
git commit -m "修改 review 问题"
git push origin HEAD:refs/for/master
这样可能会创建一个新的提交,甚至在 Gerrit 上生成一个新的 Change。
但 Gerrit 常见期望是:
在原来的 Change 上生成新的 Patch Set。
所以更常见的做法是:
git add .
git commit --amend
git push origin HEAD:refs/for/master
2. git commit --amend 是什么
amend 的意思是:
修正,修改,补充。
所以:
git commit --amend
意思是:
修改上一次提交。
它不是创建一个普通的新提交,而是用一个新的提交替换上一个提交。
原来历史可能是:
A --- B --- C
你在 C 的基础上修改代码,然后执行:
git add .
git commit --amend
历史会变成:
A --- B --- C'
注意:
C 被 C' 替换了。
C' 是一个新的 commit id。
也就是说:
amend 会重写最近一次提交。
3. amend 和普通 commit 的区别
普通提交:
git commit -m "修复 review 问题"
会在原提交后面新增一个提交:
A --- B --- C --- D
amend:
git commit --amend
会修改最近一次提交:
A --- B --- C'
简单记忆:
普通 commit:新增一次提交。
commit --amend:修改上一次提交。
4. 为什么 Gerrit 常用 amend
Gerrit 的核心单位是 Change。
一个 Change 表示:
一次待评审的代码变更。
评审者通常希望一个 Change 表达一件清楚的事情,例如:
实现用户登录功能
修复订单金额计算错误
增加配置文件读取逻辑
如果评审者提出意见,你修改后不应该再创建很多零散提交:
实现用户登录功能
修改 review 问题
继续修改
再改一次
修复拼写
这种历史会让评审很难看。
更好的方式是:
同一个 Change,不断生成新的 Patch Set。
例如:
Change 12345:实现用户登录功能
Patch Set 1:第一次提交
Patch Set 2:根据评审意见修改
Patch Set 3:修复 CI 问题
Patch Set 4:再次调整变量名
这样 Gerrit 页面会很清楚:
这是同一个代码变更,只是经过了多轮修改。
5. Change 是什么
在 Gerrit 中,Change 可以理解为:
一个代码评审单。
你推送一次待评审代码:
git push origin HEAD:refs/for/master
Gerrit 会创建一个 Change。
这个 Change 里面包含:
-
标题
-
描述
-
目标分支
-
代码差异
-
评审评论
-
Patch Set
-
CI 检查结果
-
Code Review 分数
-
Verified 分数
你可以把 Change 理解成:
这次代码修改的评审页面。
6. Patch Set 是什么
Patch Set 是 Change 里的版本。
一个 Change 可以有多个 Patch Set。
例如:
Change:实现用户登录功能
Patch Set 1:第一次提交
Patch Set 2:修复评审意见
Patch Set 3:修复单元测试
Patch Set 4:调整提交说明
Patch Set 的作用是:
记录这个 Change 每一轮修改。
评审者可以比较:
Patch Set 1 和 Patch Set 2 差异是什么?
Patch Set 2 和 Patch Set 3 又改了什么?
所以 Gerrit 很适合多人反复评审。
7. Change-Id 是什么
Gerrit 通常要求 commit message 里有一行:
Change-Id: Ixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
例如:
实现用户登录功能
Change-Id: Iabc1234567890abcdef1234567890abcdef1234
Change-Id 的作用是:
帮助 Gerrit 判断这次推送属于哪个 Change。
如果你修改代码后再次推送,只要:
Change-Id 没变
目标分支相同
仓库相同
Gerrit 通常就会把它识别为:
同一个 Change 的新 Patch Set。
如果 Change-Id 变了,Gerrit 可能会认为:
这是一个新的 Change。
8. Change-Id 从哪里来
大多数 Gerrit 项目会要求安装 commit-msg hook。
这个 hook 会在你提交时自动在 commit message 里追加:
Change-Id: I...
如果没有这个 hook,你推送到 Gerrit 时可能会失败,并看到类似提示:
missing Change-Id in message footer
意思是:
提交信息中缺少 Change-Id。
不同公司安装 hook 的命令可能不同,常见形式类似:
scp -p -P 29418 username@gerrit.example.com:hooks/commit-msg .git/hooks/
或者:
curl -o .git/hooks/commit-msg https://gerrit.example.com/tools/hooks/commit-msg
chmod +x .git/hooks/commit-msg
在 Windows 上,具体命令要看公司 Gerrit 平台说明。
9. 第一次提交到 Gerrit 的流程
假设你已经在功能分支:
git switch -c feature/login
修改代码后:
git status
git diff
git add .
git diff --cached
git commit -m "实现用户登录功能"
查看最近一次提交:
git log -1
确认 commit message 中有:
Change-Id: I...
然后推送到 Gerrit:
git push origin HEAD:refs/for/master
如果目标分支是 develop:
git push origin HEAD:refs/for/develop
10. 根据评审意见更新同一个 Change
评审者提出意见后,你修改代码。
然后执行:
git status
git diff
git add .
git diff --cached
git commit --amend
git push origin HEAD:refs/for/master
如果目标分支是 develop:
git push origin HEAD:refs/for/develop
关键点:
不要删除 Change-Id。
不要随便改 Change-Id。
这样 Gerrit 通常会生成:
Patch Set 2
而不是创建一个新的 Change。
11. amend 时会打开编辑器怎么办
执行:
git commit --amend
Git 可能会打开一个编辑器,让你修改提交信息。
如果提交标题不需要变,直接保存退出即可。
如果你想同时修改提交信息,可以在编辑器里改。
提交信息通常长这样:
实现用户登录功能
补充用户名为空时的错误提示,并拆分密码校验逻辑。
Change-Id: Iabc1234567890abcdef1234567890abcdef1234
注意:
Change-Id 那一行要保留。
12. 不打开编辑器的 amend
如果你只想把代码修改合入上一次提交,不想修改提交信息,可以使用:
git commit --amend --no-edit
意思是:
修改上一次提交,但保留原来的 commit message。
这是 Gerrit 中非常常用的命令。
常见流程:
git add .
git commit --amend --no-edit
git push origin HEAD:refs/for/master
13. amend 会改变 commit id
即使你使用:
git commit --amend --no-edit
commit id 也会变化。
因为提交内容变了。
原来可能是:
abc1234 实现用户登录功能
amend 后可能变成:
def5678 实现用户登录功能
这很正常。
Gerrit 不主要依靠 commit id 判断是否是同一个 Change。
Gerrit 主要依靠:
Change-Id
14. amend 前后如何检查
amend 前:
git status
git diff
加入暂存区:
git add .
检查即将 amend 的内容:
git diff --cached
执行 amend:
git commit --amend --no-edit
查看最近一次提交:
git log -1
确认:
提交标题正确
Change-Id 还在
修改内容正确
15. amend 和 rebase 的关系
第五课学过 rebase。
在 Gerrit 中,rebase 和 amend 经常一起出现。
15.1 只修改评审意见
如果只是根据评审意见修改代码:
git add .
git commit --amend --no-edit
git push origin HEAD:refs/for/master
15.2 目标分支更新了
如果 Gerrit 提示你的 Change 无法自动合入,可能需要先同步目标分支:
git fetch origin
git rebase origin/master
git push origin HEAD:refs/for/master
15.3 同时修改评审意见并同步主分支
一种常见流程:
git fetch origin
git rebase origin/master
# 修改评审意见
git add .
git commit --amend --no-edit
git push origin HEAD:refs/for/master
如果 rebase 过程中有冲突:
git status
# 解决冲突
git add 冲突文件
git rebase --continue
16. 一个 Change 对应一个 commit 的常见流程
很多 Gerrit 团队希望:
一个 Change 对应一个 commit。
所以开发一个小功能时,推荐这样:
git switch -c feature/login
# 修改代码
git add .
git commit -m "实现用户登录功能"
git push origin HEAD:refs/for/master
评审后继续:
# 修改代码
git add .
git commit --amend --no-edit
git push origin HEAD:refs/for/master
多轮评审也一样:
git add .
git commit --amend --no-edit
git push origin HEAD:refs/for/master
Gerrit 上会显示:
Patch Set 1
Patch Set 2
Patch Set 3
17. 如果误创建了新的 Change
新手常见问题:
明明只是修改评审意见,结果 Gerrit 上生成了一个新的 Change。
常见原因:
-
新提交没有保留原来的 Change-Id
-
使用普通 commit 创建了新提交
-
手动改了 Change-Id
-
推送到了不同目标分支
-
本地分支历史整理错误
处理方式要看团队规范。
一般思路是:
找到原 Change 的 Change-Id。
把本地提交信息改回正确的 Change-Id。
重新推送到 refs/for/目标分支。
关闭或 abandon 错误创建的新 Change。
具体操作可能涉及:
git commit --amend
修改 commit message 中的 Change-Id。
但新手遇到这种情况时,建议先问导师或团队同事,不要乱推。
18. amend 的风险
amend 会重写最近一次提交。
所以要注意:
只 amend 自己负责的提交。
不要随便 amend 公共分支上的提交。
不要在多人共享分支上随意 amend 后强推。
在 Gerrit 工作流中,amend 自己的待评审 Change 是正常操作。
但在普通共享分支上,amend 已经被别人使用的提交可能会造成麻烦。
简单记忆:
自己的本地提交可以 amend。
已经共享给别人依赖的历史不要随便 amend。
19. 实战练习:amend 修改上一次提交
创建练习仓库:
mkdir git-amend-demo
cd git-amend-demo
git init
创建第一个文件:
echo "login feature" > login.txt
git add login.txt
git commit -m "实现用户登录功能"
查看历史:
git log --oneline
现在发现还需要补充一行:
echo "empty username validation" >> login.txt
不要创建新提交,而是 amend:
git add login.txt
git commit --amend --no-edit
再次查看历史:
git log --oneline
你会发现:
还是只有一个提交,但 commit id 变了。
20. 实战练习:修改提交信息
如果你发现提交信息写得不好:
update
可以执行:
git commit --amend
把提交信息改成:
实现用户登录功能
如果是 Gerrit 提交,记得保留:
Change-Id: I...
21. 实战练习:模拟 Gerrit Patch Set 思路
虽然本地没有真正的 Gerrit 页面,但可以模拟这个过程。
第一次提交:
echo "version 1" > feature.txt
git add feature.txt
git commit -m "实现示例功能"
查看提交:
git log --oneline
模拟 Review 意见,修改文件:
echo "review fix" >> feature.txt
git add feature.txt
git commit --amend --no-edit
再次查看:
git log --oneline
你会看到:
仍然是一个提交,但提交内容已经更新。
这就类似 Gerrit 中:
Patch Set 1 -> Patch Set 2
22. 推荐的 Gerrit 提交检查流程
第一次推送前:
git status
git diff
git add .
git diff --cached
git commit -m "清楚描述本次修改"
git log -1
git push origin HEAD:refs/for/master
根据评审意见更新:
git status
git diff
git add .
git diff --cached
git commit --amend --no-edit
git log -1
git push origin HEAD:refs/for/master
如果目标分支是 develop:
git push origin HEAD:refs/for/develop
23. 常见错误
错误一:Review 修改后使用普通 commit
错误做法:
git commit -m "修改 review 问题"
更常见做法:
git commit --amend --no-edit
错误二:删除 Change-Id
不要删除:
Change-Id: I...
否则 Gerrit 可能无法识别同一个 Change。
错误三:手动乱改 Change-Id
不要随便改:
Change-Id: Iabc...
除非你明确知道自己在修复错误的 Gerrit 关联关系。
错误四:amend 后忘记重新 push
本地 amend 后,Gerrit 不会自动更新。
你还需要:
git push origin HEAD:refs/for/master
错误五:以为 amend 不会改 commit id
amend 会生成新的 commit id。
这是正常的。
Gerrit 主要看 Change-Id。
24. 本节必须记住的命令
git commit --amend
git commit --amend --no-edit
git log -1
git push origin HEAD:refs/for/master
git push origin HEAD:refs/for/develop
对应含义:
git commit --amend 修改上一次提交
git commit --amend --no-edit 修改上一次提交但不改提交信息
git log -1 查看最近一次提交详情
git push origin HEAD:refs/for/master 推送到 Gerrit,申请合入 master
git push origin HEAD:refs/for/develop 推送到 Gerrit,申请合入 develop
25. 本节总结
这一节你学习了 Gerrit 工作流中的核心能力:
-
git commit --amend用来修改上一次提交 -
Gerrit 中评审修改通常用 amend 更新同一个 Change
-
Change 是一次代码评审
-
Patch Set 是同一个 Change 的不同版本
-
Change-Id 用来让 Gerrit 识别同一个 Change
-
修改评审意见后要保留 Change-Id
-
amend 后 commit id 会变化,这是正常的
-
amend 后还需要重新推送到
refs/for/目标分支
最重要的一句话:
Gerrit 中更新评审意见,通常是 amend 后重新 push,从而生成新的 Patch Set。
下一节建议学习:
Git stash:如何临时保存工作区修改,以及切换分支前如何保护未完成代码。