分支实际上等于他们在git中的最后一次提交吗?

时间:2018-12-15 22:42:51

标签: git

我在git中读到有关“分离头”的信息,这种情况发生在有人签出“提交”而不是“分支”时。

对我来说,好奇的部分(我无法通过手册页或Google进行验证)是分支在内部是否确实是对其上次提交的引用? (否则,我认为也许checkout命令可能不允许出现头部分离的情况,但实际上不是。)

如果上述猜测是正确的,我们可以说所有分支都是对它们顶端的提交的引用吗?

(基本上,当我从两个人那里读到这两个单独的评论时,我的疑惑越来越大:

  1. HEAD是当前已检出提交的引用(引用)。
  2. 签出分支机构而不是提交!

所以我认为也许第二个人建议使用分支进行检出,因为它可以保证将HEAD移至“ tip commit”。

2 个答案:

答案 0 :(得分:4)

是的,branch只是对提交的引用,一旦在其签出中进行新提交,该分支就会更新。您甚至可以手动创建分支。

(只需准备两个提交的仓库)

$ git init
Initialized empty Git repository in /home/che/foo/.git/
$ touch bar
$ git add bar
$ git commit -minitial
[master (root-commit) 5d512c6] initial
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 bar
$ touch second
$ git add second 
$ git commit -msecond_commit
[master 5aebe72] second_commit
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 second
$ git log --format='%H %s'
5aebe72d3f6cd72c3236021319d8ea43eae899fe second_commit
5d512c6fcec86f02d69a7b4f5f177ec1a6ecb32e initial

(现在在.git/refs/heads中创建一个新文件,这是git保留分支的地方之一)

$ echo 5d512c6fcec86f02d69a7b4f5f177ec1a6ecb32e > .git/refs/heads/old
$ git branch
* master
old

(或者您可以在文件中查找,有提交哈希,什么都没有)

$ fgrep '' .git/refs/heads/*
.git/refs/heads/master:5aebe72d3f6cd72c3236021319d8ea43eae899fe
.git/refs/heads/old:5d512c6fcec86f02d69a7b4f5f177ec1a6ecb32e

还有其他类型的引用(远程分支和标记),它们也可以存储在压缩文件中,因此这些文件操作将始终无法正常工作,但是您可以使用git show-refgit update-ref

答案 1 :(得分:2)

它的工作方式是HEAD实际上是文件.git / HEAD的内容。检出分支将导致包含分支名称(完整引用)的.git / HEAD。然后,分支名称将计算为.git目录中包含哈希的路径。因此,当您在该分支上进行新的提交时,只有ref更改为最新的提交哈希。 .git / HEAD不需要更改。

当您直接签出提交哈希时,.git / HEAD仅包含提交哈希。例如,如果您签出表示 master 中最后一次提交的提交哈希,则它与签出 master 差不多。

唯一的区别是,提交到 master 或从公共存储库中提取更新不会更改您检出的内容,并且从该状态提交不会更改 master 分支。

一个示例会话,展示了以下几点:

util$ cat .git/HEAD
ref: refs/heads/master
util$ cat .git/refs/heads/master
3e213b3d373a9d157426db2ae40e0b5b8ba9c3b2
util$ git checkout 3e21
Note: checking out '3e21'.

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.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b <new-branch-name>

HEAD is now at 3e21 edit_with_editor() takes a line kwarg
util$ cat .git/HEAD
3e213b3d373a9d157426db2ae40e0b5b8ba9c3b2

因此'master'确实会间接地评估为提交哈希:

util$ git rev-parse master
3e213b3d373a9d157426db2ae40e0b5b8ba9c3b2

如果我在这种状态下进行提交,它将更改HEAD而不是master:

util$ echo Example change >> README
util$ git commit -a -m "example commit in detached head state"
[detached HEAD 45d4] example commit in detached head state
 1 file changed, 4 insertions(+)
 create mode 100644 README
util$ cat .git/HEAD
45d4b4f7816dc7626be6f7166f7cf972dc18a9d7
util$ git rev-parse master
3e213b3d373a9d157426db2ae40e0b5b8ba9c3b2