【问题标题】:How do I fix a Git detached head?如何修复 Git 分离头?
【发布时间】:2012-05-01 00:11:24
【问题描述】:

我在我的存储库中做一些工作,并注意到一个文件有本地更改。我不再想要它们,所以我删除了文件,以为我可以签出一个新副本。我想做的 Git 相当于

svn up .

使用git pull 似乎不起作用。一些随机搜索将我带到一个有人推荐的网站

git checkout HEAD^ src/

src是包含被删除文件的目录)。

现在我发现我有一个分离的头。我不知道那是什么。如何撤消?

【问题讨论】:

  • git checkout master 会让你回到主分支。如果您想清除任何工作副本更改,您可能想做git reset --hard
  • 如果你还没有提交,你可以做到git checkout -- src/
  • 试试这个:link。总之create temp branch - checkout temp branch - checkout master - delete temp branch
  • @AbeVoelker working copy changes 在 cmets 中是什么意思?您是指在签出另一个提交后对文件所做的更改(即,您在分离头状态下所做的更改)?

标签: git git-detached-head


【解决方案1】:

Detached head 意味着你不再在一个分支上,你已经签出了历史中的一个提交(在这种情况下是 HEAD 之前的提交,即 HEAD^)。

如果您想删除与分离的 HEAD 相关的更改

你只需要检查你所在的分支,例如

git checkout master

下次你改变了一个文件,想要恢复到它在索引中的状态时,不要先删除文件,直接做

git checkout -- path/to/foo

这会将文件 foo 恢复到它在索引中的状态。

如果您想保留与分离的 HEAD 关联的更改

  1. 运行 git branch tmp - 这会将您的更改保存在名为 tmp 的新分支中。
  2. 运行git checkout master
  3. 如果您想将所做的更改合并到master,请从master 分支运行git merge tmp。运行 git checkout master 后,您应该在 master 分支上。

【讨论】:

  • "这会将文件 foo 恢复到您对其进行任何更改之前的状态。" --> 它将恢复到 index 中的状态 - 请编辑
  • 为什么会出现这个错误呢?这是我讨厌 git 的原因之一——有时是完全随机的行为。 Mercurial 从来没有遇到过这样的问题。
  • @VioletGiraffe 这既不是错误也不是随机的——它只是你的存储库在签出之前的提交时进入的状态。 “分离的头”用作警告,如果您打算从该点开始做任何工作,您可能还想创建或指向一个分支。但如果你只是想查看那个标签或提交,那么处于分离的头部状态并没有错。
  • 如果您已承诺分离头,请不要这样做,请参阅其他答案。如果是这样,您可以在Previous HEAD position was 7426948... 中查看之前提到的头部 git
  • @VioletGiraffe:你有一个基于 Mercurial 的心智模型,但你使用的是 Git。如果你不愿意调整你的心智模型以适应 Git 的模型,那么事情将继续显得随机。这就像你戴着 VR 护目镜在外面走来走去,你以为你在开飞机,但实际上你是在过马路。你会被汽车撞到的。
【解决方案2】:

如果您更改了不想丢失的文件,可以推送它们。我已经在分离模式下提交了它们,之后你可以移动到一个临时分支以便稍后在 master 中集成。

git commit -m "....."
git branch my-temporary-work
git checkout master
git merge my-temporary-work

摘自:

What to do with commit made in a detached head

【讨论】:

  • 我发现这是首选的解决方案 - 特别是如果您想保留在签出单个版本时所做的更改。
  • @adswebwork:我同意。所有其他答案都建议恢复到以前的状态并丢失在分离的头部状态下本地所做的更改。
  • 为什么不git stash?因为这是我想到的第一件事。创建一个新的分支将是一个矫枉过正。
  • 你也可以git rebase my-temporary-work 然后删除分支git branch -d my-temporary-work 这样看起来好像你首先提交到了正确的分支。
  • @geekay git stash 听起来确实是这个案例的完美工具。您能否用建议的步骤写一个答案来实现这一目标?
【解决方案3】:

无需创建临时分支的解决方案。

