假设当前从 master 分别切出了两个分支:learn-rebase 和 learn-merge,它俩分别都进行了两次 commit,咱们但愿把两个分支的内容都合并到 master 上html
使用 merge 能够合并多个历史记录的流程。git
首先咱们合并 learn-merge 分支,只看蓝色的部分,learn-merge 分支与 master 分支的最新 commit 在同一条线上,这说明 learn-merge 分支的历史记录包含 master 分支全部的历史记录,那么这个合并是很是简单的。只须要经过把 master 分支的位置移动到 learn-merge 的最新分支上,Git 就会合并。这样的合并被称为 fast-forward(快进)合并。(fast-forward 模式下是不可能出现冲突的)vim
操做以下:segmentfault
➜ git branch learn-merge learn-rebase * master ➜ git merge learn-merge Updating 594a6c9..c2b4ae3 Fast-forward myfile.txt | 2 ++ 1 file changed, 2 insertions(+)
结果以下:bash
接下来须要合并 learn-rebase 分支,此时能够看到 master 的最新 commit 已经和 learn-merge 保持一致了,与 learn-rebase 分支不在一条直线上,那此时就不能进行 fast-forward 合并了。app
在这种状况下使用 merge 方法,能够合并两个修改并生成一个新的提交。(此时可能会出现冲突的状况,由于 learn-rebase 分支可能与 learn-merge 分支修改同一个文件)工具
操做以下:this
➜ git merge learn-rebase Auto-merging myfile.txt CONFLICT (content): Merge conflict in myfile.txt Automatic merge failed; fix conflicts and then commit the result. ➜ vim myfile.txt # 解决冲突 ➜ git add myfile.txt ➜ git commit -m 'fix conflicts' [master 2a6899b] fix conflicts
结果以下:spa
执行合并时,若是设定了non fast-forward 选项,即便在可以 fast-forward 合并的状况下也会生成新的提交并合并。3d
设置方法:添加 -no-ff
选项
与 merge 会保留修改内容的历史记录不一样,rebase 是在原有提交的基础上将差别内容反映进去。让咱们回到 merge learn-rebase 以前的样子。
rebase 的步骤分为两步,第一步将 learn-rebase 分支的历史记录添加在 master 分支的后面。这个时候可能会有冲突,当出现冲突时,解决冲突后的提交不是使用 commit
命令,而是执行 rebase
命令指定 --continue
选项。若要取消 rebase,指定 --abort
选项。
操做以下:
➜ git checkout learn-rebase Switched to branch 'learn-rebase' ➜ git rebase master First, rewinding head to replay your work on top of it... Applying: add rebase Using index info to reconstruct a base tree... M myfile.txt Falling back to patching base and 3-way merge... Auto-merging myfile.txt CONFLICT (content): Merge conflict in myfile.txt error: Failed to merge in the changes. Patch failed at 0001 add rebase The copy of the patch that failed is found in: .git/rebase-apply/patch When you have resolved this problem, run "git rebase --continue". If you prefer to skip this patch, run "git rebase --skip" instead. To check out the original branch and stop rebasing, run "git rebase --abort". 1 merge 与 rebase 的差异 ➜ vim myfile.txt # 解决冲突 ➜ git add myfile.txt ➜ git rebase --continue Applying: add a new line
结果以下:
而后再使用 fast-forward 合并便可
➜ git checkout master Switched to branch 'master' ➜ git merge learn-rebase Updating 0b9e66f..1fc71b9 Fast-forward myfile.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
对比 merge 和 rebase 最终的历史记录,能够发现 merge 保持了修改内容的历史记录,可是历史记录会很复杂;而 rebase 后的历史记录简单,是在原有提交的基础上将差别内容反映进去。
奇妙的变基也并不是天衣无缝,要用它得遵照一条准则:
不要对在你的仓库外有副本的分支执行变基。
变基操做的 实质是丢弃一些现有的提交,而后相应地新建一些内容同样但实际上不一样的提交。 若是你已经将提交推送至某个仓库,而其余人也已经从该仓库拉取提交并进行了后续工做,此时,若是你用 git rebase 命令从新整理了提交并再次推送,你的同伴所以将不得再也不次将他们手头的工做与你的提交进行整合,若是接下来你还要拉取并整合他们修改过的提交,事情就会变得一团糟。
到底该使用 merge 方式开发仍是使用 rebase 方法开发是有争议的
有一种观点认为,仓库的提交历史便是 记录实际发生过什么。 它是针对历史的文档,自己就有价值,不能乱改。 从这个角度看来,改变提交历史是一种亵渎,你使用谎话掩盖了实际发生过的事情。 若是由合并产生的提交历史是一团糟怎么办? 既然事实就是如此,那么这些痕迹就应该被保留下来,让后人可以查阅。
另外一种观点则正好相反,他们认为提交历史是 项目过程当中发生的事。 没人会出版一本书的初版草稿,软件维护手册也是须要反复修订才能方便使用。 持这一观点的人会使用 rebase 及 filter-branch 等工具来编写故事,怎么方便后来的读者就怎么写。
总的原则是,只对还没有推送或分享给别人的本地修改执行变基操做清理历史,从不对已推送至别处的提交执行变基操做,这样,你才能享受到两种方式带来的便利。
若是使用 merge 进行合并,可使用 revert 命令对 merge 的内容进行撤销操做(参考 revert),而使用 rebase 则不行(已经没有 merge commit 了),而须要使用 rebase -i
对提交进行从新编辑(参考 交互式 rebase)。