Git 在查看分支上的任何先前提交时开始分离头部?

Git started detaching head when viewing any previous commits on a branch?

我是一个新的 git 用户,所以这可能是一个愚蠢的问题,但是每当我在我的 Development 分支中检查任何以前提交的类似 git checkout 050aa9f 的东西时,突然间, git 立即分离头部:

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

HEAD is now at 050aa9f [Code commit title here]

但是当我从另一个分支签出提交时,例如 master 它不会分离头部。

我是不是以某种方式破坏了我的树?我怎样才能找到它的起点,我该如何解决它?

Because when I checkout my master branch, it does not create a detached head

首先,确保 use git switch, that way you are sure to deal with branch or commits (checkout deals also with files, now better served with )

其次,当您签出 master 时,您正在切换到对分支 HEAD 提交的间接引用。
间接原因:

  • .git/HEAD 将包括“refs/heads/master
  • .git/refs/heads/master 将包括实际的 SHA1

与 checkout/switch 提交相反,.git/HEAD 直接包含 SHA1(无分支间接)

这就是为什么我喜欢新的 git switch command: as I explain in "Why did my Git repo enter a detached HEAD state?",它默认需要一个分支,而不是提交。很难以 unwanted/unexpected 分离的 HEAD 结束。

whenever I [use] ... git checkout 050aa9f ... git immediately detaches the head

那是因为这种git checkout是专门要求detach HEAD.

任何 你使用的东西不是分支名称,但可以解析为提交哈希 ID,git checkout 会让你进入分离头模式。但是任何时候你使用 分支名称的东西,git checkout 都会让你进入正常模式。 (Git 不称此为“附加头”模式,但这是该模式的明显正确名称。)

这里有几个棘手的问题,其中一些可以通过使用新的(在 Git 2.23 及更高版本中)git switch 命令作为 来部分解决。我将在这里逐一介绍,但请记住,其中一些是高级 Git 并且您不需要立即了解所有内容。

  • Git 可以创建一个新分支,然后按名称检查该分支,从而产生附加的 HEAD。

  • 您可以使用带有分支名称的 --detach 来强制 Git 进入分离的 HEAD 模式,即使您提供了分支名称也是如此。

  • 使用 -b 选项,Git 将始终尝试创建一个新的分支名称(然后附加到它)。这在某些情况下可能会失败,但我不会在这里详细介绍。

  • 使用--track选项,您可以命名一个远程跟踪名称,如origin/develop和Git使用该名称来确定要创建的分支名称。 Git这里取的名字是把remote部分剥离出来的,所以运行ninggit checkout --track origin/develop和运行ninggit checkout -b develop --track origin/develop大致相同。我说 大致 相同,因为其他选项可以修改此行为。

  • git checkout 命令本身实现了在 Git 2.23 及更高版本中被拆分为两个单独的命令:git switchgit restore.在某些情况下,当您希望 git checkout 执行我将要描述的操作时,Git 会发现您有一个名为 dev 文件或文件夹 ] 并实施现在拆分为 git restore 的内容,而不是现在 git switch 的内容。这...不是一件好事,我们只是说,自从 Git 2.23 以来,git checkout 现在 告诉 你不确定你的意思在这里,不只是做 错误的 事情。

  • 给一个不存在但可以创建的分支命名,有时会导致创建该分支。例如,如果您 还没有 一个名为 dev 的分支,但是有一个 origin/dev,并且您 运行 git checkout dev ,您可能希望 Git 说:Huh ...没有 branch 命名为 dev 并且没有文件或文件夹命名为 dev。我不能把它变成一个分支名称,所以我会报错退出。 但事实并非如此。相反,Git 对自己说:嗯,没有 分支 命名为 dev,也没有文件或文件夹命名。但是有一个origin/dev。我敢打赌你想让我 创建 一个名为 dev 的分支,就好像你有 运行 git checkout --track origin/dev. 然后它做到了。


值得在这里准确描述旧的 git checkout 出了什么问题,而新的 git switch / git restore 拆分没有。 (而且,正如我提到的,git checkout 本身变得更加智能,因此它现在不会盲目地做错事——但是对于 2.23 之前的 Git 版本,请注意!)两个“ git checkout 的种类”是:

  • 切换分支的那个。这是一个非破坏性命令:如果您有未提交的工作,git checkout 可能 让您切换分支,但它只会在您未提交的 none工作将在此过程中被破坏。 (这个比较复杂,先不看,但是this question is all about that。)

    在Git 2.23中,您可以使用git switch来执行此命令。您仍然可以使用 git checkout

  • 破坏您未提交的工作的那个。这是破坏性的命令!假设您一直在编辑一些文件,并且您认为您尝试做一些有用的事情是徒劳的,现在应该彻底地、不可挽回地销毁。您想将事情恢复到原来的样子 — 至少,对于一个特定的文件,也许对于几个文件。

    在Git 2.23中,您可以使用git restore执行此命令,但在Git的每个版本中,您也可以在此处使用git checkout

这意味着一种 git checkout 是完全安全的:它永远不会破坏正在进行的工作。另一种 git checkout 非常危险:你在告诉 Git 请彻底清除我正在进行的工作。

这就是我上面提到的危险。假设您在名为 dev 的文件夹中有一堆文件,远程跟踪名称 origin/dev,但您 还没有一个名为 dev 的分支。如果你 运行:

git checkout dev

期望Git现在基于origin/dev创建一个名为dev的分支,你会得到一个令人讨厌的惊喜:Git(2.23之前)抹掉你的任何工作改为在 dev/* 文件上完成。


git checkout 可以做更多的事情,所有这些现在都是拆分命令的一部分。为了让这个答案简短,我将它们省略了。好吧,简短,总之。)