Git 的分支模型堪称它的“必杀技特性“,也正因为这一特性,使得 Git 从众多版本控制系统中脱颖而出。
在很多版本控制系统中,创建分支是一个低效的过程,因为通常的做法是创建一个源代码目录的副本,特别是对于大项目来说,这样的过程会耗费很多时间。而Git创建新分支的操作几乎能在瞬间完成,并且在不同分支之间的切换操作也是一样便捷。 与许多其它版本控制系统不同,Git 鼓励在工作流程中频繁地使用分支与合并。
理解和精通这一特性,你便会意识到 Git 是如此的强大而又独特,并且从此真正改变你的开发方式。
Git基础 中介绍了Git提交时会保存一个提交对象,这个对象记录着提交版本的目录结构指针以及文件快照指针。
Git 的分支,其实本质上仅仅是指向提交对象的可变指针。 Git 的默认分支名字是 master。 在多次提交操作之
后,你其实已经有一个指向最后那个提交对象的 master 分支。 它会在每次的提交操作中自动向前移动。
Git 的 “master” 分支并不是一个特殊分支。 它就跟其它分支完全没有区别。 之所以几乎每
一个仓库都有 master 分支,是因为 git init 命令默认创建它,并且大多数人都懒得去改动
它。
Git 创建新分支很简单,它只是为你创建了一个可以移动的新的指针。 比如,创建一个 feature 分
支:
$ git branch feature
这会在当前所在的提交对象上创建一个指针。
那么,Git 又是怎么知道当前在哪一个分支上呢? 也很简单,它有一个名为 HEAD 的特殊指针。
HEAD指针指向当前所在的本地分支(可以将 HEAD 想象为当前分支的别名)。 在上述例子中,你仍然在 master 分支上。 因为 git branch 命令仅仅创建 一个新分支,并不会自动切换到新分支中去。
既然HEAD指针是指向当前分支的,那么要切换到一个已存在的分支只需要将HEAD指针移动指向这个分支就可以了,使用 git checkout 命令切换到新创建的 feature 分支去:
$ git checkout feature
这样 HEAD 就指向 feature 分支了。
在分支上进行提交时,当前分支向前移动,其它分支保持不动。
此时切换到其它分支,使用 git checkout
命令。这个命令做了两件事, 一是使 HEAD 指向目标分支,二是将工作目录恢复成目标分支所指向的快照内容。这样就支持了多分支的非线性开发,而且分支间的切换也非常快。
同时开发多个分支,提交历史就会出现分叉,因为可能有多个commit对象指向了同一个父对象
分支的创建、合并是经常使用的功能,实际工作中常常会用到类似的工作流:
假设在一个项目上开发,项目已有一些提交历史
实现新的需求,创建新的分支,并在新分支上工作
收到紧急修复任务,回到主线分支,创建修复分支
在修复分支上工作,修复问题
修复完成,使用 git merge
命令合并修复分支到主线分支
由于当前 master 分支所指向的提交是你当前提交的直接上游,所以 Git 只是简单的将指针向前移动。 当我们试图合并两个分支时,如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候,只会简单的将指针向前推进(指针右移),因为这种情况下的合并操作没有需要解决的分歧,这种合并模式叫做 “快进(fast-forward)”。
合并完成后,最新的修改已经在 master 分支所指向的提交快照中,可以着手发布该修复了。
修复分支合并后就可以删除了,切换回需求分支继续工作
完成需求开发,我们需要将需求分支合并到主线分支并发布,使用 git merge
命令合并分支
和之前将分支指针向前推进不同,当前 master 分支指向的提交不是 feature 分支指向的提交的直接上游,因此需要进行一次合并,Git 将此次三方合并的结果做了一个新的快照并且自动创建一个新的提交指向它。 这个被称作一次合并提交,它的特别之处在于他有不止一个父提交。
需要指出的是,Git 会自行决定选取哪一个提交作为最优的共同祖先,并以此作为合并的基础。
在 Git 中整合来自不同分支的修改主要有两种方法:merge 以及 rebase。 在本节中我们将学习什么是“变
基”,怎样使用“变基”,并将展示该操作的惊艳之处,以及指出在何种情况下你应避免使
在 feature 分支上变基
$ git rebase master
rebase
命令将 feature 分支上的所有提交(C3、C6)提取出来,生成了新的提交C3‘、C6’,其中C3‘的父提交是C5。我们使用 git log
查看执行 rebase
前后的提交历史的校验和就可以发现其中的秘密。
这时 master 分支指向的提交是 feature 分支指向提交直接上游,使用 git merge
合并 feature 分支到 master 时就只是进行了指针的移动
整合分支时,我们使用 git merge
和 git rebase
都能实现,这两者并没有好坏之分,使用哪个操作来整合分支更多取决于开发者对提交历史的需求及理解。
git merge
更能反映开发过程中的实际发生的事情;
git rebase
则强调项目过程中发生的事,也就是只关心谁什么时候对哪个文件进行了哪些修改,至于记录这些信息的提交对象是否完全按照时间顺序排列并不重要,这样的提交历史是线性的,查看起来非常方便。
参考书目: