前言
本文默认你已经有了一定的git基础,并且对于分支等概念有了初步的认识
分支的合并
在实际的工作当中,由于git的特性,在仓库内会存在各种各样的分支,这些分支又是由不同的人在维护修改。这里就会涉及到分支合并的场景。
直到……
火葬场的分支(心 肺 停 止)
当然,作为开发者的我们一定是要避免这种情况的发生的,因此我们要活用分支的合并操作。
merge
在git中,我们可以使用git merge
命令来合并分支。
例如我们现在的commit树:
我们此时的HEAD 在main分支上
此时我们使用git merge dev
命令,此时commit树会变成这个样子
让我们来简单理一下发生了什么。
在merge命令发送后,git会将C2 C3 C4 C5 C6做一个简单的合并操作,然后git会自动在main分支上commit一个提交C7,让C7的祖先指向C4与C6,merge结束。
你有没有观察到什么?
merge的冲突情况
假如我们此时dev分支和main分支做出的修改是针对同一个文件呢?
在这种情况下,我们刚刚提到的合并操作会由于不知道保留何种更改而暂停操作:
$ git merge dev
Auto-merging test-1.txt
CONFLICT (content): Merge conflict in test-1.txt
Automatic merge failed; fix conflicts and then commit the result.
上述就是一个有冲突环境下的merge提示
$ git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add ..." to mark resolution)
both modified: test-1.txt
no changes added to commit (use "git add" and/or "git commit -a")
当你使用status查看状态时,git会提示你有文件目前具有冲突并没有被合并,此时你需要自行处理冲突,并手动完成最后一步commit。
而冲突会映射回源文件中:
This is test-1.
update test-1.
add test-1.
> dev
这里 HEAD 表示所指示的版本(也就是你的 master 分支所在的位置,因为你在运行 merge 命令的时候已经检出到了这个分支)在这个区段的上半部分(=======
的上半部分),而 dev 分支所指示的版本在=======
的下半部分。
你只需要解决在这些标记的地方完成合并即可,修改源文件为:
This is test-1.
update test-1.
add test-1.
test master.
test dev.
此时保存后手动提交便完成了冲突合并。
merge的好处是它将所有的commit信息完整的保留了下来,但当项目体量变得复杂且庞大时,面对很多分支和总分支合并时,你的commit树可能会变成……
rebase
另一个合并的途径便是rebase
,又称变基,它和merge不同的是它会抛弃合并的一些信息,什么意思呢?
假如我们现在有着这样的commit树:
我们来对比一下merge和rebase
使用merge:
使用rebase
此时main分支上的C3提交消失了,取而代之的是main分支上的C3'
对于rebase来说,它会将当前分支上的提交一个个取消掉,同时将它们临时保存为补丁(patch),然后main分支更新到dev分支上,接下来在main分支上应用之前保存的所有补丁完成变基流程。
对比
我们不难发现,在merge和rebase产生的两种commit树中,merge无疑是保留信息最多的那一个,它不仅会保存原commit,同时也会记录合并流程
但rebase不同,rebase会将原分支合并的commit信息丢失掉,从而保证变基后分支只保留一个,合并后的commit也不是原来的commit。
使用情景
以下的言论偏主观化。
个人认为,如果要避免灾难般的commit树,无疑rebase是最佳选择,但另一方面,merge虽然庞大繁杂,但是一目了然,方便进行问题的追踪检查。
因此在合并分支时,使用merge是最佳选择。
当从远程分支拉取合并时,使用pull与rebase能保证本地分支的简洁直观。