如何退出(“修复”)分离的 HEAD 状态当您已经在此模式下更改了某些内容,并且可以选择保存您的更改:

  1. 提交您想要保留的更改。如果您想要接管您在分离 HEAD 状态下所做的任何更改,请提交它们。喜欢:

    git commit -a -m "your commit message"
    
  2. 放弃您不想保留的更改。硬重置将放弃您在分离 HEAD 状态下所做的任何未提交的更改:

    git reset --hard
    

    (没有这个,第 3 步将失败,抱怨分离的 HEAD 中修改了未提交的文件。)

  3. 检查你的分支。通过检查你之前工作的分支来退出分离的 HEAD 状态,例如:

    git checkout master
    
  4. 接管您的提交。您现在可以通过挑选樱桃来接管您在分离 HEAD 状态下所做的提交,如 my answer to another question 所示。

    git reflog
    git cherry-pick <hash1> <hash2> <hash3> …
    

【讨论】:

  • git reset --hard 正是我需要的,因为我希望上游成为源,并且应该删除本地更改。
【解决方案4】:

分离的头部意味着:

  1. 你不再在树枝上,
  2. 您已签出历史记录中的单个提交

如果您没有更改:您可以通过应用以下命令切换到 master

  git checkout master

如果您有想要保留的更改:

在分离 HEAD 的情况下,提交工作正常,除了没有命名分支得到更新。要使用您提交的更改更新主分支,请在您所在的位置创建一个临时分支(这样临时分支将拥有您在分离的 HEAD 中所做的所有提交更改),然后切换到主分支并将临时分支与主人。

git branch  temp
git checkout master
git merge temp

【讨论】:

【解决方案5】:

HEAD 是一个指针,它直接或间接指向一个特定的提交:

附加  HEAD 表示它附加到某个分支(即它指向分支)。
已分离 HEAD 意味着它附加到任何分支,即它直接指向某个提交。

换句话说:

  • 如果它直接指向提交,则 HEAD 分离
  • 如果它间接指向一个提交(即它指向一个分支,而该分支又指向一个提交),则 HEAD 被附加

为了更好地理解附加/分离 HEAD 的情况,让我们展示导致上面四组图片的步骤。

我们从存储库的相同状态开始(所有象限中的图片都相同):


现在我们要执行git checkout——在各个图片中使用不同的目标(它们顶部的命令变暗以强调我们只是打算应用这些命令):


这是执行这些命令后的情况:

如您所见,HEAD 指向git checkout 命令的目标 — 指向一个分支(四联体的前 3 张图像),或者(直接) 到 commit(四联体的最后一张图像)。

工作目录的内容也被更改,以符合适当的提交(快照),即 HEAD (直接或间接)指向的提交。


所以现在我们处于与此答案开头相同的情况:

【讨论】:

  • 缺少的是:“当我签出一个也是某个分支顶部的数字提交时,它会导致头部分离,还是会使用关联的分支来代替?”我的猜测是:“然后没有分离的头”
  • @U.Windl,回答你自己——然后 HEAD 将指向一个分支(该分支又将指向一个提交),或者然后 HEAD 将 直接 指向一个提交?在您回答之后查看我的回答开头。
  • 我了解可以直接签出修订而不签出恰好指向它的分支。逻辑上:两个或多个分支可以指向同一个版本。如果您然后通过其哈希检查修订,该命令会选择哪个分支?
  • @Mike,不会选择任何分支,所有分支(作为提交的指针)将保持不变 - 您可以在我的答案的所有图片(棕色框)中看到它。 只有 HEAD 不会指向分支,而是直接指向提交,因此您将以“分离的 HEAD”状态结束 - 请参见最后一张(右下角)图片。 - 尽管有 2 个分支指向同一个提交,但如果您通过哈希选择此提交,HEAD 将不会指向这 2 个分支之一,而是 直接 指向该提交。
  • @MarianD 我认为有一点误解 - 我在解释为​​什么当您通过哈希选择修订时,您不能期望 Git 签出分支。
【解决方案6】:

如果您进行了更改,然后意识到自己处于超然状态,您可以执行以下操作:stash -> checkout master -> stash pop:

git stash
git checkout master   # Fix the detached head state
git stash pop         # Or for extra safety use 'stash apply' then later 
                      #   after fixing everything do 'stash drop'

您将拥有未提交的更改和正常的“附加” HEAD,就像什么都没发生一样。

