Git 合并分支

在日常开发工作中,人人都会使用git merge 命令来合并分支。但几乎没有人会关心合并分支的细节,本文将详细讲解git mergegit rebase命令的细节以及使用场景。

git merge

日常开发中,经常使用 git merge ,有三种场景不一样。

1. 快进 - 无冲突

假设分支master有三次提交记录:B0、B1、B2

B0---B1---B2(master)


因为需要紧急修复线上问题,于是新建了分支fix,并且在分支fix上提交了两次提交记录:B3、B4

            B3---B4(fix)
           /
B0---B1---B2(master)


当在分支fix上的工作结束,切换到分支master,然后把分支fix合并到分支master:

git merge fix

Updating 4f6316b..2c975d7
Fast-forward
 footer.js | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 footer.js

由于分支fix最新提交记录(B4)是master分支最新提交记录(B2)的后继,因此Git会直接将指针向前移动(由B2移动到B4)。换句话说,当试图合并两个分支时,如果顺着一个分支走下去能够达到另一个分支,那么Git在合并两者的时候,只会简单的将指针向前推进(指针右移),因为这种情况下的合并操作没有需要解决的分歧————这就叫做“快进”(fast-forward)。


合并结果如下:

B0---B1---B2---B3---B4(master)


合并分支fix之后,已经不再需要该分支,可以删除该分支了。

git branch -d fix
tips: 当不想使用快进合并时,使每次合并都有一个总结性的提交记录,可以使用git merge --no-ff

2. 非“快进”,修改不同文件。(无冲突)

假设分支master有三次提交记录:B0、B1、B2

B0---B1---B2(master)


因为需要紧急修复线上问题,于是新建了分支fix,并且在分支fix上提交了两次提交记录(与分支master代码无冲突):B3、B4

            B3---B4(fix)
           /
B0---B1---B2(master)


当在分支fix上开发时,另一位开发人员开发了新功能,并且已经合并入分支master,添加了提交记录(与分支master、分支fix代码都无冲突):B5

            B3---B4(fix)
           /
B0---B1---B2---B5(master)


当在分支fix上的工作结束,切换到分支master,然后把分支fix合并到分支master:

git merge fix

Merge made by the 'ort' strategy.
 footer.js | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 footer.js


由于分支master最新提交B5不是分支fix最新提交B4的直接祖先,所以Git无法做“快进”(fast-forward)合并。

Git会使用两个分支的最新提交(B4和B5)以及这两个分支的最新公共提交(B2),做一个简单的三方合并。并且把合并结果在分支master上创建一个新的提交(B6)。提交B3和B4的修改内容都体现在提交B6。

            B3----B4(fix)
           /        \
B0---B1---B2---B5---B6(master)


合并分支fix之后,已经不再需要该分支,可以删除该分支了。

git branch -d fix

3. 非“快进”,修改相同文件。(有冲突)

假设分支master有三次提交提交记录:B0、B1、B2

B0---B1---B2(master)


因为需要紧急修复线上问题,于是新建了分支fix,并且在分支fix上提交了两次提交记录:B3,B4

            B3---B4(fix)
           /
B0---B1---B2(master)


当在分支fix上开发时,另一位开发人员开发了新功能,并且已经合并入分支master,添加了提交记录(与分支fix代码有冲突):B5

            B3---B4(fix)
           /
B0---B1---B2---B5(master)


当在分支fix上的工作结束,切换到分支master,然后把分支fix合并到分支master:

git merge fix

Auto-merging footer.js
CONFLICT (add/add): Merge conflict in footer.js
Automatic merge failed; fix conflicts and then commit the result.

由于分支master最新提交B5与分支fix最新提交B4存在修改同一个文件的同一处代码,所以Git无法自动合并,需要开发者自己解决冲突并且提交结果。

打开有冲突的文件,Git已经标记出代码冲突:

<<<<<<< HEAD
B5
=======
B3
B4
>>>>>>> fix

手动整理该部分代码,解决冲突。

B3
B4
B5

添加该文件到暂存区,随后添加到分支master。

git add footer.js
git commit -m 'B6'

合并结果如下:

            B3----B4(fix)
           /        \
B0---B1---B2---B5---B6(master)


合并分支fix之后,已经不再需要该分支,可以删除该分支了。

git branch -d fix

git rebase

当本地分支合并远程分支时,在无法快进合并时,就会产生一个合并提交记录。该合并提交记录,并非开发时产生的,而是合并流程中产生的。为了让分支的提交记录看起来连贯无分叉,rebase合并方式出来了。

介绍

改变当前分支的基底,并且把差异代码应用在新的基底上。从而实现分支提交记录的无分叉效果。

命令git rebase

具体步骤:

  1. 找到当前分支(fix)与被合并分支(master)之间的最新公共祖先提交记录(B2)。

             B3---B4(fix)
            /
    B0---B1---B2---B5(master)
  2. 在分支fix中,从提交记录B2之后的提交记录B3开始到最新的提交记录B4都取消掉,并且把这些提交记录临时保存为补丁(patch)。

    B0---B1---B2(fix)
  3. 把分支fix更新为分支master,最后把patch应用到分支fix上,得到提交记录B3和B4(与提交分支B3和B4的commit id不一样)。

    B0---B1---B2---B5(fix)
    
    B0---B1---B2---B5---B3`---B4`(fix)

通过对比git merge可以看出,git rebase合并结果无分叉。

使用场景

由于git rebase合并命令会改变当前分支的基底。因此对多人使用的公共分支,为了避免已提交的记录被改变,不建议使用git rebase改变分支的基底。

本地分支合并远程分支

当多个开发者在一个分支上开发,在合并远程分支时。为了避免产生多余的合并提交记录,可以使用git rebase变更本地分支的基底,再把本地开发的提交记录逐个添加到新基底上。

B0---B1---B2---B5(origin/feature-1)

B0---B1---B2---B3---B4(local/feature-1)

应用git pull origin feature-1 --rebase之后:

B0---B1---B2---B5---B3`---B4`(local/feature-1)
tips: 当合并有冲突时,需要先解决冲突、git add、git commit,然后再执行git rebase --continue继续合并流程。

本地分支合并公共分支

本地的开发分支在向远端主分支提交merge request之前,为了避免产生多余的合并提交记录, 可以使用git rebase变更本地分支的基底,再把本地开发的提交记录逐个添加到新基底上。

B0---B1---B2---B5(origin/master)

B0---B1---B2---B3---B4(local/feature-1)

应用git rebase master之后:

B0---B1---B2---B5---B3`---B4`(local/feature-1)

总结

  1. 当分支master之类的主干分支合并其他分支时,需要针对每次的合并有一个总结性的提交记录,可以使用命令git merge --no-ff
  2. 当本地开发分支合并公共分支之前,可以使用命令git rebase ,改变本地分支的基底,避免产生非必要的合并提交记录。

你可能感兴趣的:(gitmergerebase)