目录

如何提交一个优雅的pr

如何提交一个优雅的pr

暑假参加了2025OSPP的活动,有幸参与了apache/fory的开源项目的开发活动,也因此在实践中学习到了如何在GitHub上参与一个开源项目

在GitHub上你想要给开源项目提交你的代码,就需要提交pr,也就是所谓的 pull request。

1. fork 仓库

首先需要将这个项目fork到你的主页,注意:后续所有的操作都是基于你仓库内的这个派生的代码库展开的。

/%E5%A6%82%E4%BD%95%E6%8F%90%E4%BA%A4%E4%B8%80%E4%B8%AA%E4%BC%98%E9%9B%85%E7%9A%84pr/images/image-20250923145650118.png
image-20250923145650118

拉取的项目的main分支不要动,因为我们要靠这个分支与原仓库做同步。

从优雅简洁的角度来看,每次你想要提交一个pr,都请先在你的这个派生的代码库中新建一个分支:

/%E5%A6%82%E4%BD%95%E6%8F%90%E4%BA%A4%E4%B8%80%E4%B8%AA%E4%BC%98%E9%9B%85%E7%9A%84pr/images/image-20250923150223563.png
image-20250923150223563

分支名最好和本次提交的主题相关,这样方便分辨哪个分支是干什么的,比如我这次需要提交一个新特性:让go语言的代码生成部分支持slice,那么我就可以新建一个叫做feat-slice的分支。

GitHub里的派生的代码库不会默认进行同步,如果你派生的代码库落后于最新的原始仓库,那么这里就会有提示,点击 Sync fork 按钮即可 Update branch。(注意我们每次只要更新main分支就好,保持main分支是最新的就行)

/%E5%A6%82%E4%BD%95%E6%8F%90%E4%BA%A4%E4%B8%80%E4%B8%AA%E4%BC%98%E9%9B%85%E7%9A%84pr/images/image-20250923150446976.png
image-20250923150446976

2. 本地开发

本地开发始终记住:永远不要在main分支上修改提交commit!!!每次开发都请和远程派生的代码库一样,各自新建一个分支来开发,pr提交合并结束记得删除本地相关分支和派生代码库的相关分支。

这里的好处是非常非常大的,也是我本地踩过很多次坑发现的:因为GitHub提交pr的特点是,pr中可能有10个commit,最终如果owner approve 你的合并,那也只会将这个pr的内容作为一个整体commit提交上去。所以如果你在main分支上直接开发,就会出现你的本地commit多于远程代码库的main分支,这显然是有问题的,两边git历史对不上。所以这就是为什么我说的永远不要在main分支上直接修改提交,新建新的分支开发完提交pr之后就可以删掉了,然后本地main分支重新git pull就可以保持完全一致!

首先先查看git历史:

1
git log --graph --oneline --decorate --all

然后切换到main分支:

1
git checkout main

新建并切换分支:

1
git checkout -b <新分支名>

下面就可以在这个新的分支上进行愉快的开发了!

3. 创建合并请求

只要你将本地的修改push到你远程的派生代码库的相关分支上,回到线上派生项目的工作区,会看到新分支和修改的合并提交信息,点击Compare & pull request:

/%E5%A6%82%E4%BD%95%E6%8F%90%E4%BA%A4%E4%B8%80%E4%B8%AA%E4%BC%98%E9%9B%85%E7%9A%84pr/images/image-20250923183009698.png
image-20250923183009698

选择你想并入的原项目分支,标题和描述信息。如果有对应的 issue,就通过键入 # 添加(Github 会自动展示 issues 列表)

然后点击Create pull request即可创建合并请求成功,之后就等待cr就行。

4. 根据评论修改你的代码

你提交的pr是需要 code owner 进行 review 的,他们认为你的代码有问题就会在cr的时候提出评论,此时你需要根据评论去修改你本地的内容,然后再提交到之前的那个远程分支上就可以自动更新pr中的commit了。

但是很多情况下会有很复杂的情况,比如给你的评论很复杂改了几天,回头发现有人先提交了一些新的pr并且合入了怎么办?

我们可以把这种情况抽象为下面的模型:

  1. 初始情况:
  • 远程
    • main: A
    • feat-slice: A
  • 本地
    • feat_slice: A
  1. 你在本地提交了 C
  • 本地 feat_slice: A–C
  • 然后你 push 到远程 feat-slice
    • main: A
    • feat-slice: A–C
  1. 远程 main 更新为 A–B
  • 远程:
    • main: A–B
    • feat-slice: A–C
  • 本地:
    • feat_slice: A–C (还没 git pull --rebase
  1. 本地 git pull --rebase 到 main

执行:git pull --rebase

结果:

  • 本地 feat_slice: A–B–C (C 被“挪”到 B 后面)
  • 远程还是:
    • main: A–B
    • feat-slice: A–C
  1. 此时 git push origin master:feat_slice 到远程 feat-slice

Git 会检查:

  • 远程 feat-slice 目前是 A–C
  • 你要推的是 A–B–C

👉 这两条历史分叉了

  • 远程的 C 在 A 后直接接,
  • 本地的 C 在 A–B 后接。

所以,Git 默认会认为这是「非快进 (non-fast-forward) 更新」,直接 push 会报错

1
! [rejected]        master -> feat_slice (non-fast-forward)
  1. 强制推送

怎么办?

如果你确认要让远程 feat-slice 变成 A–B–C,需要强制推送:

1
git push -f origin master:feat_slice

这样远程 feat-slice 就会被改写成 A–B–C。pr上也会出现force-pushed的相关显示:

/%E5%A6%82%E4%BD%95%E6%8F%90%E4%BA%A4%E4%B8%80%E4%B8%AA%E4%BC%98%E9%9B%85%E7%9A%84pr/images/image-20250923170353243.png
image-20250923170353243

5. 删除你的分支

请记住:每次提交的pr合并进去,都请删除本地和派生代码库的相关分支,然后在本地的main分支重新git pull即可,如果还需要继续开发,请回到 步骤2. 本地开发