Git分支

分支简介

我们假设现在有一个工作目录,里面包含了三个将要被暂存和提交的文件。 暂存操作会为每一个文件计算校验和,然后会把当前版本的文件快照保存到 Git 仓库中(Git 使用 blob 对象来保存它们),最终将校验和加入到暂存区域等待提交:

$ git add README test.rb LICENSE
$ git commit -m 'The initial commit of my project'

当使用 git commit 进行提交操作时,Git 会先计算每一个子目录(本例中只有项目根目录)的校验和,然后在 Git 仓库中这些校验和保存为树对象。 随后,Git 便会创建一个提交对象,它除了包含上面提到的那些信息外,还包含指向这个树对象(项目根目录)的指针。如此一来,Git 就可以在需要的时候重现此次保存的快照。

现在,Git 仓库中有五个对象:三个 blob 对象(保存着文件快照)、一个树对象(记录着目录结构和 blob 对象索引)以及一个提交对象(包含着指向前述树对象的指针和所有提交信息)。


 


Figure 3-1. 首次提交对象及其树结构

 

做些修改后再次提交,那么这次产生的提交对象会包含一个指向上次提交对象(父对象)的指针。



Figure 3-2. 提交对象及其父对象

 

Git 的分支,其实本质上仅仅是指向提交对象的可变指针。 Git 的默认分支名字是 master。 在多次提交操作之后,你其实已经有一个指向最后那个提交对象的 master 分支。 它会在每次的提交操作中自动向前移动。


 Note

Git 的 “master” 分支并不是一个特殊分支。 它就跟其它分支完全没有区别。 之所以几乎每一个仓库都有 master 分支,是因为 git init 命令默认创建它,并且大多数人都懒得去改动它。


Figure 3-3. 分支及其提交历史

 

分支创建

$ git branch [branch-name]

分支切换

会改变工作目录中的文件

$ git checkout [branch-name]

分支删除

$ git branch -d [branch-name]

分支管理

当前所有分支的列表

$ git branch
  iss53
* master
  testing

查看每一个分支的最后一次提交

$ git branch -v
  iss53   93b412c fix javascript issue
* master  7a98805 Merge branch 'iss53'
  testing 782fd34 add scott to the author list in the readmes

查看哪些分支已经合并到当前分支

$ git branch --merged
  iss53
* master

查看所有包含未合并工作的分支

$ git branch --no-merged
  testing

远程分支

查看远程分支的信息

$ git ls-remote [remote]
$ git remote show [remote]

推送分支到远程仓库

$ git push [remote] [branch]

创建跟踪分支

$ git checkout -b [branch] [remotename]/[branch]

查看设置的所有跟踪分支

会将所有的本地分支列出来并且包含更多的信息,如每一个分支正在跟踪哪个远程分支与本地分支是否是领先、落后或是都有。

$ git branch -vv
  iss53     7e424c3 [origin/iss53: ahead 2] forgot the brackets
  master    1ae2a45 [origin/master] deploying index fix
* serverfix f8674d9 [teamone/server-fix-good: ahead 3, behind 1] this should do it
  testing   5ea463a trying something new

拉取数据

从服务器上抓取本地没有的数据时,它并不会修改工作目录中的内容。 它只会获取数据然后让你自己合并。

$ git fetch

删除远程分支

$ git push origin --delete serverfix
To https://github.com/schacon/simplegit
 - [deleted]         serverfix

变基

开发任务分叉到两个不同分支,又各自提交了更新。


 


 

Figure 3-27. 分叉的提交历史

 

merge 命令

它会把两个分支的最新快照(C3C4)以及二者最近的共同祖先(C2)进行三方合并,合并的结果是生成一个新的快照(并提交)。



Figure 3-28. 通过合并操作来整合分叉了的历史

 

rebase 命令

提取在 C4 中引入的补丁和修改,然后在 C3 的基础上再应用一次。

$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command

它的原理是首先找到这两个分支(即当前分支 experiment、变基操作的目标基底分支 master)的最近共同祖先 C2,然后对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件,然后将当前分支指向目标基底 C3, 最后以此将之前另存为临时文件的修改依序应用。(译注:写明了 commit id,以便理解,下同)


 

 


Figure 3-29. 将 C4 中的修改变基到 C3

 

现在回到 master 分支,进行一次快进合并。

$ git checkout master
$ git merge experiment


 

Figure 3-30. master 分支的快进合并

 

更有趣的变基例子

在对两个分支进行变基时,所生成的“重演”并不一定要在目标分支上应用,你也可以指定另外的一个分支进行应用。 就像Figure 3-31中的例子这样。 你创建了一个特性分支 server,为服务端添加了一些功能,提交了 C3C4。 然后从 C3 上创建了特性分支 client,为客户端添加了一些功能,提交了 C8C9。 最后,你回到 server 分支,又提交了 C10



Figure 3-31. 从一个特性分支里再分出一个特性分支的提交历史

 

假设你希望将 client 中的修改合并到主分支并发布,但暂时并不想合并 server 中的修改,因为它们还需要经过更全面的测试。 这时,你就可以使用 git rebase 命令的 --onto 选项,选中在 client 分支里但不在 server 分支里的修改(即 C8C9),将它们在 master 分支上重演:

$ git rebase --onto master server client

以上命令的意思是:“取出 client 分支,找出处于 client 分支和 server 分支的共同祖先之后的修改,然后把它们在 master 分支上重演一遍”。 这理解起来有一点复杂,不过效果非常酷。



Figure 3-32. 截取特性分支上的另一个特性分支,然后变基到其他分支

 

现在可以快进合并 master 分支了。(如图 Figure 3-33):

$ git checkout master
$ git merge client


Figure 3-33. 快进合并 master 分支,使之包含来自 client 分支的修改

 

接下来你决定将 server 分支中的修改也整合进来。 使用 git rebase [basebranch] [topicbranch] 命令可以直接将特性分支(即本例中的 server)变基到目标分支(即 master)上。这样做能省去你先切换到 server 分支,再对其执行变基命令的多个步骤。

$ git rebase master server

如图Figure 3-34所示,server 中的代码被“续”到了 master 后面。


 


 

Figure 3-34. 将 server 中的修改变基到 master 上

 

然后就可以快进合并主分支 master 了:

$ git checkout master
$ git merge server

至此,clientserver 分支中的修改都已经整合到主分支里去了,你可以删除这两个分支,最终提交历史会变成图Figure 3-35中的样子:

$ git branch -d client
$ git branch -d server


Figure 3-35. 最终的提交历史

 

待续。。。。。。

posted @ 2016-11-15 16:37  心有猛虎~细嗅蔷薇  阅读(208)  评论(0)    收藏  举报