git学习系列(三)---- 分支的合并(git merge)与变基(git rebase)

安卓学吧 2019-09-08

最近在学习git,对git也有了新的认识,写一些总结,文章基本总结于 Pro Git

其他git相关文章:

git学习系列(一)---- git的基础知识
git学习系列(二)---- git的分支

本章主要介绍分支的合并与变基,分支的基础知识在上一章已经讲述过了。

注:图片中的箭头方向指向的是上一次提交(父对象),所以实际的提交顺序是从左往右。

合并(git merge)

首先我们来构建一个工作流程

1.首先,我们假设你正在你的项目上工作,并且已经有一些提交

git学习系列(三)---- 分支的合并(git merge)与变基(git rebase)

2.这个时候你需要开发一个新功能,你创建分支issue53并切换至他

git checkout -b iss53

3.你修改了一些东西,并做了提交(commit)

vim index.html
 git commit -a -m 'added a new footer [issue 53]'

git学习系列(三)---- 分支的合并(git merge)与变基(git rebase)

4.这时候你发现有个线上问题需要你紧急处理,所以你先切回master,并新建一个紧急分支hotfix,在hotfix上进行了修改

git checkout master
git checkout -b hotfix
vim index.html
git commit -a -m 'fixed the broken email address'

git学习系列(三)---- 分支的合并(git merge)与变基(git rebase)
5.问题已经解决,这个时候需要合并到master上线

git checkout master
git merge hotfix

git学习系列(三)---- 分支的合并(git merge)与变基(git rebase)

在合并的时候,你应该注意到了"快进(fast-forward)"这个词。 由于当前 master 分支所指向的提交是你当前提交(有关hotfix 的提交)的直接上游,所以 git 只是简单的将指针向前移动。
换句话说,当你试图合并两个分支时,如果顺着一个分支走下去能够到达另一个分支,那么 git在合并两者的时候,只会简单的将指针向前推进(指针右移),因为这种情况下的合并操作没有需要解决的分歧——这就叫做“快进(fast-forward)”。

6.这个时候你可以返回issue53分支继续你的工作

git checkout iss53
vim index.html
git commit -a -m 'finished'

git学习系列(三)---- 分支的合并(git merge)与变基(git rebase)
你在 hotfix 分支上所做的工作并没有包含到 iss53 分支中。 如果你需要拉取 hotfix 所做的修改,你可以使用 git merge master 命令将 master 分支合并入 iss53 分支,或者你也可以等到 iss53 分支完成其使命,再将其合并回 master 分支。

7.issue53工作已经完成,你想要合并到master上

git checkout master
git merge iss53

这和你之前合并 hotfix 分支的时候看起来有一点不一样。 在这种情况下,你的开发历史从一个更早的地方开始分叉开来(diverged)。
因为,master 分支所在提交并不是 iss53 分支所在提交的直接祖先,git 不得不做一些额外的工作。
出现这种情况的时候,git 会使用两个分支的末端所指的快照(C4 和 C5)以及这两个分支的工作祖先(C2),做一个简单的三方合并。

git学习系列(三)---- 分支的合并(git merge)与变基(git rebase)

和之前将分支指针向前推进所不同的是,git 将此次三方合并的结果做了一个新的快照并且自动创建一个新的提交指向它。这个被称作一次合并提交

git学习系列(三)---- 分支的合并(git merge)与变基(git rebase)

8.遇到冲突时的解决方案
上述的操作是在没有冲突的情况下进行的,git会自动为你合并,并生成一个新的快照。然而当两个不同的分支对同一文件做了不同的修改,git就无能为力了,此时 git 做了合并,但是没有自动地创建一个新的合并提交,需要你自己去解决合并后的冲突,生成新的commit,然后再提交就可以了。

git merge iss53
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.

查看已合并分支与未合并分支

git branch --merged
git branch --no-merged

变基(get rebase)

在 git 中整合来自不同分支的修改主要有两种方法:merge 以及 rebase
请回顾之前在 分支的合并 中的一个例子,你会看到开发任务分叉到两个不同分支,又各自提交了更新。

git学习系列(三)---- 分支的合并(git merge)与变基(git rebase)

你可以提取在 C4 中引入的补丁和修改,然后在 C3 的基础上应用一次。 在 Git 中,这种操作就叫做 变基。 你可以使用 rebase 命令将提交到某一分支上的所有修改都移至另一分支上,就好像“重新播放”一样。

git checkout experiment
git rebase master

原理
1.首先找到这两个分支(即当前分支 experiment、变基操作的目标基底分支 master)的最近共同祖先 C2
2.对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件
3.然后将当前分支指向目标基底 C3
4.最后以此将之前另存为临时文件的修改依序应用

现在回到 master 分支,进行一次快进合并。

git checkout master
git merge experiment

git学习系列(三)---- 分支的合并(git merge)与变基(git rebase)
此时,C4' 指向的快照就和上面使用 merge 命令的例子中 C5 指向的快照一模一样了。
这两种整合方法的最终结果没有任何区别,但是变基使得提交历史更加整洁。
你在查看一个经过变基的分支的历史记录时会发现,尽管实际的开发工作是并行的,但它们看上去就像是串行的一样,提交历史是一条直线没有分叉

假如在rebase时遇到冲突了该怎么解决

git学习系列(三)---- 分支的合并(git merge)与变基(git rebase)

git本身会给出你下一步的提示,这时候你只需要打开冲突的文件,解决完冲突之后

git add .
git rebase --contine

注:合并与变基虽然操作过程有差异,但最终你指向的快照是相同的
解决冲突时,新的修改会存储在补丁当中,即C4‘

本来还想着写一下变基可能会带来的风险与问题,但是本篇的篇幅已经有点长了,那就放在下一章讲述吧

相关推荐