Skip to content

Instantly share code, notes, and snippets.

@Abirdcfly
Last active January 18, 2024 02:08
Show Gist options
  • Save Abirdcfly/b942d4e9d44f3fb71344b92b979c8d40 to your computer and use it in GitHub Desktop.
Save Abirdcfly/b942d4e9d44f3fb71344b92b979c8d40 to your computer and use it in GitHub Desktop.
新手如何使用git / github 参与代码协作 How to use git / github for newbies to participate in code collaboration

新手如何使用 git/github 参与代码协作

1. fork 代码仓库

以 kubernetes 仓库为例,点击这个 fork 按钮,在下一步中选择 Create Fork 即可。

CleanShot 2023-08-03 at 10 24 22

会打开:

CleanShot 2023-08-03 at 10 26 19

这样,你就有一个仓库可以自由折腾。不会影响到上游仓库。

2. (可选)认领任务 (issue)

依据仓库的规模和合作方式,开源社区的仓库很多建议(不强制)先认领任务再开发。这样有助于大家异步开发保持合作,其他成员可以看到这个任务有人认领,一般不会再投入时间和精力重复开发。

以 etcd 库为例:

etcd-io/etcd#16343 CleanShot 2023-08-03 at 10 31 32

可以随意写一些内容标明你要做这个任务,然后有权限的用户可以 assign 这个任务给你,然后就可以开发了。

从开源社区来说,约定俗成的规矩是:标有 "good first issue"的任务适用于新手前几次参与项目,熟悉项目的开发者不应该抢这些简单的任务。

3. 本地开发,提交代码

一个典型的 git 工作流见 kubernetes 的工作流,相关描述在 https://www.kubernetes.dev/docs/guide/github-workflow/

3.1 git clone

先将代码 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

3.2 git checkout

一般建议使用新的分支来写功能,这样哪怕同时做着几件事也可以在不同的分支里来回切换,互不干扰:

git fetch upstream
git checkout main
git rebase upstream/main
git checkout -b myfeature

然后写代码......

3.3 git status

通过 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"

3.4 git add

通过 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

3.5 git commit

通过 git commit 创建一个 commit,会根据情况弹出一个编辑界面,让你填写 commit 的内容: CleanShot 2023-08-03 at 11 12 30

3.5.1 commit message

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 是保存。

一些参考链接:

3.5.2 commit 一些原则

还是来自 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 中。

3.6 git push

然后就可以使用 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

4. 创建 pull request

Github 官方说明文档在 https://docs.github.com/zh/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request

可以在很多页面打开 pull request 的界面,实际只要是 https://github.com/kubebb/core/compare/main...Abirdcfly:core:main 这种链接就可以(kubebb/core 是上游仓库,main 是上游分支,Abirdcfly:core:main 是 fork 的仓库和分支),我习惯在自己 fork 的仓库打开 pull reqeust 界面:

CleanShot 2023-08-03 at 13 50 01

会打开:

CleanShot 2023-08-03 at 13 51 25

在上面的图中:

  • 一般自己 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,尤其是本地查询的时候。
  • 右侧的 reviewers 和 labels 根据项目不同而不同,需要看对应项目怎么做。(一般这种需要看情况的事情,看其他人怎么做,看已经合并的 pr 是怎么做的就好)

这样,一个 pull reqeust 就提交成功了,其他人就可以看到这个代码提交了,大家就可以进行代码 review 了。

5. 参与 code review

一个已提交的 pull request 长这样:

CleanShot 2023-08-03 at 14 28 28

