在正常情况下,HEAD
要么指向 SHA1(在这种情况下,它称为detached),要么指向现有的分支引用(在这种情况下,指定的分支被认为已签出)。
当您签出new-branch
(HEAD
指向refs/heads/new-branch
)然后以某种方式设法删除new-branch
分支时,Git 只会删除分支的 ref 文件(.git/refs/heads/new-branch
)和分支的 reflog 文件(.git/logs/refs/heads/new-branch
)。Git 不会delete,HEAD
也不会更新它以指向其他地方(例如new-branch
曾经指向的 SHA1),因为不应该有需要——你不应该能够删除当前分支。所以HEAD
仍然引用现在删除的分支,这意味着HEAD
不再指向有效的提交。
如果你这样做了git checkout -f master
,Git 更新HEAD
指向refs/heads/master
,一个新条目被添加到HEAD
的 reflog 文件 ( .git/logs/HEAD
),文件被签出,并且索引被更新。所有这些都是正常的——当你检出另一个分支时,Git 总是这样做。
您遇到的问题来自于 reflog 文件如何更新以及如何git reflog
处理更新的 reflog 文件。每个 reflog 条目都包含一个“from”和“to”SHA1。当您从不存在的new-branch
分支切换到 时master
,Git 不知道“来自”SHA1 是什么。它没有出错,而是使用全零 SHA1 ( 0000000000000000000000000000000000000000
)。创建 ref 时也会使用全零 SHA1,因此这个最新的 reflog 条目使它看起来像是HEAD
刚刚创建的,而实际上它从未被删除。显然,git reflog
即使有更多条目,瓷器命令也会在遇到全零 SHA1 时停止遍历 reflog,这就是为什么git reflog
只打印一个条目的原因。
以下说明了这一点:
$ git init test
Initialized empty Git repository in /home/example/test/.git/
$ cd test
$ echo hi >file1
$ git add file1
$ git commit -m "test commit 1"
[master (root-commit) 3c79ff8] test commit 1
1 file changed, 1 insertion(+)
create mode 100644 file1
$ git checkout -b new-branch
Switched to a new branch 'new-branch'
$ echo test2 >file2
$ git add file2
$ git commit -m "test commit 2"
[new-branch f828d50] test commit 2
1 file changed, 1 insertion(+)
create mode 100644 file2
$ cat .git/HEAD
ref: refs/heads/new-branch
$ cat .git/refs/heads/new-branch
f828d50ce633918f2fcaaaad5a52ac1ffa1c81b1
$ git update-ref -d refs/heads/new-branch
$ cat .git/HEAD
ref: refs/heads/new-branch
$ cat .git/refs/heads/new-branch
cat: .git/refs/heads/new-branch: No such file or directory
$ cat .git/logs/HEAD
0000000000000000000000000000000000000000 3c79ff8fc5a55d7c143765b7f749db4dd8526266 Your Name <email@example.com> 1411018898 -0400 commit (initial): test commit 1
3c79ff8fc5a55d7c143765b7f749db4dd8526266 3c79ff8fc5a55d7c143765b7f749db4dd8526266 Your Name <email@example.com> 1411018898 -0400 checkout: moving from master to new-branch
3c79ff8fc5a55d7c143765b7f749db4dd8526266 f828d50ce633918f2fcaaaad5a52ac1ffa1c81b1 Your Name <email@example.com> 1411018898 -0400 commit: test commit 2
$ git checkout -f master
Switched to branch 'master'
$ cat .git/logs/HEAD
0000000000000000000000000000000000000000 3c79ff8fc5a55d7c143765b7f749db4dd8526266 Your Name <email@example.com> 1411018898 -0400 commit (initial): test commit 1
3c79ff8fc5a55d7c143765b7f749db4dd8526266 3c79ff8fc5a55d7c143765b7f749db4dd8526266 Your Name <email@example.com> 1411018898 -0400 checkout: moving from master to new-branch
3c79ff8fc5a55d7c143765b7f749db4dd8526266 f828d50ce633918f2fcaaaad5a52ac1ffa1c81b1 Your Name <email@example.com> 1411018898 -0400 commit: test commit 2
0000000000000000000000000000000000000000 3c79ff8fc5a55d7c143765b7f749db4dd8526266 Your Name <email@example.com> 1411018898 -0400 checkout: moving from new-branch to master
$ git reflog
3c79ff8 HEAD@{0}: checkout: moving from new-branch to master
如您所见,HEAD
的 reflog 仍然包含所有旧条目——它们只是没有由 . 显示git reflog
。我认为这是 Git 中的一个错误。
旁注:当你删除一个 ref 时,相应的日志也会被删除。我认为这是一个错误,因为除非您有日志备份,否则无法完全撤消意外删除的引用。