mancuoj

mancuoj

Better late than never.
github
twitter

GitHub 合并 PR 的三种策略

一般来说,我们创建 PR 会有一个通用的工作流程:

  1. 创建一个分支,比如 feature
  2. 提交 PR
  3. 让别人 review
  4. 根据 review 意见修改
  5. 合并 PR 到主分支

但这个 feature 分支可能会有很多次提交,如何合并到主分支就成为了一个问题,GitHub 会给你三个选项:

image

Create a merge commit#

创建一个连接两个分支历史记录的 “合并提交”

image

我们可以在本地模拟图中这个过程,首先在主分支创建三个提交,使用 git log --oneline --graph 可以看到提交记录如下:

* b8472e4 (HEAD -> main) add c.txt
* 2db7159 add b.txt
* b5b52d8 add a.txt

然后在新的分支 git checkout -b feature 创建两个提交:

* 9770b82 (HEAD -> feature) add e.txt
* 4e65cc4 add d.txt
* b8472e4 (main) add c.txt
* 2db7159 add b.txt
* b5b52d8 add a.txt

切回主分支,使用命令合并:

git checkout main
git merge --no-ff feature

再次查看记录:

*   7da24fd (HEAD -> main) Merge branch 'feature' into main
|\  
| * 9770b82 (feature) add e.txt
| * 4e65cc4 add d.txt
|/  
* b8472e4 add c.txt
* 2db7159 add b.txt
* b5b52d8 add a.txt

如上,GitHub 在幕后使用 git merge --no-ff 的默认选项会创建一个连接两个分支历史记录的合并提交。

这种方法的优点就是保留了所有的历史记录以及各个时间节点,缺点就是多分支或者长期分支会一团乱麻。

Squash and merge#

将分支中的所有提交压缩替换为单个提交后合并

image

依旧来尝试本地模拟,首先 git reset --hard b8472e4 回到合并前:

* b8472e4 (HEAD -> main) add c.txt
* 2db7159 add b.txt
* b5b52d8 add a.txt

使用 git merge --squash feature 压缩合并,然后编写适当的提交消息。

最后主分支的提交记录如下:

* 52c548d (HEAD -> main) add d.txt and e.txt
* b8472e4 add c.txt
* 2db7159 add b.txt
* b5b52d8 add a.txt

可以看出这种方法的优点就是主分支历史清晰且线性,一个功能用一个提交解决,但是缺点就是丢失了合并以及各个独立提交的上下文信息,甚至没法知道这个分支到底合并没有。

另一种 Squash 的方法#

使用交互式 rebase 命令实现更细致的操作:

git checkout feature
git rebase -i main

选择 squash 将后面的提交融合到前一个提交:

pick 4e65cc4 add d.txt
squash 9770b82 add e.txt

编写适当的提交消息,此时 feature 分支提交记录变为:

* f37e60f (HEAD -> feature) add d.txt and e.txt
* b8472e4 (main) add c.txt
* 2db7159 add b.txt
* b5b52d8 add a.txt

最后回到主分支合并即可(当然你也可以用 cherry-pick 合并单独或组合的提交):

git checkout main
git merge feature

Bonus#

这种搞坏提交记录的操作怎么恢复?

git reflog
# 你会看到你做的所有 git 操作,找到搞坏的前一步 reset 即可
git reset HEAD@{index}

Rebase and merge#

所有提交都会被添加合并到主分支的 “顶部”

没图,但是可以从描述中了解到主分支最后的结果应该是:

image

恢复分支状态,然后执行以下操作:

git checkout feature
git rebase main

git checkout main
git merge feature

完成后主分支提交记录如下:

* 9770b82 (HEAD -> main, feature) add e.txt
* 4e65cc4 add d.txt
* b8472e4 add c.txt
* 2db7159 add b.txt
* b5b52d8 add a.txt

优点是主分支历史清晰线性,没有多余的合并提交,其他分支的提交历史也会直接保存在主分支记录里。

缺点与 squash 合并类似,并且更多小提交可能会影响对大局的注意力。

建议#

所有的选择都是 trade-off 的结果,应该权衡这几个关键的因素:

  • 团队偏好
  • 提交历史记录:完整还是清晰?
  • 更小的提交还是整个功能的完整提交
  • 有无长期运行的分支
  • ...

我只有一个建议:多提交,多写 PR,不要堆积代码,更小的代码片段意味着更容易的 code review,更简单的单元测试以及更符合单一职责原则的代码。

参考#

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。