Git 中没有这样的东西基础分支的一个分支。相反,只有一个当前提交,Git 称之为树枝的尖端.
绘制图表
为了直观地理解这一点,您应该从绘制(至少部分)Git 开始提交图。这是一个小型存储库的示例,其中只有三个提交:
A <-B <-C <--master
任何给定提交的“真实名称”都是那些丑陋的大哈希 ID 之一,c0ffeeface1deadbead...
等等。这个哈希 ID 唯一标识了特定的提交,实际上是made通过散列(因此称为“散列 ID”)contents该提交的。它们看起来很随意,很难记住,所以这里我只使用单个大写字母。
Git“看到”图表的方式是它首先读取分支名称,例如master
。该分支名称包含hash ID像这样的提交C
。我们这么说master
指着 commit C
.
同时,承诺C
本身包含其前一个(或parent) 犯罪B
。所以我们说C
指着B
, 一样的方法master
指着C
.
同样,提交B
指向回A
. A
是有史以来的第一次提交,所以它没有地方可以指向……所以它就没有。我们称之为A
a root提交,它让我们(和 Git)停止向后工作。
这些内部箭头绘制起来有点烦人,请注意,B
实际上是part of C
本身,所以它永远不会改变(如果我们尝试改变这部分C
,我们得到一个新的,不同的犯罪)。所以我们可以不再费心画它们,而是写:
A--B--C <-- master
箭头从 a 出来分店名称,不过,是not恒定,这就是整个想法的所在提示提交来自。
假设我们想添加一个新的提交master
。我们进行 Git 所需的所有常规设置(添加或修改一些文件并使用git add
)然后运行git commit
. Git:
当然,没有理由再在图中保留这种扭结:
A--B--C--D <-- master
这就是 Git 中分支的生长方式。
做一个new分支,Git 只是创建分支名称指向一些现有的提交:
A
\
B
\
C
\
D <-- master
我们可以选择这些提交中的任何一个,并在其中创建一个新的分支名称。我们来选一下B
和做newbr
点那里:
A
\
B <-- newbr
\
C
\
D <-- master
我们会这样做git branch newbr <thing-that-finds-B>
.
但是我们如何找到提交呢?
我们如何找到B
?好吧,一种方法是跑步git log
并剪切并粘贴哈希 ID。但另一种方法是使用分支名称。名字newbr
现在指向B
。如果我们想再提交一个分支点B
too:
git branch thirdbr newbr
这使得 Git 查找newbr
,这指向B
,并创建新名称thirdbr
,这...也指向B
:
A--B <-- newbr, thirdbr
\
C--D <-- master
这就是为什么在 Git 中创建分支如此之快:它几乎什么都不做!它只是创建一个指向某些现有提交的标签。
某些分支名称指向的提交称为提示提交那个分支的。请注意,一次提交可以同时成为多个分支的尖端。这是关于 Git 的一个更大的事情的一部分:一些提交正在进行many分支,全部同时进行。例如,提交A
—根提交—已开启every分支。 (一个存储库中可以有多个根提交,尽管这有点棘手,但我们现在不需要担心这一点。您无法使用到目前为止显示的命令来做到这一点。)
是什么让分支名称如此特别
然而,分支标签的特殊属性是它们move。他们不仅会动,还会动自动地.
当我们做出新的提交时我们已经看到了这一点D
。 Git 将新提交的 ID 写入master
. But: Git 如何知道使用master
?就此而言,Git 如何知道使用C
当 Git 提交时作为父提交D
?
好吧,当然我们当时只有一个分支,但是让我们进行一个新的提交now,现在我们有了三个标签master
, newbr
, and thirdbr
。首先,让我们做git checkout thirdbr
,并绘制结果:
A--B <-- newbr, thirdbr (HEAD)
\
C--D <-- master
Nothing really changed in the drawing,1 except that I added the word HEAD
here. HEAD is how Git knows which branch-and-commit are the current branch and commit.
现在我们进行通常的修改一些文件,git add
, and git commit
。 Git 写出新的提交,并将其父级设置为提交B
。 Git 看到current分支是thirdbr
and thirdbr
指着B
,所以当前的commit is B
。让我们绘制新的提交E
:
E
/
A--B <-- newbr
\
C--D <-- master
唯一剩下的就是移动当前分支name thirdbr
这样它就指向新的提交E
:
E <-- thirdbr (HEAD)
/
A--B <-- newbr
\
C--D <-- master
我们都完成了:我们已经向分支添加了一个新的提交thirdbr
(仍然是 HEAD,因此仍然是当前分支,但现在E
是当前提交)。
当您向当前分支添加提交时,HEAD 会告诉您当前提交是什么以及新提交的位置。HEAD 通常包含分支的名称,这就是git checkout
作用:它检查特定的提交(通常是现有分支的提示提交),然后设置文件HEAD
记住name的分支机构。这是分支名称本身记住的提示提交.
Using git checkout -b newname commit
意思是:“检查指定的commit,然后创建一个新的分支名称newname指向该提交,然后将 HEAD 设置为该新名称。”如果省略commit部分,默认是使用HEAD。自从头is(总是)当前提交,Git 会跳过“签出”部分,只创建新的分支名称并将其存储到HEAD
file.
1While nothing changed in the graph, Git did have to update our work-tree, and the index, so that we could have the files as of the way they were at commit B
.