概述

对于大项目而言,使用分支是不可避免的。GIT同样也提供了分支功能,但是GIT分支轻量,操作速度快。下面就对GIT分支内容进行的整理。

1、分支简介

之前我们整理过,GIT保存的是一些不同时刻的文件快照,而不是文件的变化和差异

在进行提交操作(git commit)时,GIT会保存一个提交对象(commit object)。该提交对象会包含一个指向暂存内容快照的指针作者的姓名和邮箱提交时输入的信息以及指向父对象的指针。首次提交对象没有父对象,普通提交对象由一个父对象,而多个分支合并产生的提交对象由多个父对象。

为了更加形象,我们举例说明,比如:有一个新建空GIT仓库,我们在这个仓库里添加了三个文件,分别是README、test.rb、LICENSE。我们使用git add进行暂存操作。暂存操作会为每一个文件计算校验和(SHA-1算法),然后会把当前版本的文件快照保存到GIT仓库中,最终将校验和加入到暂存区域等待提交。

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

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

现在,GIT仓库中有5个对象:3个文件的快照、一个树对象、以及一个提交对象。如下图所示:
GIT分支操作
如果对文件做一些修改然后提交,那么这次产生的提交对象会包含一个指向上次提交对象的指针,这个指针就是父指针。如下图:
GIT分支操作
GIT分支的本质仅仅是指向提交对象的可变指针。GIT的默认分支名称是master。在多次提交之后你其实已经有一个指向最后那个提交对象的master分支。它会在每次的提交操作中自动向前移动。

2、分支操作的命令

2.1、分支的创建

使用命令git branch进行分支的创建,其实就是创建了一个可以移动的新的指针。比如,创建一个testing分支:

git branch testing

这会在当前所在的提交对象上创建一个指针,如下图:

GIT分支操作
那么,GIT又怎么知道当前在哪个分支上呢?它有一个名为HEAD的特殊指针,指向当前所在的本地分支。在本例中,你仍然在master分支上,因为 git branch 命令仅仅是创建一个新分支,并不会自动切换到新分支中去。

GIT分支操作
可以使用git log 查看查看各个分支当前所指的对象,选项为 --decorate。

git log --online --decorate

2.2、分支切换

使用命令git checkout [branch-name] 切换到分支。比如切换到新创建的 testing 分支:

git checkout testing

这样 HEAD 就指向 testing 分支了。如下图:

GIT分支操作
如果我们现在对文件进行修改,然后提交,则HEAD 指向的testing分支随着提交自动向前移动,如下图:

GIT分支操作
如果我们现在使用命令 git checkout master 切回到master分支,则 HEAD 指向 master 分支,并且工作目录恢复成master分支所指向的快照内容。

如果对文件在进行修改和提交,则HEAD 会随着 matser 分支自动向前移动。如下图:
GIT分支操作
现在这个项目就产生了分叉,可以使用git log命令查看分叉历史,运行git log --oneline --decorate --graph --all,它会输出你的提交历史、各个分支的指向以及项目的分支分叉情况。

由于GIT分支实际上所指对象校验和的文件,所以它的创建和销毁都异常高效。

上面讲述了分支的创建和切换,如果使用命令git checkout -b [branch-name],则是创建一个branch-name的新分支,并且切换到这个分支上

2.3、分支合并

使用git merge 命令进行分支的合并。比如现有两条分支,分别为master和iss53,如果想把iss53合并到master分支,则需要先切换到master分支,然后运行git merge命令,如下:

git checkout master
git merge iss53

这里会有三种情况:
第一种情况: iss53 有过提交记录,而master分支在产生分支之后,没有发生过提交记录,如下图所示:

GIT分支操作
因为master 分支指向的提交时当前提交的上游,执行上面的命令时,GIT只是简单的将指针向前移动。如下图:

GIT分支操作
这种没有需要解决的合并操作叫做----“快进(fast–forward)”

第二种情况是:iss53分支进行了提交,master分支也进行了提交,两者有了分叉。如下图:

GIT分支操作
执行合并命令后,GIT不得不做一些额外的工作。GIT会使用两个分支的末端所指的快照(C4和C5)以及这两个分支的工作祖先(C2),做一个三方合并。GIT将此次三方合并的结果做了一个新的快照并且自动创建一个新的提交指向它。如下图:

GIT分支操作
这种被称作一次合并提交,它的特别之处在于他有不止一个父提交

第三种情况:在第二种情况下,出现文件合并冲突,GIT没法干净的合并他们。此时GI不会自动创建新的合并提交,而是会暂停下来,等待你去解决合并产生的冲突。你可用git status 命令来查看合并冲突而处于未合并状态的文件。如:

$ git status
On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")

Unmerged paths:
  (use "git add <file>..." to mark resolution)

    both modified:      index.html

no changes added to commit (use "git add" and/or "git commit -a")

这样就需要我们打开文件进行手动解决冲突。出现冲突的文件会包含一些特殊区段,看起来像下面这个样子:

<<<<<<< HEAD:index.html
<div id="footer">contact : [email protected]</div>
=======
<div id="footer">
 please contact us at [email protected]
</div>
>>>>>>> iss53:index.html

这表示HEAD 所指示的版本(也就是master版本)在 ============区段的上半部分,而iss53 分支所指示的版本在 ============的下半部分。你可以选择一个版本,也可以自己手动合并。记得<<<<<,=====,>>>>>>>这些行删除掉。

解决完冲突之后,使用git add命令把文件暂存,GIT就会将它们标记为冲突已解决。然后使用git commit进行合并提交。

2.4、分支的删除

使用命令 git branch -d [branch-name]进行分支的删除。比如上面合并完成之后的iss53分支,执行命令:

git branch -d iss53

来删除掉。

2.5 分支的查看

使用命令 git branch 命令查看分支,它会得到当前所有分支的列表。如下:

$ git branch
  iss53
* master
  testing

注意master 分之前 * 字符,它代表现在检出的是哪一个分支(HEAD当前所指分支)。这时候提交,master会随着新的工作向前移动。

使用命令 git branch -v 查看每一个分支的最后一次提交。如下:

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

–merged 与 --no–merged 这两个有用的选项可以过滤这个列表中已经合并或者尚未合并到当前分支的分支。如:

$ git branch --merged
  iss53
* master

感谢大家,我是假装很努力的YoungYangD(小羊)。

参考资料:GIT官网

相关文章: