没有财务自由,就没有思想自由。
——许小年
简单介绍一下Git协同工作中的分支操作,记录不同分支合并操作方法之异同。
在Git中合并分支有三种方法,分别是merge,rebase和cherry-pick,分别说说。
Git Merge 的三种方法
如上图所示,git merge
有如下特点:
- 只处理一次冲突
- 引入了一次合并的历史记录,合并后的所有
commit
会按照提交时间从旧到新排列 - 所有的过程信息更多,可能会提高之后查找问题的难度
为什么讲 git merge
提交的信息过多可能会影响查找问题的难度呢?因为在一个大型项目中,单纯依靠 git merge
方法进行合并,会保存所有的提交过程的信息:引出分支,合并分支,在分支上再引出新的分支等等,类似这样的操作一多,提交历史信息就会显得杂乱,这时如果有问题需要查找就会比较困难了。
fast-forward
1 | git checkout master |
如果带合并的分支在当前分支的下游,也就是说没有分叉时,会发生快速合并,从test分支切换到master分支,然后合并test分支。
这种方法相当于直接把master分支移动到test分支所在的地方,并移动HEAD指针。
fast-forward方式就是当条件允许的时候,git直接把HEAD指针指向合并分支的头,完成合并。属于“快进方式”,不过这种情况如果删除分支,则会丢失分支信息。因为在这个过程中没有创建commit。
no-ff
1 | git checkout master |
如果我们不想要快速合并,那么我们可以强制指定为非快速合并,只需加上–no-ff参数。
这种合并方法会在master分支上新建一个提交节点,从而完成合并。
squash
1 | git checkout master |
svn的在合并分支时采用的就是这种方式,squash会在当前分支新建一个提交节点。
squash和no-ff非常类似,区别只有一点不会保留对合入分支的引用。
git merge --squash
是用来把一些不必要commit进行压缩,比如说,你的feature在开发的时候写的commit很乱,那么我们合并的时候不希望把这些历史commit带过来,于是使用--squash
进行合并,此时文件已经同合并后一样了,但不移动HEAD,不提交。需要进行一次额外的commit来“总结”一下,然后完成最终的合并。
总结
--no-ff
:不使用fast-forward方式合并,保留分支的commit历史--squash
:使用squash方式合并,把多次分支commit历史压缩为一次
Git Rebase 交互模式和黄金准则
Rebase
与 git merge
一致,git rebase
的目的也是将一个分支的更改并入到另外一个分支中去。
如上图所示,他的主要特点如下:
- 改变当前分支从
master
上拉出分支的位置 - 没有多余的合并历史的记录,且合并后的
commit
顺序不一定按照commit
的提交时间排列 - 可能会多次解决同一个地方的冲突(有
squash
来解决) - 更清爽一些,
master
分支上每个commit
点都是相对独立完整的功能单元
那么我们现在来具体操作一下,看看 git rebase
是如何做的。
首先,和 git merge
不同的是,你需要在 feature
分支上进行 git rebase master
的操作,意味着让当前分支 feature
相对于 分支 master
进行变基:
在 feature 分支上执行 git rebase
可以看出,我们遇到了冲突,进行对比的双方分别是 master
分支的最新内容和 feature
分支的第一次提交的内容,上图下方红框内容告诉我们,在我们解决了冲突之后,需要执行 git rebase --continue
来继续变基的操作。
在解决冲突之后执行 git rebase --continue
时遇到了提示,看来我们首先需要把我们的修改存到暂存区,随后再执行 git rebase --continue
。执行之后又遇到了冲突,这次是与 feature
分支的第二次提交进行对比出现的冲突,意味着我们需要多次解决同一个地方的冲突。
继续重复先解决冲突,再 git rebase --continue
的步骤,直到遇到:
意味着完成了 feature
最后一次提交的变基操作,至此整个变基就完成了。
再来看看执行 git rebase
之后的 feature
分支:
完全符合上面所说的执行 git rebase
的特点,我们引出 feature
分支的位置变了,没有多余的提交历史,且提交的时序也改变了,另外回忆一下,在我们执行变基的过程中也多次解决了同一个地方的冲突。
这个时候我们再切换到 master
分支上,将 feature
分支合并进来。
看得出来,feature
分支上的所有提交信息都会被合并到 master
分支上了,这些信息对我们来说不是必要的,我们在 masetr
分支上往往只需要知道合并进来了什么新的功能即可,这些多余的信息可以通过 git rebase
的交互模式进行整合
交互模式Rebase
打开变基的交互模式只需要传入一个参数 -i
即可,同时还需要指定对哪些提交进行处理,如:
1 | git rebase -i HEAD~4 |
上述命令指定了对当前分支的最近四次提交进行操作。下面我们使用上面这行命令将 feature
分支的提交合并。
中间红框内有一些命令,可以用来处理某次提交的,此处我们使用 squash
来将所有的 commit
合并成一次提交,编辑并保存之后会出现编辑提交的信息的提示,编辑提交即可。
此时再看 feature
分支上的提交记录,会看到只有一次提交了。
此时无论是直接将分支合并到 master
还是先变基再合并,master
分支上的提交记录都会十分清爽了。
Rebase 的黄金法则
当你理解 rebase 是什么的时候,最重要的就是什么时候 不能 用 rebase。git rebase
的黄金法则便是,绝不要在公共的分支上使用它。
比如说,如果你把 master 分支 rebase 到你的 feature 分支上会发生什么:
这次 rebase 将 master 分支上的所有提交都移到了 feature 分支后面。问题是它只发生在你的代码仓库中,其他所有的开发者还在原来的 master 上工作。因为 rebase 引起了新的提交,Git 会认为你的 master 分支和其他人的 master 已经分叉了。
同步两个 master 分支的唯一办法是把它们 merge 到一起,导致一个额外的合并提交和两堆包含同样更改的提交。不用说,这会让人非常困惑。
所以,在你运行 git rebase
之前,一定要问问你自己「有没有别人正在这个分支上工作?」。如果答案是肯定的,那么把你的爪子放回去,重新找到一个无害的方式(如 git revert
)来提交你的更改。不然的话,你可以随心所欲地重写历史。
总结
你使用 rebase 之前需要知道的知识点都在这了。如果你想要一个干净的、线性的提交历史,没有不必要的合并提交,你应该使用 git rebase
而不是 git merge
来并入其他分支上的更改。
另一方面,如果你想要保存项目完整的历史,并且避免重写公共分支上的 commit, 你可以使用 git merge
。两种选项都很好用,但至少你现在多了 git rebase
这个选择。
cherry-pick
这命令简直就是神器,给你自由,你想把那个节点merge过来就把那个节点merge过来,其合入的不是分支而是提交节点
Git创建long commit message
使用git commit -m
/gcmsg
创建提交信息必须限制在50个字符内,那如何提交超长的commit message呢?
答案是使用git commit
,git会弹出一个vim文本编辑控制台(也取决于git config中设置的默认编辑器),在vim中编辑分为header和body两个部分。header部分遵循50字符的限制,body部分可以添加足够多和足够多行的信息。
丰富的commit message有利于减少collaborators之间信息沟通的能量消耗。
Git删除分支/删除远程分支/停止跟踪远程分支
1 | 查看远程分支 |
Reference
- https://juejin.im/post/5af26c4d5188256728605809
- https://www.atlassian.com/git/tutorials/merging-vs-rebasing
- https://blog.csdn.net/qq_31024823/article/details/82787643
- https://zhuanlan.zhihu.com/p/28137908