【讨论】:

  • 已为这个坏男孩添加了书签 - 节省了创建临时分支的时间。辛苦了。
  • 在检查 git 子模块然后对其进行更改后,我经常会处于分离的 HEAD 状态。我发现这是修复问题的最佳和最简单的解决方案,因此我可以保留我的更改。
  • 如果您已经在分离状态下提交了更改,这不起作用?
【解决方案7】:

这就是我在意识到自己处于超然状态并且已经做出一些改变之后所做的事情。

我提交了更改。

$ git commit -m "..."
[detached HEAD 1fe56ad] ...

我记得提交的哈希 (1fe56ad)。然后我检查了我应该在的分支。

$ git checkout master
Switched to branch 'master'

最后我将提交的更改应用到了分支。

$ git cherry-pick 1fe56ad
[master 0b05f1e] ...

我认为这比创建临时分支要容易一些。

【讨论】:

  • 这应该是答案。它会取回你的 nuked 文件。
  • 是的,这确实是最简单的事情——足够简单,下次发生时无需搜索网络即可记住。提交,记下哈希,返回到你打算提交的分支,然后git cherry-pick &lt;hash&gt;
  • 感谢您的解决方案。这有帮助。我还可以补充一点,我必须执行“git push origin master”,以便我的 master 和 origin/master 指向同一个提交。
  • 这基本上是tanius' answer(一年多前发布)。
  • 感谢这个愉快的选择恢复了最后的分离头更改
【解决方案8】:

当您签出 git 中的特定提交时,您最终会处于 分离头 状态...也就是说,您的工作副本不再反映命名引用的状态(例如“掌握”)。这对于检查存储库的过去状态很有用,但如果您实际尝试还原更改,则不是您想要的。

如果您对特定文件进行了更改并且只想丢弃它们,则可以使用checkout 命令,如下所示:

git checkout myfile

这将丢弃任何未提交的更改并将文件恢复到它在当前分支头部的任何状态。如果您想放弃已经提交的更改,您可能需要使用reset 命令。例如,这会将存储库重置为先前提交的状态,丢弃任何后续更改:

git reset --hard HEAD^

但是,如果您与其他人共享存储库,git reset 可能会造成干扰(因为它会删除存储库历史的一部分)。如果您已经与其他人共享了更改,您通常希望查看git revert,它会生成一个“反提交”——也就是说,它会创建一个“撤消”相关更改的新提交。

The Git Book 有更多详细信息。

【讨论】:

  • 正如我在@ralphtheninja 的回答中所说,git checkout path/to/foo 可能与git checkout some-branch 冲突,因此最好使用git checkout -- path/to/foo 来避免这些冲突。
【解决方案9】:

由于“分离的头部状态”让您处于临时分支,只需使用 git checkout - 即可将您置于您所在的最后一个分支。

【讨论】:

  • 小心,当你处于分离的头部状态时,你会丢失你所做的任何提交。
  • @Ajak6 你并没有真正失去那些提交。它们仍然可以通过git reflog 获得,并且可以被接管到新分支或通过git cherry-pick 进入现有分支。见this question
  • 但是,在一段时间后,任何没有被分支或标签指向的修订(或者不是当前工作修订),或者不是这样的祖先修订,可能被永久删除。
【解决方案10】:

你可能做过git reset --hard origin/your-branch

试试git checkout your-branch

【讨论】:

  • 非常感谢您先生,您是一位绅士和一位学者。
  • 我什至无法告诉你我对此有多么感激 - 在过去的一个小时里,我感觉自己就像被困在一件直筒夹克里。有人可以解释一下为什么会这样吗?
【解决方案11】:

为了进一步澄清@Philippe Gerber 的回答,这里是:

在这种情况下,cherry-pick 之前需要一个 git checkout master。此外,它只需要在detached head 中使用commit

