- 新手如何使用 git/github 参与代码协作
- 1. fork 代码仓库
- 2. (可选)认领任务 (issue)
- 3. 本地开发,提交代码
- 4. 创建 pull request
- 5. 参与 code review
- 6. 其他建议
- 6.1 如果可能,请本地测试后才提交 pr,或者将 pr 标为 draft。
- 6.2 不要同样的代码开多个 pull request
- 6.3 当一个 pr 没有动静时,发挥主观能动性
- 6.4 保持礼貌
- 6.5 不要在没有沟通的时候扔一个大 Feat 上去
- 6.6 能否自动化,能否批量处理?
- 6.7 Help Wanted 和 Good First Issue 标签
- 6.8 Git Merge vs. Rebase vs. Squash Commit
- 6.9 很多测试本地因为网络问题,因为配置问题过不去?
- 6.10 将未完成的代码标记为草稿是一种礼貌
- 6.11 因为网络关系,时不时访问 github 超时?
以 kubernetes 仓库为例,点击这个 fork 按钮,在下一步中选择 Create Fork 即可。
会打开:
这样,你就有一个仓库可以自由折腾。不会影响到上游仓库。
依据仓库的规模和合作方式,开源社区的仓库很多建议(不强制)先认领任务再开发。这样有助于大家异步开发保持合作,其他成员可以看到这个任务有人认领,一般不会再投入时间和精力重复开发。
以 etcd 库为例:
可以随意写一些内容标明你要做这个任务,然后有权限的用户可以 assign 这个任务给你,然后就可以开发了。
从开源社区来说,约定俗成的规矩是:标有 "good first issue"的任务适用于新手前几次参与项目,熟悉项目的开发者不应该抢这些简单的任务。
一个典型的 git 工作流见 kubernetes 的工作流,相关描述在 https://www.kubernetes.dev/docs/guide/github-workflow/
先将代码 clone 到本地:
# 从自己 fork 的仓库 clone 代码,会把自己的仓库作为 origin 上游
git clone git@github.com:Abirdcfly/etcd.git
# 把上游仓库设置为 upstream,
git remote add upstream https://github.com/etcd-io/etcd.git
# 给 upstream 设置禁止 push,防止错误的 push 到上游
git remote set-url --push upstream no-pushing
一般建议使用新的分支来写功能,这样哪怕同时做着几件事也可以在不同的分支里来回切换,互不干扰:
git fetch upstream
git checkout main
git rebase upstream/main
git checkout -b myfeature
然后写代码......
通过 git status
查看当前代码仓库的状态:
$ git status
位于分支 main
您的分支与上游分支 'origin/main' 一致。
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git restore <文件>..." 丢弃工作区的改动)
修改: main.go
未跟踪的文件:
(使用 "git add <文件>..." 以包含要提交的内容)
argocd-plugin-generator/
controller.json
log
my-nginx-3.3-revision-1
nginx-15.1.0.tgz
nginx.json
nginx.yaml
nginx/
tekton-operator-0.64.0.tgz
tmp.yaml
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
通过 git add xxxx
添加提交到的目录和文件:
$ git add main.go controller.json
$ git status
位于分支 main
您的分支与上游分支 'origin/main' 一致。
要提交的变更:
(使用 "git restore --staged <文件>..." 以取消暂存)
新文件: controller.json
修改: main.go
未跟踪的文件:
(使用 "git add <文件>..." 以包含要提交的内容)
argocd-plugin-generator/
log
my-nginx-3.3-revision-1
nginx-15.1.0.tgz
nginx.json
nginx.yaml
nginx/
tekton-operator-0.64.0.tgz
tmp.yaml
通过 git commit
创建一个 commit,会根据情况弹出一个编辑界面,让你填写 commit 的内容:
commit 信息的填写类似代码风格,一般依据仓库的不同而不同,但是有一些通用的约定俗成的规则(下面的规则来自 kubernetes commit-message-guidelines):
- 尽量将主题行(第一行,上图中 This is the commit message subject 那行)控制在 50 个字符以内;不超过 72 个字符。
- 主题行开头使用一些约定俗成的单词,比如 fix,cleanup,deprecation 等等
- 主题行不要以句号结束
- 在主题行中使用祈使语气,实现的效果是“当你这个 commit 提交后,会",xxxxx 部分是主题行,比如
Fix x error in y
或者Add foo to bar
- 在主题行和提交消息正文(上图中 Any text here is the commit message body Some text Some more text ...都是)中间添加一个空行
- 提交消息正文 72 字符后换行
- 不要在提交消息正文中使用 GitHub 关键字(close/closes/closed fix/fixes/fixed resolve/resolves/resolved)或
@
提及某个用户 - 使用提交消息正文来解释 commit 的 What 和 Why
如果你和我一样使用的是 vim 的话,:wq
是保存。
一些参考链接:
- https://www.kubernetes.dev/docs/guide/pull-requests/#commit-message-guidelines
- https://cbea.ms/git-commit/
- https://chris.beams.io/posts/git-commit/
- https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project
- https://preslav.me/2015/02/21/what-s-with-the-50-72-rule/
- https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
还是来自 kubernetes 的指南:
- Small Commits, Small Pull Requests,让 commit 尽可能小,做一件事用一个 commit,这样方便代码审查 code review,方便移植到别的版本或分支 git cherry-pick,方便以后出错回滚 git revert
- 拆分 commit,如果一件事需要先重构 A,再更新 B,一个合适的选择是创建一个 pull request,但是写 2 个 commit,一个重构,一个更新
- 不要把 feat 和 fix 放在一个 commit 中。
然后就可以使用 git push origin <branch-name>
来推送到某个远程分支了:
$ git commit
[main d04ca4b] This is the commit message subject
2 files changed, 3 insertions(+), 1 deletion(-)
create mode 100644 controller.json
$ git push origin main
枚举对象中: 6, 完成.
对象计数中: 100% (6/6), 完成.
使用 8 个线程进行压缩
压缩对象中: 100% (4/4), 完成.
写入对象中: 100% (4/4), 626 字节 | 626.00 KiB/s, 完成.
总共 4(差异 2),复用 0(差异 0),包复用 0
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
To github.com:Abirdcfly/core.git
1c59fd5..457fe27 main -> main
可以在很多页面打开 pull request 的界面,实际只要是 https://github.com/kubebb/core/compare/main...Abirdcfly:core:main
这种链接就可以(kubebb/core 是上游仓库,main 是上游分支,Abirdcfly:core:main 是 fork 的仓库和分支),我习惯在自己 fork 的仓库打开 pull reqeust 界面:
会打开:
在上面的图中:
- 一般自己 fork 的分支依据自己刚才的 push 命令的提交情况选择分支,
- 上游分支则根据项目不同而不同,可能是 master,main,dev 或者其他,具体情况具体对待,一般项目会说明。
- 在整张图的下半部分展示了这个 pr 会包含的 commit 信息和文件的 diff 信息。
核对无误,点击
Create pull request
然后就会跳到下面这张图的创建界面,这个界面类似 commit 的界面,一般内容也和 commit 差不多。 - 有的项目会有 pull request 模版,里面会提到应该写什么内容,按要求写就好。
- 这里推荐写 GitHub 关键字(close/closes/closed fix/fixes/fixed resolve/resolves/resolved Duplicate of)以及
@
某人。- 在 pull request 的提交信息界面写
fix #123
会在当前 pull request 和序号 123 的 issue 页都显示一个链接,方便查找关联,并在在当前 pull request 合并后自动关闭序号为 123 的 issue。 - 只有第一条 pull request 的提交信息有效,后序的 comment 里写无效。
- 这个
#
写法,套路是本仓库可以直接写 issue 或者 pullrequest 序号,比如#123
,如果是其他仓库,写用户仓库名即可,比如Abirdcfly/core#123
- 要注意,重要的内容一定要也出现在 commit message 中,我们查看代码的时候,可不一定会再翻一遍 Github,尤其是本地查询的时候。
- 在 pull request 的提交信息界面写
- 右侧的 reviewers 和 labels 根据项目不同而不同,需要看对应项目怎么做。(一般这种需要看情况的事情,看其他人怎么做,看已经合并的 pr 是怎么做的就好)
这样,一个 pull reqeust 就提交成功了,其他人就可以看到这个代码提交了,大家就可以进行代码 review 了。
一个已提交的 pull request 长这样:
对于代码审核,主要看测试是否通过,(图中的 Checks 部分)以及具体的代码修改(图中 7 部分)
这部分界面如何操作,GitHub 的官方文档写的很完整了 https://docs.github.com/zh/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/reviewing-proposed-changes-in-a-pull-request 和 https://docs.github.com/zh/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/commenting-on-a-pull-request
只需要补充几点:
- 对任何有疑问的地方,大胆的用"+",写 comment
- 在解决其他人的疑问后,及时关闭 comment,点"Resolve conversation",目前没有定论,应该是 reviewer 还是 committer 点这个按钮。
- 代码 review 不是 reviewer 说的就一定对,可以 battle,真理不一定在谁那边,但是合并代码的权限在写入者这里,另一方面,坚持错误的东西很可能会被任何看到代码的人反复鞭尸哦。
一般而言,我们的代码在 code review 后会需要修改,这时,有 2 个策略:创建一个新的 commit 来修复 reviewer 提到的地方,或者修改代码后使用 git commit --amend
强行覆盖旧的 commit 再强制推送 git push --force
创建新的 commit 的好处是明显的标明了做了哪些改动,方便 review,但是可能需要在最后 squash commit,我们应该尽量让 git 历史干净整洁。
直接使用强制推送的话,在界面上会显示一个图标,可以用来比较(但不是所有的强制推送都会显示!)
压缩 commit,实际的流程如下:(来自 kubernetes https://www.kubernetes.dev/docs/guide/github-workflow/#squash-commits )
先检查当前状态:
$ git status
输出应该类似于:
On branch your-contribution
Your branch is up to date with 'origin/your-contribution'.
然后使用 git rebase -i HEAD~<n>
命令,表示要修改的 commit 数量
git rebase -i HEAD~3
输出类似于
pick 2ebe926 Original commit
pick 31f33e9 Address feedback
pick b0315fe Second unit of work
# 变基 7c34fc9..b0315ff 到 7c34fc9 (3 个提交)
#
#
# 命令:
# p, pick <提交> = 使用提交
# r, reword <提交> = 使用提交,但编辑提交说明
# e, edit <提交> = 使用提交,但停止以便修补提交
# s, squash <提交> = 使用提交,但挤压到前一个提交
# f, fixup [-C | -c] <提交> = 类似于 "squash",但只保留前一个提交
# 的提交说明,除非使用了 -C 参数,此情况下则只
# 保留本提交说明。使用 -c 和 -C 类似,但会打开
# 编辑器修改提交说明
# x, exec <命令> = 使用 shell 运行命令(此行剩余部分)
# b, break = 在此处停止(使用 'git rebase --continue' 继续变基)
# d, drop <提交> = 删除提交
# l, label <label> = 为当前 HEAD 打上标记
# t, reset <label> = 重置 HEAD 到该标记
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . 创建一个合并提交,并使用原始的合并提交说明(如果没有指定
# . 原始提交,使用注释部分的 oneline 作为提交说明)。使用
# . -c <提交> 可以编辑提交说明。
# u, update-ref <引用> = 为引用 <ref> 设置一个占位符,以将该引用更新为此处的新提交。
# 此 <引用> 在变基结束后更新。
#
# 可以对这些行重新排序,将从上至下执行。
#
# 如果您在这里删除一行,对应的提交将会丢失。
#
# 然而,如果您删除全部内容,变基操作将会终止。
上面写的很清楚,默认是 pick,可以改成 s 来压缩这个 commit,如下修改:
pick 2ebe926 Original commit
squash 31f33e9 Address feedback
pick b0315fe Second unit of work
输出类似于:
[detached HEAD 61fdded] Second unit of work
Date: Thu Mar 5 19:01:32 2020 +0100
2 files changed, 15 insertions(+), 1 deletion(-)
...
Successfully rebased and updated refs/heads/master.
然后 git push --force
强制推送即可。
如果能本地测试,优先测试完再提交 pr 能节省很多代码 review 的时间。
或者很多项目都可以蹭线上环境的测试。(比如 kubernetes),但是要标为 draft,这样其他人就不会被你这个 pr 的改动打扰而收到邮件通知,你也可以尽情的修改直到测试通过,再去掉 draft 的标记。见 Github 官方文档描述
在修改方案一致的前提下,不要开多个 pr,这样不利于 code review,同样的代码可以方便的新建多个 pr,但是代码的 review 意见不会自动迁移到新的 pr 上,其他人会花更多时间来了解上下文。
很多开源项目就是不活跃,有写权限的人(作者或者协作者)可能就是很多天才上一次号,就是会积压很多 issue 和 pr;
但是也有一个情况是,你可能需要通过其他方式,邮件,slack,twitter 等等礼貌的询问一下作者的意见。很可能结果就会不一样。
我发现这点很难,除去本身动机的问题。很可能翻译软件或者对英语的掌握不够,容易写出语意正确,但是不礼貌的句子,另一方面是接收者有可能也不是 native...他们的翻译软件也不一定靠谱啊!
然后很多时候,你认为加个表情可以让对话没有那么严肃,然而消息的接受者不一定是什么文化背景,什么年龄的人,他们往往不一定能 get 到这个表情的含义。谨慎加表情符号 😊
很多项目要求任何 pr 都要有 issue,这略显呆板,但是某种程度上是有道理的。虽说代码可以自解释,但是代码一般只能解释 how,不能解释 why 和 what,更不能比较各种备用方案。(这也是目前 AI 生成 commit message 比较差的一点)。有的社区会要求写 proposal,先写点文档类的东西来解释一下你想做什么,大家在 issue/slack/社区会议/邮件里充分讨论之后,再开始干活。
千万不要直接写一个很大的 feature 扔到社区,这样会导致很难 review,大家不知道你这个功能的背景,目的和边界。kubernetes 就有一个例子,有个哥们扔了一个大的功能上去,但是完全没有 issue 和 proposal,那个 pr 放在那里好久了,虽然这个哥们似乎一直在更新这个 pr,但是就是没有人审核,更无法合并到上游。
这点针对提交 pr,如果当前的改动能后序用工具自动化就更好了;如果当前的更新针对的问题在项目中多个位置存在,能一并修改就更好了。
一般来说,社区用这两个标签来鼓励新人参与项目,Good First Issue
更新手一些,一个例子:kubernetes/kubernetes#68231
bytebytego 有个图说明了这个问题,如果看图不理解的话,建议看他的免费的 4 分钟的视频
一种方法当然是提交 commit,让 github action 或者其他工具触发测试,不过这种方式比较慢,而且很多时候不能保留测试失败现场。 如果希望保留测试现场,一个方法是修改 github action 文件,增加 ssh to github action 的步骤,比如:
- https://github.com/marketplace/actions/debugging-with-tmate
- https://github.com/marketplace/actions/debugging-with-ssh
- https://github.com/valeriangalliat/action-sshd-cloudflared
另一种方法就是使用远程的环境,直连各种本地需要各种方法才能连接的服务:
- github codespaces 2-core 每月 60 小时免费额度。
- gitpod 每月 50 小时免费额度。
因为 reviewer 会反复收到推送了新的 commit,测试没过的提醒消息...
使用科学上网,或者试试这个项目 通过修改 Hosts 解决国内 Github 经常抽风访问不到,每日更新