用 Git 进行多人协作开发时,必然会合并代码,解决冲突。然而合并代码也是需要点技巧的,如果对一些关键命令没有理解去使用的话,git 的版本演进路线就会变得很乱,从而造成了日后维护的一些麻烦。
Git 上合并代码有 git merge 以及 git rebase 两种方式。下面将深入两者的用法以及对两者的适用场景作个总结。
有了以上知识点,我们可以了解一般团队开发都是基于 feature 分支进行开发,然后把 feature 分支合并到 develop 分支的。接着我们模拟如下一个实际开发场景。
现在在 develop 开发分支上,然后你创建了一个 feature 分支开发新功能,现在团队中另一个成员在 develop 分支上添加了新的提交。如下图所示
现在,如果 develop 中新的提交和你的工作是相关的。为了将新的提交并入你的分支,你有两个选择:merge 或 rebase。
将 develop 分支合并到 feature 分支最简单的办法就是用下面这些命令:
- git checkout feature
- git merge develop
或者,你也可以把它们压缩在一行里。(个人还是喜欢上面的写法)
- git merge develop feature
feature 分支中新的合并提交 (merge commit) 将两个分支的历史连在了一起。你会得到下面这样的分支结构:
Merge 好在它是一个安全的操作。现有的分支不会被更改,避免了 rebase 潜在的缺点 (后文会讲)。但是这同样意味着每次合并上游更改时 feature 分支都会引入一个外来的合并提交。如果 master 非常活跃的话,这或多或少会污染你的分支历史。
默认情况下,Git 执行 "快进式合并"(fast-farward merge),会直接将 develop 分支指向 feature 分支。如 git merge 里的图所示。使用 --no-ff 参数后,会执行正常合并,在 develop 分支上生成一个新节点。为了保证版本演进的清晰,我们希望采用这种做法。关于合并的更多解释,请参考 Benjamin Sandofsky 的《Understanding the Git Workflow》。
Git 演进图如下图所示。(如有错误欢迎指正)
可以看到,使用了 git merge --no-ff 命令后的 git 演进路线是清晰的,命令概括如下:
- git checkout feature
- git merge --no-ff develop
先提个问题吧,git rebase 和 git reset 有什么区别? 如果不知道的话,可以在回顾一下在什么场景下用 git merge 以及 git rebase 的,而 git reset 则仅仅是在当前的分支 (一个分支) 的版本切换。
接着来讲 git rebase。作为 merge 的替代选择,你可以像下面这样将 feature 分支并入 master 分支:
- git checkout feature
- git rebase develop
它会把整个 feature 分支移动到 develop 分支的后面,有效地把所有 develop 分支上新的提交并入过来。但是,rebase 为原分支上每一个提交创建一个新的提交,重写了项目历史,并且不会带来合并提交。
rebase 最大的好处是你的项目历史会非常整洁。首先,它不像
那样引入不必要的合并提交。其次,如上图所示,rebase 导致最后的项目历史呈现出完美的线性。这让你更容易使用 git log 来查看项目历史。
- git merge
不过,这种简单的提交历史会带来两个后果:安全性和可跟踪性。如果你违反了 Rebase 黄金法则,重写项目历史可能会给你的协作工作流带来灾难性的影响。此外,rebase 不会有合并提交中附带的信息——你看不到 feature 分支中并入了上游的哪些更改。
当你理解 rebase 是什么的时候,最重要的就是什么时候 不能 用 rebase。
的黄金法则便是,绝不要在公共的分支上使用它。
- git rebase
比如说,如果你在 develop 分支上,rebase 到你的 feature 分支上会发生什么:
这次 rebase 将 develop 分支上的所有提交都移到了 feature 分支后面。问题是它只发生在你的代码仓库中,其他所有的开发者还在原来的 develop 上工作。因为 rebase 引起了新的提交,Git 会认为你的 develop 分支和其他人的 develop 已经分叉了。
同步两个 develop 分支的唯一办法是把它们 merge 到一起,导致一个额外的合并提交和两堆包含同样更改的提交。不用说,这会让人非常困惑。
所以重要的再强调一遍,绝不要在公共的分支上使用它。在你运行
之前,一定要问问你自己 "有没有别人正在这个分支上工作?"。如果答案是肯定的,重新找到一个无害的方式(如
- git rebase
)来提交你的更改。不然的话,你可以随心所欲地重写历史。
- git revert
如果你想要一个干净的、线性的提交历史,没有不必要的合并提交,你应该使用
而不是
- git rebase
来并入其他分支上的更改。
- git merge
另一方面,如果你想要保存项目完整的历史,并且避免重写公共分支上的 commit, 你可以使用
- git merge (--no-ff)。
参考文献:
来源: http://www.cnblogs.com/MuYunyun/p/6876413.html