对于代码审核,主要看测试是否通过,(图中的 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-requesthttps://docs.github.com/zh/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/commenting-on-a-pull-request

只需要补充几点:

  1. 对任何有疑问的地方,大胆的用"+",写 comment
  2. 在解决其他人的疑问后,及时关闭 comment,点"Resolve conversation",目前没有定论,应该是 reviewer 还是 committer 点这个按钮。
  3. 代码 review 不是 reviewer 说的就一定对,可以 battle,真理不一定在谁那边,但是合并代码的权限在写入者这里,另一方面,坚持错误的东西很可能会被任何看到代码的人反复鞭尸哦。

5.1 修改代码

一般而言,我们的代码在 code review 后会需要修改,这时,有 2 个策略:创建一个新的 commit 来修复 reviewer 提到的地方,或者修改代码后使用 git commit --amend 强行覆盖旧的 commit 再强制推送 git push --force

创建新的 commit 的好处是明显的标明了做了哪些改动,方便 review,但是可能需要在最后 squash commit,我们应该尽量让 git 历史干净整洁。

直接使用强制推送的话,在界面上会显示一个图标,可以用来比较(但不是所有的强制推送都会显示!) CleanShot 2023-08-03 at 15 20 43

5.2 git squash

压缩 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 强制推送即可。

6. 其他建议

6.1 如果可能,请本地测试后才提交 pr,或者将 pr 标为 draft。

如果能本地测试,优先测试完再提交 pr 能节省很多代码 review 的时间。

或者很多项目都可以蹭线上环境的测试。(比如 kubernetes),但是要标为 draft,这样其他人就不会被你这个 pr 的改动打扰而收到邮件通知,你也可以尽情的修改直到测试通过,再去掉 draft 的标记。见 Github 官方文档描述

6.2 不要同样的代码开多个 pull request

在修改方案一致的前提下,不要开多个 pr,这样不利于 code review,同样的代码可以方便的新建多个 pr,但是代码的 review 意见不会自动迁移到新的 pr 上,其他人会花更多时间来了解上下文。

6.3 当一个 pr 没有动静时,发挥主观能动性

很多开源项目就是不活跃,有写权限的人(作者或者协作者)可能就是很多天才上一次号,就是会积压很多 issue 和 pr;

但是也有一个情况是,你可能需要通过其他方式,邮件,slack,twitter 等等礼貌的询问一下作者的意见。很可能结果就会不一样。

6.4 保持礼貌

我发现这点很难,除去本身动机的问题。很可能翻译软件或者对英语的掌握不够,容易写出语意正确,但是不礼貌的句子,另一方面是接收者有可能也不是 native...他们的翻译软件也不一定靠谱啊!

然后很多时候,你认为加个表情可以让对话没有那么严肃,然而消息的接受者不一定是什么文化背景,什么年龄的人,他们往往不一定能 get 到这个表情的含义。谨慎加表情符号 😊

6.5 不要在没有沟通的时候扔一个大 Feat 上去

很多项目要求任何 pr 都要有 issue,这略显呆板,但是某种程度上是有道理的。虽说代码可以自解释,但是代码一般只能解释 how,不能解释 why 和 what,更不能比较各种备用方案。(这也是目前 AI 生成 commit message 比较差的一点)。有的社区会要求写 proposal,先写点文档类的东西来解释一下你想做什么,大家在 issue/slack/社区会议/邮件里充分讨论之后,再开始干活。

千万不要直接写一个很大的 feature 扔到社区,这样会导致很难 review,大家不知道你这个功能的背景,目的和边界。kubernetes 就有一个例子,有个哥们扔了一个大的功能上去,但是完全没有 issue 和 proposal,那个 pr 放在那里好久了,虽然这个哥们似乎一直在更新这个 pr,但是就是没有人审核,更无法合并到上游。

6.6 能否自动化,能否批量处理?

这点针对提交 pr,如果当前的改动能后序用工具自动化就更好了;如果当前的更新针对的问题在项目中多个位置存在,能一并修改就更好了。

6.7 Help Wanted 和 Good First Issue 标签

一般来说,社区用这两个标签来鼓励新人参与项目,Good First Issue 更新手一些,一个例子:kubernetes/kubernetes#68231

6.8 Git Merge vs. Rebase vs. Squash Commit

bytebytego 有个图说明了这个问题,如果看图不理解的话,建议看他的免费的 4 分钟的视频

6.9 很多测试本地因为网络问题,因为配置问题过不去?

一种方法当然是提交 commit,让 github action 或者其他工具触发测试,不过这种方式比较慢,而且很多时候不能保留测试失败现场。 如果希望保留测试现场,一个方法是修改 github action 文件,增加 ssh to github action 的步骤,比如:

另一种方法就是使用远程的环境,直连各种本地需要各种方法才能连接的服务:

6.10 将未完成的代码标记为草稿是一种礼貌

因为 reviewer 会反复收到推送了新的 commit,测试没过的提醒消息...

6.11 因为网络关系,时不时访问 github 超时?

使用科学上网,或者试试这个项目 通过修改 Hosts 解决国内 Github 经常抽风访问不到,每日更新

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment