git merge v.s. rebase

分支的合并
分支的合并主要有两种方法:merge 和 rebase,例如 release 分支或 hotfix 分支测试完成后,最后要合并回 master 分支,使用这两种方法,合并后分支的历史记 录会有很大的差别,本文主要详细论述了这两种方法的差异以及使用场景。
merge
假设有两个分支:bugfix 分支是从 master 分支分叉出来的。下面主要分两种情况来讨论 merge 操作。
master 没有新的提交
如下图所示:

在 master 分支上执行一下命令会合并 bugfix 分支到 master分支:
git merge bugfix # 等价于 git merge --ff bugfix
如果 master 分支的状态没有被更改过,那么这个合并是非常简单的。 bugfix 分支的历史记录包含 master 分支所有的历史记录,所以通过把 master 分支的指针向前 移动 到 bugfix 的最新分支上,Git 就会合并。这样的合并被称为 fast-forward(快进)合并。

换句话说,如果顺着一个分支走下去可以到达另一个分支的话,那么 Git 在合并两者时,只会简单地把指针右移,因为这种单线的历史分支不存在任何需要解决的分歧,所以这种合并过程可以称为快进(Fast forward)。
执行合并时,如果设定了 non fast-forward 选项,即使在能够 fast-forward 合并的情况下也会生成新的提交并合并。
git merge --no-ff bugfix
如图所示:

master 也有新的提交
master 分支的历史记录有可能在 bugfix 分支分叉出去后有新的更新。这种情况下,要把 master 分支的修改内容和 bugfix 分支的修改内容汇合起来。

如果对于这种两个分支都有新的提交,主要分两种情况讨论:
master 和 bugfix 新的提交没有文件冲突
执行 git merge bugfix 会出现以下信息,可以修改 merge 的 commit message 信息。
Merge branch 'bugfix'
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
~
~
合并两个修改 会生成一个提交。这时,master分支的 HEAD 会移动到该提交上。新的合并节点 E 的 commit message 是“Merge branch 'bugfix'”,加不加 --ff 的效果其实是一样的。

master 和 bugfix 新的提交有文件冲突
如果它们都同时修改了 README.md 且导致该文件的有冲突(修改同一行包括该行的前后一行,都会出现冲突。如果连续修改该行的下一行以及下下一行,那么这些连续行也会算是同一个冲突),执行 git merge bugfix 会出现以下错误:
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.
处理冲突的时候
(1)如果用了 bugfix 的代码 或者 修改了部分代码,则处理冲突完成后,不仅需要 add ,还需要 commit 。
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)
Changes to be committed:
modified: README.md
那么将会生成一个新的合并节点,其实就是默认的 --ff 模式不能走下去,走的是 --no-ff 模式。 message 就是以上代码 commit 的 message,分支合并示意图如下图所示:

当然合并的时候你可以强制只 有 --ff 走得通的情况下才合并,否则终止合并:
git merge --ff-only bugfix
(2)如果全部用master的代码,则处理冲突完成后,只需要执行:
git add --all
add 之后看一下 status 会返回以下信息:
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)
注意这里不需要进行 commit 即可将 master 的 commit(包括 message 和 hash) 会覆盖掉 bugfix 的 commit。且无论加不加 --no-ff 合并后分支是 线性 的。其实就是还是原来的 master 分支走向。当然现实中这种情况还是比较少的。
PS: 如果中途想无害终止 merge 可以直接
git merge --abort
fast-forward 和 non fast-forward 优缺点
fast-forward:删除分支后,会丢掉分支信息,因为合并完之后的视图为线性的,看不出其他分支和交叉合并的信息,但是线性的分支会看起来更加赏心悦目,有洁癖的程序员推荐。non fast-forward:即使 bugfix 分支被删除,master 的分叉合并形状图也会保留下来。那么要查明在这个分支里的操作就很容易了。但当分支很多的时候,分支图会很乱。
git merge --squash
git merge --squash会在当前分支新建一个提交节点,无论 master 和 bugfix 是否都有新的提交,冲突处理都会增加一个新的提交节点,但最后的分支图都是