【讨论】:

    【解决方案12】:

    附录

    如果您希望返回的分支是您最后一次结帐,您可以简单地使用checkout @{-1}。这将带您回到之前的结帐。

    此外,您可以将此命令别名为 git global --config alias.prev 等,这样您只需键入 git prev 即可切换回上一个结帐。

    【讨论】:

    • 我需要的是在我重新设置分支之前找回我的 HEAD 的方法。这解决了我的问题!非常感谢:)
    【解决方案13】:

    Detached head 意味着你没有正确检出你的分支或者你只是检出了一个提交。

    如果您遇到此类问题,请先存储您的本地更改,以免丢失您的更改。

    之后...使用以下命令检查您想要的分支:

    假设你想要分支 MyOriginalBranch:

    git checkout -b someName origin/MyOriginalBranch

    【讨论】:

      【解决方案14】:

      处于“分离头”意味着 HEAD 指的是特定的未命名提交(与命名分支相反)(参见:https://git-scm.com/docs/git-checkout 部分 分离头)。 实际上,这意味着您已经签出了一个提交,但没有与之关联的分支名称。

      您可以选择只创建一个与您的提交相关联的新分支

      git branch new-branch-name.

      这允许您将当前状态保存在新分支中。

      或者你可能想回到之前的状态然后再做这个,你需要选择之前被选中的分支

      git checkout @{-1}

      【讨论】:

        【解决方案15】:

        这种方法可能会丢弃部分提交历史,但如果旧主分支和当前状态的合并很棘手,或者您只是不介意丢失部分提交历史。

        为了简单地保持当前状态,不合并,将当前分离的 HEAD 变成 master 分支:

        1. 手动备份存储库,以防出现意外错误。
        2. 提交您要保留的最后更改。
        3. 创建一个临时分支(我们将其命名为 detached-head),其中将包含处于当前状态的文件:
        git checkout -b detached-head
        
        1. (a) 如果不需要保留 master 分支,请删除它
        git branch -D master
        
        1. (b) 或者如果您想保留它,请重命名
        git branch -M master old-master
        
        1. 将临时分支重命名为新的主分支
        git branch -M detached-head master
        

        来源:改编自 Gary Lai 的 this Medium article

        【讨论】:

          【解决方案16】:

          Git 告诉我该怎么做。

          如果你输入了:

          git checkout <some-commit_number>
          

          保存状态

          git add .
          git commit -m "some message"
          

          然后:

           git push origin HEAD:<name-of-remote-branch>
          

          【讨论】:

            【解决方案17】:

            通常HEAD 指向一个分支。当它不指向分支时,而是指向像69e51 这样的提交哈希,这意味着你有一个分离的HEAD。您需要将它指向两个分支来解决问题。你可以做两件事来修复它。

            1. git checkout other_branch // 当您需要该提交中的代码时不可能hash
            2. 创建一个新分支并将提交哈希指向新创建的分支。

            HEAD 必须指向一个分支,而不是提交哈希是黄金法则。

            【讨论】:

            • 这就是我遇到这个错误的原因。我签出了一个修订版,然后再次签出到当前/最新的修订版,而不是签出到分支,这将正确地附加头部。感谢您的帮助。
            【解决方案18】:

            当您处于分离头的情况并创建新文件时,首先确保将这些新文件添加到索引中,例如:

            git add .
            

            但是,如果您只更改或删除了现有文件,您可以通过以下方式添加 (-a) 并同时使用消息 (-m) 提交:

            git commit -a -m "my adjustment message"
            

            然后您可以使用当前状态简单地创建一个新分支:

            git checkout -b new_branch_name
            

            您将拥有一个新分支,并且您的所有调整都将在该新分支中进行。然后,您可以随意继续推送到远程和/或结帐/拉取/合并。

            【讨论】:

              【解决方案19】:

              意识到我有一个超然的头脑,但不知道我是如何设法得到它的(比如三个提交),我还发现尝试 mergerebasecherry-pick 会触发数百个合并冲突,所以我采取了不同的方法:

              1. (假设一切都已提交(工作树是“干净的”))

              2. 保存我的提交信息:git log &gt; /tmp/log

              3. 保存我的工作树:mkdir /tmp/backup &amp;&amp; cp -a all_my files_and_directories /tmp/backup

              4. 恢复为master:git checkout master

              5. 删除所有工作文件和目录:rm ...

              6. 使用备份:cp -a /tmp/backup/. .

              7. git addgit commit 使用来自已保存 /tmp/log 的消息,可能会重复使用不同的文件子集...

              缺点是如果一个文件自master 以来多次更改,您会丢失提交历史记录,但最后我有一个干净的master

              【讨论】:

                【解决方案20】:

                我想保留我的更改,所以我只是解决这个问题......

                git add .
                git commit -m "Title" -m "Description"
                (so i have a commit now example: 123abc)
                git checkout YOURCURRENTBRANCH
                git merge 123abc
                git push TOYOURCURRENTBRANCH
                

                对我有用

                【讨论】:

                  【解决方案21】:

                  我也遇到过类似情况。
                  出于某种原因,我最终得到了一个分离的头——我已经在与我认为我所在的分支相同的路径上进行了提交——例如,HEAD 是分支标签的子标签,但由于某种原因,分支标签一直停留在历史上提交...可能是因为我推了??

                  它不会让我推动,因为我不被认为是在我认为我所在的分支上。

                  我不想改变我的任何历史或做任何樱桃采摘,我刚刚在分支上工作了大约 8 周,所以reset --hard 让我有点紧张!

                  解决方案只是执行以下操作:

                  git branch -f myStuckBranch HEAD
                  git checkout myStuckBranch
                  

                  即使 HEAD 和 myStuckBranch 现在指向同一事物,您也需要进行结帐,因为您仍被认为处于分离的头部状态(而不是在分支上)

                  我不是 git 专家(主要使用 mercurial,它永远不会造成这种奇怪的情况),但我对这个命令的理解是它只是说 “将 myStuckBranch 更改为指向 HEAD”。

                  我经常发现自己在获取后使用此命令合并来自 master 的更改,而无需交换我的工作目录 - 否则它会尝试使用旧(无趣)版本的 master:

                  git fetch
                  git branch -f master origin/master  -- err yeah don't just ignore what's been going on remotely - eg point my master at the real master
                  git merge master -- merge the changes into my local branch
                  

                  不得不一直手动这样做有点烦人,但总比不得不更改工作目录来更新另一个分支以合并其中的更改要好。

                  【讨论】:

                    【解决方案22】:
                    git pull origin master
                    

                    为我工作。这只是明确给出远程和分支名称。

                    【讨论】:

                      【解决方案23】:

                      分离的 HEAD 表示您当前不在任何分支上。如果您想保留当前的更改并简单地创建一个新分支,您可以这样做:

                      git commit -m "your commit message"
                      git checkout -b new_branch
                      

                      之后,您可能希望将此新分支与其他分支合并。总是有用的是 git "a dog" 命令:

                      git log --all --decorate --oneline --graph
                      

                      【讨论】:

                        【解决方案24】:

                        这对我有用,它将为分离头分配一个新分支:

                        git checkout new_branch_name detached_head_garbage_name

                        【讨论】:

                          【解决方案25】:

                          使用 git rebase 你可以将你的 HEAD 移动到所需的提交

                          假设您的分支处于分离状态,如下所示:

                          * bfcb8f9 Commit 4
                          * 540a123 Commit 3
                          * 4356d64 Commit 2
                          | * fecb8d2 Commit 2
                          |/
                          | * 8012f45 Commit 2x
                          |/
                          | * 6676d15 (HEAD -> master) Commit 2 --amend
                          |/
                          * 1818f91 Commit 1
                          

                          分离的头部是通过错误地变基创建的,指向一个分离的提交,该提交是先前由于 git commit --amend 命令而创建的。

                          如果您想将 HEAD 引用移动到最近的提交,请使用您想要指向的所需 HASH 提交应用 rebase。在此示例中,哈希是最近提交的:

                          git rebase bfcb8f9
                          

                          这将使您的分支的 HEAD 指向所需的提交(在这种情况下是最新的):

                          * bfcb8f9 (HEAD -> master) Commit 4
                          * 540a123 Commit 3
                          * 4356d64 Commit 2 --amend
                          | * fecb8d2 Commit 2
                          |/
                          | * 8012f45 Commit 2x
                          |/
                          | * 6676d15 Commit 2
                          |/
                          * 1818f91 Commit 1
                          

                          【讨论】:

                            猜你喜欢
                            • 2022-06-26
                            • 1970-01-01
                            • 2013-10-31
                            • 2017-06-05
                            • 2022-01-14
                            相关资源
                            最近更新 更多