关于git:我如何协调分离的HEAD与master / origin?


How can I reconcile detached HEAD with master/origin?

我对Git的分支复杂性是个新手。我总是在一个分支上工作,提交更改,然后定期地推送到我的远程源站。

最近的某个地方,我对一些文件进行了重置,以使它们脱离提交阶段,然后执行了rebase -i,以消除最近的几个本地提交。现在我的状态我不太明白。

在我的工作区域,git log准确地显示了我所期望的——我坐的是正确的火车,承诺我不想去,新的承诺在那里等等。

但我只是推到了远程存储库,有什么不同——我在REBASE中杀死的两个提交被推了,而在本地提交的新提交不在那里。

我认为"master/origin"与head是分离的,但我并不完全清楚这意味着什么,如何使用命令行工具可视化它,以及如何修复它。


首先,让我们澄清什么是头,当它被分离时它意味着什么。好的。

head是当前签出提交的符号名。当head未被分离时("normal"1情况:您签出了一个分支),head实际指向分支的"ref",分支指向commit。因此,头部"附着"在树枝上。当您进行新的提交时,head指向的分支将被更新为指向新的提交。头部自动跟随,因为它只是指向分支。好的。

  • git symbolic-ref HEAD生成refs/heads/master。名为"master"的分支已签出。
  • git rev-parse refs/heads/master产量17a02998078923f2d62811326d130de991d1a95a。该提交是主分支的当前提示或"head"。
  • git rev-parse HEAD也产生17a02998078923f2d62811326d130de991d1a95a。这就是所谓的"符号参考"。它通过其他引用指向一个对象。(符号引用最初是作为符号链接实现的,但后来改为带有额外解释的普通文件,以便可以在没有符号链接的平台上使用。)

我们有HEADrefs/heads/master17a02998078923f2d62811326d130de991d1a95a。好的。

分离head时,它直接指向commit,而不是通过分支间接指向commit。你可以把一个分开的脑袋想象成是在一个无名的树枝上。好的。

  • git symbolic-ref HEADfatal: ref HEAD is not a symbolic ref故障
  • git rev-parse HEAD生成17a02998078923f2d62811326d130de991d1a95a。因为它不是一个符号引用,所以必须直接指向提交本身。

我们有HEAD17a02998078923f2d62811326d130de991d1a95a。好的。

使用分离的头要记住的重要一点是,如果它指向的提交没有被引用(没有其他引用可以到达它),那么当您签出其他提交时,它将成为"悬空的"。最终,这些挂起的提交将通过垃圾收集过程进行修剪(默认情况下,这些提交将保留至少2周,并且可以通过head的reflog引用保留更长的时间)。好的。

一用一个独立的脑袋做"正常"的工作是非常好的,你只需要跟踪你正在做的事情就可以避免从反射波中捕捉掉的历史。好的。

交互钢筋的中间步骤是用一个分离的头完成的(部分是为了避免污染活动分支的回流)。如果您完成了完整的REBASE操作,它将使用REBASE操作的累积结果更新原始分支,并将头重新附加到原始分支。我的猜测是,您从未完全完成REBASE过程;这将使您有一个独立的头指向最近由REBASE操作处理的提交。好的。

要从您的情况中恢复,您应该创建一个分支,指向分离的头当前指向的提交:好的。

1
2
git branch temp
git checkout temp

(这两个命令可以缩写为git checkout -b temp)好的。

这将使您的头重新连接到新的temp分支。好的。

接下来,您应该将当前提交(及其历史记录)与您期望在其上工作的正常分支进行比较:好的。

1
2
3
git log --graph --decorate --pretty=oneline --abbrev-commit master origin/master temp
git diff master temp
git diff origin/master temp

(您可能希望尝试使用日志选项:添加-p,保留--pretty=…以查看整个日志消息等。)好的。

如果您的新temp分支看起来不错,您可能需要更新(例如)master以指向它:好的。

1
2
git branch -f master temp
git checkout master

(这两个命令可以缩写为git checkout -B master temp)好的。

然后可以删除临时分支:好的。

1
git branch -d temp

最后,您可能希望推动重新建立的历史:好的。

1
git push origin master

如果远程分支不能"快速转发"到新的提交(即,您删除了一些现有的提交,或重写了一些现有的提交,或以其他方式重写了一些历史记录),则可能需要将--force添加到该命令的末尾以进行推送。好的。

如果您正在执行一个重新平衡操作,您可能应该清理它。您可以通过查找目录.git/rebase-merge/来检查REBASE是否正在进行中。只需删除该目录(例如,如果您不再记得活动的REBASE操作的目的和上下文),就可以手动清除正在进行的REBASE。通常,您会使用git rebase --abort,但这会执行一些您可能想要避免的额外重置(它将头移回原始分支并将其重置回原始提交,这将撤消我们在上面所做的一些工作)。好的。好啊。


只要这样做:

1
git checkout master

或者,如果您有要保留的更改,请执行以下操作:

1
2
git checkout -b temp
git checkout -B master temp


我遇到了这个问题,当我读到最热门的答案时:

HEAD is the symbolic name for the currently checked out commit.

我想:啊哈!如果HEAD是当前签出提交的符号名称,我可以通过将其重新与master进行比较,使其与master进行比较:

1
git rebase HEAD master

这个命令:

  • 查看master
  • 确定HEAD的父承诺返回到HEADmaster的分歧点。
  • master之上播放那些承诺
  • 最终结果是,所有在HEAD而不是master中的承诺都在master中。master仍处于签出状态。

    关于遥控器:

    a couple of the commits I'd killed in the rebase got pushed, and the new ones committed locally aren't there.

    无法再使用本地历史记录快速转发远程历史记录。您需要强制push(git push -f来覆盖远程历史记录。如果您有任何合作者,那么与他们进行协调通常是有意义的,这样每个人都在同一个页面上。

    当您将master推到远程origin后,您的远程跟踪分支origin/master将更新为指向与master相同的提交。


    请查看此处了解分离头的基本说明:

    网址:http://git-scm.com/docs/git-checkout

    可视化命令行:

    1
    git branch

    1
    git branch -a

    您将得到如下输出:

    1
    2
    3
    * (no branch)
    master
    branch1

    * (no branch)显示你是一个独立的人。

    您可以通过执行git checkout somecommit等操作来达到这种状态,它会警告您以下事项:

    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

    现在,要让他们成为大师:

    做一个git reflog或者甚至仅仅是git log并注意你的承诺。现在以东十一〔四〕和以东十一〔五〕犯了罪。

    1
    git merge HEAD@{1}

    编辑:

    要添加,使用git rebase -i不仅可以删除/杀死您不需要的提交,还可以编辑它们。只要在提交列表中提到"编辑",您就可以修改您的提交,然后发布一个git rebase --continue来继续。这可以确保你永远不会进入一个超然的头脑。


    把你的独立承诺放到它自己的分支上

    只需运行git checkout -b mynewbranch

    然后运行git log,您将看到这个新分支上的commit现在是HEAD


    如果您只有主分支并且想要返回到"开发"或某个特性,那么只需执行以下操作:

    1
    git checkout origin/develop

    注:检查来源/发展。

    你处于超然状态。你可以四处看看,做实验更改并提交它们,您可以放弃在此过程中所做的任何提交。通过执行另一个签出而不影响任何分支的状态…

    然后

    1
    git checkout -b develop

    它的工作原理是:


    如果要推动当前分离的头部(之前检查git log),请尝试:

    1
    git push origin HEAD:master

    把你的超支头送到原点的总支。如果您的推送被拒绝,请先尝试git pull origin master以获得源站的更改。如果您不关心源站的更改,并且由于您做了一些有意的重新平衡,并且您希望用当前分离的分支替换源站/主站,那么您可以强制它(-f)。如果您失去了对以前提交的一些访问权,您可以始终运行git reflog来查看所有分支的历史记录。

    要在保留更改的同时返回主分支,请尝试以下命令:

    1
    2
    git rebase HEAD master
    git checkout master

    请参见:git:"目前不在任何分支上。"是否有一种简单的方法可以在保留更改的同时回到分支上?


    我今天刚遇到这个问题,我确信我通过以下方式解决了它:

    1
    2
    3
    git branch temp
    git checkout master
    git merge temp

    我在工作电脑上找到了解决方法,现在我在个人电脑上遇到了同样的问题。所以我必须等到星期一,当我回到工作电脑,看看我是如何做到的。


    以下内容适用于我(仅使用分支主机):

    1
    2
    3
    git push origin HEAD:master
    git checkout master        
    git pull

    第一个将分离的头部推到远程原点。

    第二个移动到分支主机。

    第三个恢复连接到分支主机的头。

    如果推送被拒绝,第一个命令可能会出现问题。但这将不再是分离式头部的问题,而是关于分离式头部不知道某些远程变化的事实。


    如果你完全确信头部是良好的状态:

    1
    2
    git branch -f master HEAD
    git checkout master

    你可能不能推到原点,因为你的主人已经偏离了原点。如果您确定没有其他人使用回购,您可以强制推动:

    1
    git push -f

    如果您在一个其他人都不使用的功能分支上,则最有用。


    正如克里斯所指出的,我有以下情况

    git symbolic-ref HEADfatal: ref HEAD is not a symbolic ref故障

    然而,git rev-parse refs/heads/master指出了一个很好的承诺,我可以从中恢复(在我的例子中,最后一个承诺,你可以通过使用git show [SHA]看到这个承诺)。

    在那之后我做了很多乱七八糟的事情,但似乎已经解决了,

    git symbolic-ref HEAD refs/heads/master

    头被重新连接!


    您所要做的就是"git checkout[branch name]",其中[branch name]是您进入分离头状态的原始分支的名称。(与asdfasdf分离)将消失。

    例如,在分支"dev"中,您签出提交asdfasd14314->

    1
    'git checkout asdfasd14314'

    你现在处于超然状态

    "git branch"将列出类似->

    1
    2
    3
    4
    * (detached from asdfasdf)
      dev
      prod
      stage

    但要离开分离的头部状态并返回到dev->

    1
    'git checkout dev'

    然后"git branch"将列出->

    1
    2
    3
    * dev
      prod
      stage

    当然,如果您不打算保留脱离头部状态的任何更改,但我发现自己这样做并不是为了进行任何更改,而是为了查看以前的提交


    我今天遇到了这个问题,我更新了一个子模块,但没有任何分支。我已经承诺过了,所以藏匿、结帐、解冻都不起作用。我最后谢丽接受了超然的首长的承诺。因此,在我承诺(当推送失败时)之后,我立即做到了:

    1
    2
    git checkout master
    git cherry-pick 99fe23ab

    我的想法是:我是一个超然的人,但我想成为主人。假设我的分离状态和主没有太大的不同,如果我能将我的承诺应用到主,我就可以被设置。这正是Cherry Pick所做的。


    而不是做git checkout origin/master

    就这么做吧。

    那么,git branch将确认你的分行。


    我在搜索You are in 'detached HEAD' state.时发现了这个问题。

    在分析了我为到达这里所做的一切之后,与我过去所做的相比,我发现我犯了一个错误。

    我的正常流量是:

    1
    2
    3
    4
    git checkout master
    git fetch
    git checkout my-cool-branch
    git pull

    这次我做到了:

    1
    2
    3
    4
    git checkout master
    git fetch
    git checkout origin/my-cool-branch
    # You are in 'detached HEAD' state.

    问题是我不小心做到了:

    1
    git checkout origin/my-cool-branch

    而不是:

    1
    git checkout my-cool-branch

    修复方法(在我的情况下)只是运行上述命令,然后继续流:

    1
    2
    git checkout my-cool-branch
    git pull

    如果你在master上面做了一些承诺,只是想"向后合并"master在那里(也就是说,你想master指向HEAD),那么一行是:

    1
    git checkout -B master HEAD
  • 这就创建了一个新的分支,命名为master,即使它已经存在(就像移动master,这就是我们想要的)。
  • 新创建的分支被设置为指向HEAD,这就是您所在的位置。
  • 新的分支机构已签出,因此您在之后就可以使用master
  • 我发现这在子存储库的情况下特别有用,而子存储库恰好也经常处于分离状态。


    对我来说,这就像再次删除本地分支一样简单,因为我没有任何要推动的本地提交:

    所以我做到了:

    1
    git branch -d branchname

    然后再次检查分支:

    1
    git checkout branchname

    我有同样的问题,我通过以下步骤解决了它。

    如果您需要保留更改

  • 首先,你需要运行git checkout master命令,把你送回主人那里。分支机构。
  • 如果您需要保留更改,只需运行git checkout -b changesgit checkout -B master changes
  • 如果你不需要零钱

  • 要从分支中删除所有未跟踪的文件,请运行git clean -df

  • 然后,您需要清除存储库中所有未分页的更改。为了做到这一点,你必须运行git checkout --

  • 最后,您必须使用git checkout master命令将分支放回主分支。


  • 简单来说,分离的头状态意味着您没有签出到任何分支的头(或尖端)。

    以身作则

    大多数情况下,分支是多个提交的序列,例如:

    承诺1:主管——>分行主管(123BE6A76168ACA712EA16076E971C23835F8CA)

    承诺2:Master——>123BE6A76168ACA712EA16076E971C23835F8CA——>分支头(100644A76168ACA712EA16076E971C23835F8CA)

    正如上面在提交序列的情况下看到的,您的分支指向最新的提交。因此,在这种情况下,如果您签出以提交123BE6A76168ACA712EA16076E971C23835F8CA,那么您将处于独立的头状态,因为您的分支机构负责人指向100644A76168ACA712EA16076E971C23835F8CA,并且从技术上讲,您是在没有分支机构的负责人签出的。因此,您处于分离的头部状态。

    理论解释

    在这个博客里,它清楚地表明Git存储库是一个提交树,每次提交都指向其祖先,每次提交指针都会更新,指向每个分支的这些指针存储在.git/refs子目录中。标记存储在.git/refs/tags中,分支存储在.git/refs/heads中。如果您查看任何一个文件,就会发现每个标记对应于一个单独的文件,具有40个字符的提交哈希,正如上面由@chris johnsen和@yaroslav nikitenko解释的那样,您可以查看这些引用。


    当我发现自己处于这样一种情况时,当我发现自己在不在master的情况下做出了一些改变(即HEADmaster的正上方被分离,并且两者之间没有承诺时,藏匿可能会有所帮助:

    1
    2
    3
    git stash # HEAD has same content as master, but we are still not in master
    git checkout master  # switch to master, okay because no changes and master
    git stash apply  # apply changes we had between HEAD and master in the first place

    我进入了一个非常愚蠢的状态,我怀疑其他人会发现这个有用的……但以防万一

    1
    2
    3
    4
    git ls-remote origin
    0d2ab882d0dd5a6db93d7ed77a5a0d7b258a5e1b        HEAD
    6f96ad0f97ee832ee16007d865aac9af847c1ef6        refs/heads/HEAD
    0d2ab882d0dd5a6db93d7ed77a5a0d7b258a5e1b        refs/heads/master

    我最终解决了这个问题

    1
    git push origin :HEAD

    如果在Eclipse中使用eGit:假设你的主人是你的主要发展部门

    • 将更改提交到分支,通常是新的分支
    • 然后从遥控器中拔出
    • 然后右键单击项目节点,选择团队,然后选择显示历史记录。
    • 然后右键单击主控形状,选择"签出"
    • 如果Eclipse告诉您,有两个主控形状,一个本地,一个远程,请选择远程

    在这之后,您应该能够重新连接到源站主服务器。


    在我的例子中,我运行了git status,我看到我的工作目录中有一些未跟踪的文件。

    为了使钢筋工作,我只需要清理它们(因为我不需要它们)。


    这对我很有效:

    1.git stash以保存您的本地修改

    如果要放弃更改git clean -dfgit checkout -- .git clean删除所有未跟踪的文件(警告:虽然它不会删除直接在.gitignore中提到的被忽略文件,但它可能删除文件夹中的被忽略文件),git checkout清除所有未跟踪的更改。

    2.git checkout master切换到主分支(假设您想使用master)3.git pull从总分行提取最后一笔承诺4.git status为了检查一切看起来都很好

    1
    2
    On branch master
    Your branch is up-to-date with 'origin/master'.


    我也有同样的问题。我把零钱存起来git stash和硬重置本地的分支到以前的一个承诺(我认为是它导致的),然后做了一个git pull,我现在没有让那个头脱离。别忘了,git stash apply会让你再次改变。


    1
    2
    3
    git checkout checksum  # You could use this to peek previous checkpoints
    git status # You will see HEAD detached at checksum
    git checkout master # This moves HEAD to master branch