Git合并策略之rebase v.s. merge
git rebase
和git merge
是我们日常开发中使用的两种分支合并策略,最近团队也在代码库的分支管理上有一些要求,所以记录一下这两种合并策略的优缺点和使用场景,便于日常的代码库管理。
概述
关于git rebase
和git merge
,两者所处理的问题是相同的,就是将一个分支上的改动集成到另一个分支上,区别在于他们实现的方式不同。
考虑如下的工作场景:
你要在当前项目开发一个新的功能点,于是基于main分支拉出了一个feature分支开始进行开发。而与此同时你的同事开发了另一个新的功能or修复了一个bug并且合并到了main分支,于是main分支在你开发的过程中也有了新的提交,这就导致分支的提交历史开始分叉。
当feature分支开发完成要合入main分支时,就需要考虑使用merge还是rebase进行合入。
Merge
merge选项是最简单的合入方式,当我们要将feature分支合入main分支时,只需要首先在本地切换到main分支,然后执行git merge
命令即可:
git checkout main
git merge feature
也可以使用一行命令完成:
git merge main feature
之后将本地的修改通过git push
推送到远端仓库即可。
执行git merge
后会在main分支产生一个“merge commit
”,分支状态如下:
merge的好处在于它是一个非破坏性的操作。因为在执行merge后原本的分支(此处指feature分支)并没有被改变。
相对的,merge的缺点就在于每次合并分支都会产生一个无关的合并提交。如果提交的次数较多,这些无关的合并提交会污染分支的历史记录,在多人协作时可能会让人难以理解项目的提交历史。
Rebase
和merge选项相似,我们也可以使用rebase命令将feature分支合并到main分支上, 使用如下命令:
git checkout feature
git rebase main
rebase命令会将整个feature分支以main分支的顶端(head所在的提交)为基准,将所有的新的提交合入。但是,和merge commit不同,rebase命令会通过为每次提交创建全新的提交来重写整个项目的历史。
此时feature分支的本地状态如下:
通过rebase操作后,我们本地的feature分支上的提交都是基于main分支的提交后生成的,此时再切换到main分支执行merge操作:
git checkout main
git merge feature
由于rebase后的feature分支和main分支不会有冲突,因此直接执行fast-forward
式的merge,不会产生merge commit
,再执行git push推送到main分支的远端即可。
rebase的最大好处就是分支的提交历史会非常清晰。首先它不会产生不必要的merge commit,其次分支的提交历史不会产生分叉,只有一条十分清晰的线性历史,可以清楚的看到每次提交。
但是rebase的特性也造成了一些风险。必须要牢记的一点就是,不要在公共分支上执行rebase操作!在多人合作的分支上执行rebase操作很有可能将分支历史重写,甚至将其他人提交的代码丢失,这是很危险的操作。因此rebase前一定要好好确认。
其次,rebase不像merge操作会保留合并时的上下文,实际上rebase后产生的提交已经和原本的提交不同(commit id是不同的),提交顺序也很可能产生错乱,这时就没办法判断合并时的上下文。如果我们的代码库主干分支中一直使用rebase进行合并,那么就很难判断某个版本上线进行了哪些代码更改。
冲突解决
多人协作中,最常遇见的问题就是合并过程中产生的冲突。
开发时修改文件,可能产生以下三种情况:
- A和B修改了不同文件的代码;
- A和B修改了同一个文件的代码,但不是同一个部分的代码;
- A和B修改了同一个文件同一个部分的代码;
对于前两种情况,git会直接进行自动的合并,不会引起冲突。需要解决冲突的是第三种情况。
在使用git merge
时,产生冲突后,我们需要针对冲突文件进行判断修改,保存后再依次执行git add
、git commit
和git push
命令即可。只需解决一次冲突就可以完成merge操作。
git add filename
git commit -m "commit"
git push
而在使用git rebase
时,如果产生冲突,则需要解决冲突保存文件后执行:
git add filename
git rebase --continue
而此时也有可能执行git rebase --continue
后又一次的产生冲突。那么我们就需要继续解决冲突(重复以上的步骤)直到不再产生冲突,就可以提交代码了。
总结
git merge
和git rebase
的对比如下:
优点 | 缺点 | |
---|---|---|
git merge | 保留了原始分支的历史/保留了合并操作的上下文/只需解决一次冲突 | 产生无关的merge commit,次数较多会污染提交历史 |
git rebase | 提交历史清晰,保留线性提交历史 | 没有合并上下文,commit id和提交顺序被修改,出现问题难以定位/可能需要多次解决冲突 |
两者各有优缺点,所以我们应该根据团队的规模、开发模式来选择不同的策略。个人认为,多人协作时每个人都应该拉出属于自己的feature分支进行开发,开发完毕后通过rebase
统一合入开发环境的分支。因为开发过程中分支是比较活跃的(通常会很频繁的合入代码、部署dev环境等等),rebase合入能够保证每个功能的完整性,并且保持线性历史,同时也不用太担心操作失误的风险。
而在进行提测、上线等节点的操作时,建议使用merge
操作,可以留下合并分支的上下文,清楚的知道本次提测or上线操作所产生的所有变更,当出现问题时也更方便溯源。
转载自:https://juejin.cn/post/7010590036387168286