文章目录
- 前言
- 分支机制简述
- 创建分支
- 切换分支
- 基本的分支与合并操作
- 基本的分支操作
- 基本的合并操作
- 基本的合并冲突处理
- 分支管理
- 与分支有关的工作流
- 长期分支
- 主题分支
- 远程分支
- 推送
- 跟踪分支
- 拉取
- 删除远程分支
- 变基
- 基本的变基操作
- 变基操作的潜在危害
- 只在需要的时候执行变基操作
- 变基操作与合并操作的对比
- 总结
前言
几乎每个版本控制系统都支持某种形式的分支功能。分支意味着偏离开发主线并继续你自己的工作而不影响主线开发。在很多版本控制工具中,这么做存在着比较昂贵的成本,因为常常需要去对整个源代码目录进行一次复制,而对于大型项目,这种复制会耗去很长时间。
而Git很好的解决了这个问题,怎么做的?看下面的博客你就懂了。
分支机制简述
要想真正理解Git分支机制,我们要首先回过头来看一下Git是如何存储数据的。
正如前面的博客所言,Git并没有采用变更集(changeset)或是差异的方式存储数据,而是采用一系列快照的方式。
当你发起提交时,Git存储的是提交对象,其中包含了指向存储区快照的指针
。提交对象也包括作者姓名和邮箱地址、已输入的提交信息以及指向其父提交的指针。初始提交没有父提交,而一般的提交会有一个父提交;对于两个或多个分支的合并提交来说,存在着多个父提交。
现在Git仓库中有5个对象:3个blob对象(分别保存了你的三个文件的内容)、1个树对象(记录着目录结构以及blob对象和文件名之间的关系),以及一个提交对象(包含着提交的全部元数据和指向根目录树对象的指针)
。
Git分支只不过是一个指向某次提交的轻量级的可移动指针
。Git默认的分支名是master。当你发起提交时,就有了一个指向最后一次提交的master分支。每次提交它都会自动向前移动。
创建分支
创建分支
git branch branch_name
查看各个分支当前所指向的对象。
git log --oneline --decorate
切换分支
操作如下
以上的命令一共做了两件事。他会把HEAD指针(Git的HEAD指针不同于其他版本控制系统,是一个指向当前所在的本地分支的指针
)
移回到master分支,还会把工作目录的文件恢复到master分支指向的快照的状态。这也就意味着,从这时起,你所做的修改将基于项目的较老版本。总而言之,上述操作回滚了你在testing分支上所做的工作,使你能向另一个方向进行开发工作。
当你准备好了就可以合并这些修改(见后文)
Git中的分支实际上就是一个简单的文件,其中包含了该分支所指向提交的长度为40个字符的SHA-1校验和。正因如此,Git分支创建和删除的成本很低。而且Git提交时保存了父对象的指针,当进行合并操作时Git会自动寻找合适的合并基础,操作起来非常简单。
基本的分支与合并操作
现在我们要展示一个简单的分支和合并案例,其中的工作流可供真实项目借鉴。要遵循的步骤如下:
-
在网站上开展工作;
-
为新需求创建分支;
-
在新分支上展开工作;
这时,你接到一个电话,说项目有一个很严重问题需要紧急修复。你随后会这样做: -
切换到你的生产环境分支;
-
创建新的分支进行此次问题的热修补工作;
-
通过测试后,合并热修补分支并推送到生产环境中;
-
切换回之前的需求分支上继续工作。
基本的分支操作
操作如下
现在,你接到一个电话,说网站突然有个问题需要立即修复。如果没有Git的帮助,你要么把你的修复补丁和issu53的变更一起部署,要么就花费大量精力去恢复之前针对的issu53所做的工作,好让你制作的修复补丁单独上线。如今你要做的就是切换回master分支即可。
但先别急,在你切换分支之前要注意的是,如果你的工作目录或者暂存区存在着未提交的更改,并且这些更改与你要切换的分支冲突。Git就会不允许你切换分支。在切换分支时,最好是保持一个干净的工作区域。后面我们会介绍几种绕过这个问题的办法:存储和修改提交。就现在而言,让我们假定你已经提交了所有更改,操作如下
然后删除hotfix分支,切回issu53分支,继续我们的工作
继续操作如下
值得注意的是,issu53分支并不包含你在hotfix分支上做的操作。如果需要上述修补工作并入issu53,就需要执行git merge master
使得master分支合并到issu53中,或者可以等到要把issu53合并回master分支时再把热修补的工作整合进来。
基本的合并操作
现在issu53的工作已经完成,我们需要合并回主分支。操作如下
git checkout master
git merge issu53
与之前简单的向前移动分支指针不同,这次Git会基于三方合并的结果创建一个新的快照,然后创建一个提交指向新的快照。这个提交叫做合并提交“。合并提交的特殊性在于它拥有不止一个父提交”
删除issu53分支
基本的合并冲突处理
有时候,上述合并过程并不会那么顺利。如果你在要合并的两个分支上都改了同一个文件的同一部分内容,Git就没办法干净的合并这两个分支。如下
你只需要进入将文件删除,修改成你想要的那样就可以解决了。然后再次合并就OK。
你也可以使用图形化工具来解决冲突git mergetool
.
冲突解决后,相应的文件就进入了暂存区,然后使用git commit
命令来完成此次合并提交。
分支管理
前面我们已经分析了git branch 与 git branch -v
。另外还有两个很有用的选项是--merged
和--no-merged
。这两个选项分别是筛选已并入当前分支的所有分支和筛选尚未并入的所有分支。
查看那些分支已经并入当前分支:
git branch --merged
查看那些分支没有并入到当前分支:
git branch --no-merged
注意:如果你没有合并到当前分支,是不能直接删除的,如果实在想删除就使用-D选项
与分支有关的工作流
长期分支
长期分支指的是在较长时间内多次把一个分支合并到另一个分支上去,而合并的这个分支就用来存放发放版本的代码。
很多使用Git的开发者都喜欢构建他们自己的工作流。例如,其中一种流程就是master
分支只存放稳定版本的代码,即已经发布的版本或即将发布的版本的代码。他们还会使用另一个叫做develop或 next
的平行分支用于开发,或是用于测试代码的稳定性。
主题分支
与长期分支不同,在任何规模的项目上主题分支都非常有用。主题分支是指短期的,用于实现某一特定的功能及其相关工作的分支。
远程分支
远程分支指的是指向远程仓库的分支指针,这些指针存在于本地且无法被移动。当你与服务器进行任何网络通信时,他们会自动更新。远程分支有点像书签,他们会提示你上一次连接服务器时远程仓库中每个分支的位置。
使用git fetch xxx
命令将远程服务器的数据同步到本地
推送
当需要同别人共享某个分支上的工作成果时,就要把它推送到一个具有写权限的远程仓库。你的本地分支并不会自动同步到远程仓库,必须要显式地推送那些你想与别人共享的分支。
使用git push (remote) (branch)
命令即可。
如果不想每次推送时都键入密码,可以设置一个“凭据缓存”。最简单的设置方式就是把凭据信息暂时保存在内存中几分钟,这只需要执行git config --global credential.helper cache
命令即可。
跟踪分支
基于远程分支创建的本地分支会自动成为跟踪分支,或者有时候也叫做游分支。
跟踪分支是远程分支直接关联的本地分支。如果
拉取
git fetch
命令会拉取本地没有的远程所有最新更改数据,但这条命令完全不会更改你的工作目录。它只会从服务器上读取数据,然后让你自己进行合并。初次之外,还有一个git pull
命令,这条命令在大多数情况下基本等同于git fetch + git merge
。
删除远程分支
当你和你的同事已经完成一个功能,并且把工作合并到了远程的master分支之后,你已经不再需要包含这个功能的远程分支了。可以通过git push --delete
选项来删除远程分支。
基本上可以说,以上命令只是删除了远程服务器上的分支指针。Git会保留数据一段时间,直到下一次触发垃圾回收。所以,即使误删了分支,一般来说也很容易进行恢复。
变基
在Git中,要把更改从一个分支整合到另一个分支,有两种主要方式:合并和变基。
基本的变基操作
之前我们讲过,要整合不同的分支,最简单的办法就是使用merge命令。该命令会对两个分支上的最新提交快照以及这两个提交快照最近的共同祖先,进行一次三方合并,并创建一个新的合并提交。实际上,除了上述方式之外还有一种方式:你可以把另一个分支的提交的更改以补丁的形式应用到另一个分支上。这种操作就叫做变基。使用如下命令
git checkout xxx
git rebase master
变基的工作原理是:首先找到两个要整合的分支(你当前的分支和要整合到的分支)的共同祖先,然后取得当前所在分支的每次提交引入的更改(diff),并把这些更改保存为临时文件,这之后将当前分支重置为要整合的分支,最后在该分支上依次引入之前保存的每个更改。
总结:
变基就是把某条开发分支线上的工作在另一个分支线上按顺序重现。而合并操作则是找出两个分支的末端,并把他们合并在一起。
变基操作的潜在危害
变基操作可以带来种种好处。但它并不是完美无缺、其缺点可以总结成一句话:不要对已经存在于本地仓库之外的提交执行变基操作。——即不要对已经推送到远程服务器的公开提交进行变基操作。
这是因为在执行变基操作时,实际上是抛弃了已有的某些提交,随后创建了新的对应提交。
只在需要的时候执行变基操作
如果你将变基操作看作在推送数据前整理和处理提交的一个手段,并且你只对那些仅存在于本地还没有公开的提交进行变基操作,那么一切都不会有问题。反之,如果你对那些已经推送了的提交执行变基操作,而这时其他人可能已经基于这些提交进行了自己的开发工作,那么那就可能会遇到大麻烦,使用git pull --reabse = git fetch + git rebase remote/branch
来减轻你的痛苦。
变基操作与合并操作的对比
区别:合并不会更改实际的历史记录,变基则会更改历史记录。结合这两种操作的优点的操作方式是,对本地尚未推送的更改进行变基操作,从而简化提交历史,但绝不能对任何已经推送到服务器的更改进行变基操作。
总结
相信现在你应该可以自由创建和切换到新的分支,在不同的分支之间切换,并对本地的不同分支进行合并操作。你也应该可以将你的分支推送到远程服务器来分享你的工作成果,与其他人在某个分支上进行协作,对本地尚未推送的分支进行变基操作。
后面将对如何搭建自己的Git仓库托管服务器
进行分析和学习。