在日常的开发工作中我们不可避免地要进行 PR,几乎每天我们都会看到这些选项,那这些是什么意思呢?
合并的代码的姿势
git merge
marge 特点:自动创建一个新的commit,如果合并的时候遇到冲突,仅需要修改后重新commit。
- 优点:记录了真实的commit情况,包括每个分支的详情
- 缺点:因为每次merge会自动产生一个merge commit,所以在使用一些git 的GUI tools,特别是commit比较频繁时,看到分支很杂乱。
git rebase
git rebase 一般被翻译为 变基,这个名字很灵性。他会找共同的父亲,然后将 commit 插入到 commit 列表中。
rebase 将所有 master 的 commit 移动到你的 feature 的顶端。问题是:其他人还在 original master上开发,由于你使用了 rebase 移动了 master ,git 会认为你的主分支的历史与其他人的有分歧,会产生冲突。
如果不是只有一个人使用,切记不要使用rebase。
rebase 特点:会合并之前的 commit 历史,很有可能会造成冲突。
- 优点:得到更简洁的项目历史,去掉了merge commit。
- 缺点:如果合并出现代码问题不容易定位,因为重写了历史。
我们要解决什么问题?
rebase 有非常严重的缺点,不适合多人协作,但是 merge 又会污染我们的commit,而且让每个人都写成完美的commit 有些不太现实。以下是 antd 的 merge 提交。
提交本质上是不可逆的,虽然我们有办法修改历史,但是在一个多人协作的库里面这样做肯定会翻车。但是写好commit 又非常重要,一个好的 commit 可以帮助我们快速定位问题。如果写的不好我们很难定位到问题所在。
小例子,这个 pr 改动了什么?
git squash merge 的优势
我们如果想要一个优秀的提交历史,又不想使用 reabse,那么 squash merge 将会是首选。
squash merge 可以让我们把多次的 commit 合并为一个提交。尤其是在 PR 中的时候更加好用。虽然写好commit 很难,但是写好一个 pr 标题其实没那么困难。
C - D - E bugfix
/
A - B - F - G master
squash merge 之后
A - B - F - G - CDE
正常 merge
A - B - F - G - C - D - E - merge_commit
以 morse 项目为例,使用 squash merge 之后能得到如下的 commit:
祥细的 commit 信息 和 PR 的id 可以让我们在寻找问题中快速定位代码变更。
在 vscode 中的显示效果:
在 code 中使用
code 中也是支持 squash 的
如果觉得每次设置麻烦可以在 设置 中只启用 squash merge
Q & A
打开了 squash 之后,不推荐写好的 commit 信息?
commit 信息在任何时候都推荐好好写,但是如果你写了错别字或者因为一时偷懒,会导致整个主分支的commit 都杂乱无章充斥着莫名其妙的信息。这里推荐增加 git 的钩子来解决问题
"gitHooks": {
"pre-commit": "pretty-quick --staged",
"commit-msg": "node ./node_modules/@umijs/fabric/dist/verifyCommit.js"
},
任何时候都不要使用 rebase 吗?
如果项目中只有你一个人,或者这个分支只有你的时候使用rebase 是很推荐的。git pull -r
也是我们个人最常用的命令之一。但是一旦有多个人或者冲突太厉害的时候。建议还是 merge 保平安。
git rebase --abo
git merge --abo
这两个命令可以让你退出复杂的冲突,然后徐徐图之。
我可不可以一个 commit 一个分支?
理论上这个是最完美的方案,但是世事总是不尽如人意。很多时候你不会想因为一个 commit 就走一个 PR 流程的。但是人一多冲突会天天发生,因为别人不可能每时每刻去 merge 迭代分支。所以只推荐 依赖更新这样干。