问题复现与解决

在团队开发中,由于git使用的不够合理规范,开pr之后就会发现其中掺杂着若干merge的commit:

commit history

事实上我们本地的branch和远端的branch会同步地非常频繁,这两个分支几乎完全同步,所以以上的merge动作是没有必要的。并且如果一个pr中存在merge,那么pr的files changed中就会看到merge目标的代码。这会给代码 Review 的工作代码困扰。因为代码reviewm时通常是看一个pr修改了哪些代码,现在pr中满是merge目标的代码,是没办法review的。

想要解决这个问题的关键是永远不要merge主分支,而是rebase主分支。

1
2
3
git fetch origin master
# git merge origin/master # 不要这么干
git rebase origin/master
1
2
# git pull origin master # 不要这么干
git pull --rebase

主要是因为git pull这一git命令实际执行了git fetchgit merge FETCH_HEAD两条指令。因此不要直接使用git pull来更新代码。git pull --rebase指令的意思是:

  1. 把本地repo自从上次pull之后的变更暂存起来
  2. 回到上次pull时的情况
  3. 套用远端的变更
  4. 套用第一步中暂存的本地变更

关于git rebase

git rebasegit merge做的事是一样的,都是把一个分支合并到另一个分支,只是方式不同,我们应该在不同情况下做出更合理的选择。我们做一下对比。
假设合并前是这样:

D—E master
/
A—B—C—F origin/master

使用merge合并后:

D——–E
/ \
A—B—C—F—-G master, origin/master

适应rebase合并后:

A—B—C—F—D’—E’ master, origin/master

由此可见,使用rebase其实是重写了提交记录,并使我们的项目历史会非常整洁,它不想git merge那样引入不必要的合并提交,rebase使得项目历史呈线性,便于通过git log查看项目历史。

A rebase-based workflow

1.新建分支

git checkout master
git pull #更新master
git checkout master -b test #从master创建feature分支
git push -u origin test:test
#把分支push到远程,本地分支名在前,-u是–set-upstream-to 的简写,设置追踪分支

2.更新分支

从master更新

git pull –rebase origin master #此方法不会更新本地master分支

从test更新

git pull –rebase #需先设置追踪分支

3.完成后回到主分支

git checkout master
git pull #这里不会导致出现merge commit
git rebase test #把commits拉取到主分支
git push

处理脏pr

这里只介绍一个比较好理解的方法,从主分支上新建一个干净的分支,然后把需要搞干净的pr中所有非merge的commit都cherry-pick到新分支。

git fetch origin master:new-feature
git checkout new-feature
git cherry-pick COMMIT_HASH_1
git cherry-pick COMMIT_HASH_2

git cherry-pick COMMIT_HASH_N

cherry-pick命令”复制”一个提交节点并在当前分支做一次完全一样的新提交。
注意cherry-pick的顺序应是commit 从旧到新 的顺序,否则会一直出现无数冲突。

参考内容