Git 分支是“轻量级”的,因为它们只是指针:git 只是在特定提交处指向“master”或“development”或“trunk”或“myfeature”。当您在分支上重新提交时,指针会前进。考虑git-scm 文档中的这张图表,这是关于该主题的一流资源。
此图中的“master”分支指向 commit f30ab
。'testing' 分支指向 commit c2b9e
。HEAD
在这个图中是一个特殊的指针:大多数时候它指向另一个分支(在 git 术语中,它是一个“符号引用”)以显示当前工作目录的签出状态。例如,当您发出 时git checkout f30ab
,您将存储库置于“分离的 HEAD”状态。换句话说,您将指针从符号引用“testing”移动到提交,f30ab
.
举个例子,你应该能够在本地设置自己。
git init /tmp/test && cd /tmp/test ;# make a repo and cd to it
echo A > A ;# add a file
git add A && git commit -m "first commit" ;# make the first commit
echo B > B ;# add another file
git add B && git commit -m "second commit" ;# commit that one too
git checkout -b development ;# now let's checkout development
echo C > C ;# commit one more file
git add C && git commit -m "third commit" ;# and commit that final one
你现在得到了类似下面的东西。我没有omnigraffle,所以我们被一个有向图困住了:
* 93e71ee - (HEAD, development) third commit
/
* 6378754 - (master) second commit
* d2b4ba9 - first commit
正如您可以从括号中推断的那样,“master”指向 commit 6378754
,“development”指向 commit 93e71ee
,HEAD
指向“development”。不要相信我的话。自己探索指针:
$ cat .git/refs/heads/master ;# cat the 'master' pointer
5a744a27e01ae9cddad02531c1005df8244d188b
$ cat .git/refs/heads/development ;# now cat the 'development' one
93e71ee0a538b0e8ac548e3936f696fa4936f8dc
$ cat .git/HEAD ;# note that 'HEAD' points at 'development'
ref: refs/heads/development
$ git symbolic-ref HEAD ;# as we can also show with 'symbolic-ref'
refs/heads/development
当分支只是指针时,它们之间的切换是微不足道的。一种特殊情况是HEAD
。考虑当我们 checkout master 时会发生什么:
$ git checkout master ;# checkout master...
$ cat .git/HEAD ;# where are we now?
ref: refs/heads/master
检查提交怎么样?
$ git checkout d2b4ba9 ;# this will throw some advice
Note: checking out 'd2b4ba9'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
$ cat .git/HEAD ;# 'HEAD' points at a commit
d2b4ba97698d7f528f9ba1e08d978a70651b3b1d
$ git symbolic-ref HEAD ;# and thus isn't a symbolic reference
fatal: ref HEAD is not a symbolic ref
那建议是什么意思?对处于“分离 HEAD”状态的存储库进行提交会生成无法从任何分支访问的提交。当HEAD
更改(来自任何签出操作,例如git checkout master
)时,这些提交将丢失。这在图表中更容易看到:
echo D > D ;# add another file
git add D && git commit -m "fourth commit" ;# and commit it
让我们看看我们的图表。请注意,没有 git 命令会生成您在下面看到的内容。出于本示例的目的,我已经修改了现有输出。
* 93e71ee - (development) third commit
/
* 6378754 - (master) second commit
/
* / 72c1f03 - (HEAD) fourth commit
|/
* d2b4ba9 - first commit
HEAD
仍然是分离的。它指向72c1f03
。'master' 和 'development' 指向我们期望的位置,但72c1f03
无法从任何分支访问。那是个问题。如果我想留下72c1f03
来,我必须给它一个分支:
$ git checkout -b experimental ;# checkout 'experimental' based on '72c1f03'
$ cat .git/HEAD ;# HEAD is once again pointed at a branch
ref: refs/heads/experimental
$ git symbolic-ref HEAD ;# and is a symbolic ref
refs/heads/experimental
和图表:
* 93e71ee - (development) third commit
/
* 6378754 - (master) second commit
/
* / 72c1f03 - (HEAD, experimental) fourth commit
|/
* d2b4ba9 - first commit
Git 使分支变得容易。推送和拉取有关指针的信息比推送和拉取整套文件要快得多。切割一个分支需要几毫秒。这太容易了,几乎感觉不对。因此,git 允许更多分布式工作流选项,尽管它当然也可以处理集中式工作流选项。