何谓分支
提交对象
在 Git 中提交时,会保存一个提交(commit)对象,该对象包含一个指向暂存内容快照的指针,包含本次提交的作者等相关附属信息
当使用 git commit 新建一个提交对象前,Git 会先计算每一个子目录(本例中就是项目根目录)的校验和,然后在 Git 仓库中将这些目录保存为树(tree)对象。之后 Git 创建的提交对象,除了包含相关提交信息以外,还包含着指向这个树对象(项目根目录)的指针,如此它就可以在将来需要的时候,重现此次快照的内容了。
单提交对象数据结构
现在,Git 仓库中有五个对象:三个表示文件快照内容的 blob 对象;一个记录着目录树内容及其中各个文件对应 blob 对象索引的 tree 对象;以及一个包含指向 tree 对象(根目录)的索引和其他提交信息元数据的 commit 对象
多个提交对象之间的链接关系
作些修改后再次提交,那么这次的提交对象会包含一个指向上次提交对象的指针(译注:即下图中的 parent 对象)。两次提交后,仓库历史会变成图 3-2 的样子:
概述
本质上仅仅是个指向 commit 对象的可变指针。Git 会使用 master 作为分支的默认名字。在若干次提交后,你其实已经有了一个指向最后一次提交对象的 master 分支,它在每次提交的时候都会自动向前移动。
操作
查看
git branch
参数
-v : 连带最后一个提交对象的信息
新增
git branch <branch-name>
切换
git checkout <branch-name>
HEAD 在你转换分支时指向新的分支, 并把工作目录中的文件换成了目标分支所指向的快照内容
合并
git merge <branch-target>
讲目标分支合并入当前分支
删除
git branch -d <branch-name>
HEAD
Git 是如何知道你当前在哪个分支上工作的呢?其实答案也很简单,它保存着一个名为 HEAD 的特别指针
它是一个指向你正在工作中的本地分支的指针(译注:将 HEAD 想象为当前分支的别名。)
运行 git branch 命令,仅仅是建立了一个新的分支,但不会自动切换到这个分支中去
HEAD 指向当前所在的分支
为什么非常快
由于 Git 中的分支实际上仅是一个包含所指对象校验和(40 个字符长度 SHA-1 字串)的文件,所以创建和销毁一个分支就变得非常廉价。说白了,新建一个分支就是向一个文件写入 41 个字节(外加一个换行符)那么简单,当然也就很快了。
这和大多数版本控制系统形成了鲜明对比,它们管理分支大多采取备份所有项目文件到特定目录的方式,所以根据项目文件数量和大小不同,可能花费的时间也会有相当大的差别,快则几秒,慢则数分钟。而 Git 的实现与项目复杂度无关,它永远可以在几毫秒的时间内完成分支的创建和切换。同时,因为每次提交时都记录了祖先信息(译注:即 parent 对象),将来要合并分支时,寻找恰当的合并基础(译注:即共同祖先)的工作其实已经自然而然地摆在那里了,所以实现起来非常容易。Git 鼓励开发者频繁使用分支,正是因为有着这些特性作保障。
分支使用
特性分支
特性分支是指一个短期的,用来实现单一特性或与其相关工作的分支
远程分支
概述
远程分支(remote branch)是对远程仓库中的分支的索引。它们是一些无法移动的本地分支;只有在 Git 进行网络交互时才会更新。远程分支就像是书签,提醒着你上次连接远程仓库时上面各分支的位置
与本地分支的区别
我们用 (远程仓库名)/(分支名) 这样的形式表示远程分支。比如我们想看看上次同 origin 仓库通讯时 master 分支的样子,就应该查看 origin/master 分支。如果你和同伴一起修复某个问题,但他们先推送了一个 iss53 分支到远程仓库,虽然你可能也有一个本地的 iss53 分支,但指向服务器上最新更新的却应该是 origin/iss53 分支。
更新远程分支信息
git fetch <remote-name>
从远程服务器获取你尚未拥有的数据,更新你本地的数据库,然后把 origin/master 的指针移到它最新的位置上
在 fetch 操作下载好新的远程分支之后(假设为serverfix),你仍然无法在本地编辑该远程仓库中的分支。换句话说,在本例中,你不会有一个新的 serverfix 分支,有的只是一个你无法移动的 origin/serverfix 指针。
推送本地分支
git push <remote-name>:<remote-branch>
你创建的本地分支不会因为你的写入操作而被自动同步到你引入的远程服务器上,你需要明确地执行推送分支的操作
跟踪远程分支
从远程分支 checkout 出来的本地分支,称为 跟踪分支 (tracking branch)。跟踪分支是一种和某个远程分支有直接联系的本地分支。在跟踪分支里输入 git push,Git 会自行推断应该向哪个服务器的哪个分支推送数据。同样,在这些分支里运行 git pull 会获取所有远程索引,并把它们的数据都合并到本地分支中来。
创建跟踪远程分支
在克隆仓库时,Git 通常会自动创建一个名为 master 的分支来跟踪 origin/master。这正是 git push 和 git pull 一开始就能正常工作的原因
git checkout -b [分支名] [远程名]/[分支名]
$ git checkout --track origin/serverfix<br> Branch serverfix set up to track remote branch refs/remotes/origin/serverfix.<br> Switched to a new branch "serverfix"
--track简化版: <br>git checkout --track [远程名]/[分支名]<br>
简化版本 <br><br>$ git checkout -b sf origin/serverfix<br> Branch sf set up to track remote branch refs/remotes/origin/serverfix.<br> Switched to a new branch "sf"<br>
删除远程分支
git push [remote] --delete [branch-name]
推送空分支到远程
git push [远程名] :[分支名]
分支的变基(衍合): rebase
基本的变基
合并结果中最后一次提交所指向的快照,无论是通过衍合,还是三方合并,都会得到相同的快照内容,只不过提交历史不同罢了。衍合是按照每行的修改次序重演一遍修改,而合并是把最终结果合在一起。
命令
git rebase [主分支] [特性分支]
会先取出特性分支 server,然后在主分支 master 上重演
特别地:
git rebase [主分支]
当前操作的即是即是特性分支
风险
准则
一旦分支中的提交对象发布到公共仓库,就千万不要对该分支进行衍合操作。
建议
如果把衍合当成一种在推送之前清理提交历史的手段,而且仅仅衍合那些尚未公开的提交对象,就没问题