Git 内部实现:分支

本文出处:http://robinnagpal.wordpress.com/2012/08/05/git-internals-branches/

通过阅读本文,您将了解到:

  1. 一些有用的技术:
    • Master
    • HEAD
    • Branch
  2. Git 中的分支的概念,以及为什么在 Git 中创建分支是如此简单和快速
  3. 本地分支和远程分支是什么意思
  4. 帮助我们创建和使用分支的命令

以下几点能够帮助我们在我们的项目中理解 GIT 的分支。

Master
当你初始化一个 GIT 仓库的时候,这将成为初始化的分支。你可以将它理解成 SVN 中的 trunk。

HEAD
这指向你签出的当前分支的引用,由 .git 文件夹中的 HEAD 文件表示。如果你查看这个文件的内容,应该类似下面:

ref: refs/heads/master

上面的内容指出,我们当前工作的副本指向的是 master 分支的最新提交版本。假设现在正在 branch_1 分支,那么 HEAD 内容应该是

ref: refs/heads/branch_1

Branch
在 GIT 中,我们的每次提交都包含以下信息:

  1. 一个指向唯一的父提交对象的引用;如果是合并提交的话,则应该是一个指向两个父提交对象的引用;
  2. 一个树对象的引用,这个对象会在以后包含多个 blob 或者树对象;
  3. 有关本次提交的信息,例如作者名字、email 以及提交信息等。

所以,每次独立提交都能够表示一个快照,这个快照是本工程当前提交对象的完整快照。这种设计使得 GIT 的分支十分简单、廉价以及健壮。

一个分支只是一个包含 40 个字符的字符串,该字符串指向这个提交对象,除此之外什么都不是。

我能理解你会觉得这种说法很难理解。给我一段时间,我将告诉你,一旦你明白了 GIT 的分支模型,你将不可救药的爱上她!

现在我们用图形表示分支。

假设我们有三个提交 mc_1、mc_2 和 mc_3,它们都位于 master 分支:

master

现在,我们决定从 master 分支创建一个分支 branch_1。我们使用如下命令:

git branch branch_1
git checkout branch_1

这两个命令可以合并为:

git checkout -b branch_1

现在我们的仓库看上去是这样的:

branch 1

我们将会发现,现在的 HEAD 指向 branch_1。如果我们查看 HEAD 文件的内容,我们就会发现这一事实:

$ cat .git/HEAD
ref: refs/heads/branch_1

如果我们查看 .git/refs/heads 文件夹中的内容,我们会发现以下文件:

branch_1
master

当我们创建该分支时,就会创建一个新的文件 branch_1。这个文件包含了 mc_3 提交的 SHA1 值。这就是分支的含义,也就是,一个指向了该分支的最新提交的 40 个字符的字符串。

现在,我们向该分支添加并提交一些内容:

$ echo "Greeting !!" > greeting.txt
git add greeting.txt
git commit -m "Our first commit to the branch"

现在我们有了该分支的第一次提交。我们的仓库应该如下表示:

branch 1 commit

现在,bc_1 是新分支上的新的提交对象。如果我们检查 .git/refs/branch_1 文件的内容,我们将会发现它就是指向我们新的提交对象的指针,也就是提交对象 bc_1 的 SHA1 值。

现在,我们回到 master 分支,添加一些东西:

git checkout master
$ echo "Happy Birthday" > wishes.txt
git add wishes.txt
git commit -m "Our first commit in master after creation of the branch"

这次提交以后,我们的仓库类似:

master commit

现在,我们可以看出 GIT 中创建和管理分支是多么的简单。一旦你开始基于这些分支工作,你就会爱上 GIT 处理分支的这种方式。

现在我们试着修改欢迎文件,然后切换到 branch_1:

$echo “Merry Christmas”>greeting.txt
git checkout branch_1

在我们试图执行第二个命令的时候,我们会得到下列错误:

error: The following untracked working tree files would be overwritten by checkout:
greeting.txt
Please move or remove them before you can switch branches.
Aborting

GIT 会要求你,在切换到其它任意分支之前提交或者移除你所做的修改。这种设计看起来很琐碎,我们可以选择自动提交归档,然后切换到另外的分支。但是考虑一下真实用例,我们会有多个文件,有可能存在你即不想提交又不想回滚的改变。GIT 提供了 stash 的概念,用于处理这种情况。

本地分支和远程分支
本地分支就是我们上面看到的;远程分支是当你开始使用一个分布式方式时,你不得不接触到的东西。因为 GIT 是一个分布式版本控制系统,你可以简单地从拥有同一仓库的不同团队的机器上 pull 或者 push 内容。所有这些都是通过远程分支实现的。

通过上面的例子,你应该明白,为什么 GIT 中创建分支是如此快速、简单和廉价。

Leave a Reply