如何使分离的HEAD与主站/源站协调?


1558

我对Git的分支复杂性并不陌生。我总是在单个分支上工作并提交更改,然后定期推送到我的远程源。

最近在某个地方,我对一些文件进行了重置,以使它们脱离提交暂存阶段,后来又做了一些操作,rebase -i以摆脱一些最近的本地提交。现在我处于一种不太了解的状态。

在我的工作区域中,git log确切地显示了我的期望-我在正确的火车上带着我不想离开的提交,那里有新的提交,等等。

但是我只是推送到远程存储库,而又有什么不同–我在rebase中杀死的几个提交都被推送了,而本地提交的新提交则不存在。

我认为“ master / origin”是与HEAD分离的,但是我不清楚这意味着什么,如何使用命令行工具对其进行可视化以及如何对其进行修复。


您是否在重新设置基准之前就推送了提交?
manojlds

@manojlds:不确定您的意思。我在变基之前花了一些时间,但没有紧迫。
Ben Zotto

就像以前一样,您之前在重新定位中推送了删除的提交-i ..从您的答案中我认为没有。
manojlds

@manojlds:正确。我只杀死了比最近推送更新的提交。(尽管正如我提到的那样,但自从我认为一切都很好之后,我就开始努力)
Ben Zotto

您能解释一下您所做的I did a reset of some files to get them out of commit staging部分吗?对于这个问题表示抱歉:)
manojlds 2011年

Answers:


2521

首先,让我们弄清HEAD是什么,分离后的含义。

HEAD是当前已检出提交的符号名称。如果未分离HEAD(“正常” 1情况:您已签出分支),则HEAD实际上指向分支的“ ref”,而分支则指向提交。HEAD因此“附加”到分支。当您进行新提交时,HEAD指向的分支将更新为指向新提交。HEAD自动跟随,因为它仅指向分支。

  • git symbolic-ref HEAD产生refs/heads/master
    签出名为“ master”的分支。
  • git rev-parse refs/heads/masteryield 17a02998078923f2d62811326d130de991d1a95a
    提交是master分支的当前提示或“头部”。
  • git rev-parse HEAD也会产生17a02998078923f2d62811326d130de991d1a95a
    这就是“符号引用”的含义。它通过其他一些引用指向一个对象。
    (符号引用最初是作为符号链接实现的,但后来更改为带有额外解释的纯文件,以便可以在没有符号链接的平台上使用。)

我们有HEAD→交通refs/heads/master→交通17a02998078923f2d62811326d130de991d1a95a

分离HEAD时,它直接指向一个提交,而不是通过分支间接指向一个提交。您可以将分离的HEAD视为未命名的分支。

  • git symbolic-ref HEAD 失败于 fatal: ref HEAD is not a symbolic ref
  • git rev-parse HEADyields 17a02998078923f2d62811326d130de991d1a95a
    因为它不是符号引用,所以必须直接指向提交本身。

我们HEAD17a02998078923f2d62811326d130de991d1a95a

对于分离的HEAD要记住的重要一点是,如果指向它的提交未被引用(其他引用无法到达它),那么当您签出其他提交时,它将变成“悬挂”。最终,此类悬空的提交将在垃圾回收过程中被修剪(默认情况下,它们至少保留2周,并且可能被HEAD的reflog引用保留更长时间)。

1 使用分离的HEAD进行“正常”工作是完全可以的,您只需跟踪自己在做什么,以避免不得不从reflog中删除掉历史记录。


交互式基础的中间步骤是通过分离的HEAD完成的(部分是为了避免污染活动分支的引用日志)。如果您完成了完整的rebase操作,它将使用rebase操作的累积结果更新您的原始分支,并将HEAD重新附加到原始分支。我的猜测是您永远不会完全完成变基过程。这将使您留下一个分离的HEAD,指向由rebase操作最近处理的提交。

为了从您的情况中恢复过来,您应该创建一个分支,该分支指向分离的HEAD当前指向的提交:

git branch temp
git checkout temp

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

这会将您的HEAD重新连接到新temp分支。

接下来,您应该将当前提交(及其历史记录)与预期在其上进行操作的普通分支进行比较:

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以指向它:

git branch -f master temp
git checkout master

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

然后,您可以删除临时分支:

git branch -d temp

最后,您可能需要推送重新建立的历史记录:

git push origin master

--force如果无法将远程分支“快速转发”到新的提交(例如,您删除或重写了一些现有的提交,或以其他方式重写了一些历史记录),则可能需要在此命令的末尾添加以推送。

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


6
有趣的是man git-symbolic-ref:“过去.git/HEAD曾经有一个符号链接指向refs/heads/master。当我们想要切换到另一个分支时,我们做了ln -sf refs/heads/newbranch .git/HEAD,而当我们想要找到我们所在的分支时,我们做了readlink .git/HEAD。但是符号链接并不是完全可移植的,因此现在已弃用,默认情况下使用符号引用(如上所述)。”
德米特里·明科夫斯基

10
我同意@AntonioSesto:对于大多数项目(甚至是相当大的项目),您都不需要令人难以置信的Git复杂性。我的大脑反抗于解决如此明显过度设计的问题。我不需要,也不需要。
Jasper Sprengers

36
这是一个很好的答案,但是我认为没有必要使用temp分支(尽管我通常自己使用一个)。 git branch -f master HEAD && git checkout master足够-假设您的目标是保持当前的领导地位,但将其指定为master。其他目标也很有意义,并要求其他食谱。
Adrian Ratnapala 2015年

38
哈哈在评论有关长度的评论。而我们其余的人只是简单地浏览直到我们到达“从您的情况中恢复[...]”这一行,然后从那里走–同时请注意,这里有一个有用的,经过充分解释的背景资料,我们可以阅读在下雨天。阅读更多内容的选择不会伤害您,但确实可以使他人受益。
underscore_d

5
这就是为什么我讨厌git。
Monica Heddneck

626

只是这样做:

git checkout master

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

git checkout -b temp
git checkout -B master temp

57
这是一个危险的反应。达到此答案的人有不同的状态,“只是这样做以解决问题”的回答不会回答问题。这很容易破坏工作。
Archonic

15
如果分离的头部不是master的一部分,“ git checkout master”将导致所有更改丢失!
托尼

3
@Blauhirn您可能已经检出了提交,而不是分支。分支仍然指向同一提交,但是您处于不同的“模式”。
Daniel Alexiuc

1
git reset应该带有警告“如果您不知道自己在做什么,那就停止它”。刚从一个小时的恐怖中恢复过来,以为我失去了上周的工作。谢谢!
Opus1217

1
同意@Archonic在盲目的运行任何命令之前,了解git的工作原理很重要。您可以不阅读重要答案而节省时间,但是如果您的工作丢失了,则可以浪费更多时间。
Yusufali2205 '19

132

我遇到了这个问题,当我在投票结果中投票时:

HEAD是当前已检出提交的符号名称。

我以为:啊哈!如果HEAD是当前结帐提交的象征性名称,我可以根据master以下内容对它进行调和master

git rebase HEAD master

该命令:

  1. 签出 master
  2. 标识HEAD回溯到HEAD与之背离的点的父提交master
  3. 发挥这些承诺 master

最终的结果是,这在所有的提交HEAD而不是master随后也mastermaster仍然签出。


关于遥控器:

我在rebase中杀死的一些提交被推送了,而本地提交的新提交不存在。

远程历史记录无法再使用您的本地历史记录进行快速转发。您需要强制按下(git push -f)来覆盖远程历史记录。如果您有任何合作者,通常需要与他们进行协调,以便所有人都在同一页面上。

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


3
git:“首先,倒退磁头以在其上重放您的工作...将主播快速转发到HEAD。” 我:“不错!”
本杰明

81

在这里查看分离头的基本说明:

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

命令行将其可视化:

git branch

要么

git branch -a

您将得到如下输出:

* (no branch)
master
branch1

* (no branch)你在分离的头节目。

您可能通过执行git checkout somecommit等操作来达到此状态,并且它会通过以下警告您:

您处于“分离头”状态。您可以环顾四周,进行实验性更改并将其提交,并且可以放弃在此状态下所做的任何提交,而不会通过执行另一次签帐而影响任何分支。

如果要创建新分支以保留创建的提交,则可以(现在或以后)通过再次将-b与checkout命令一起使用来进行。例:

git checkout -b new_branch_name

现在,让他们成为主人:

做一个git reflog甚至只是git log记下您的提交。现在git checkout mastergit merge提交。

git merge HEAD@{1}

编辑:

要添加,git rebase -i不仅用于删除/杀死不需要的提交,还用于编辑它们。只要在提交列表中提到“编辑”,您就可以修改提交,然后发出a git rebase --continue继续。这样可以确保您永远不会进入一个独立的HEAD。


感谢您提供的详细信息和有用的信息指针。似乎没有必要进行显式合并,但这使我将要介绍的一些概念形象化。谢谢。
Ben Zotto

6
“ @ {1}”有什么作用?
ebi 2015年

35

将您的分离提交提交到自己的分支

简单地运行git checkout -b mynewbranch

然后运行git log,您将看到提交现在HEAD在这个新分支上。


如果执行此操作,是否会mynewbranch附加任何内容?
约翰2015年

1
是的,它连接到分离头的安装位置,这正是我想要的。谢谢!
约翰·

22

如果您只有master分支并且想返回到“ develop”或某个功能,请执行以下操作:

git checkout origin/develop

注意:签出origin / develop

您处于分离的HEAD状态。您可以环顾四周,进行实验性更改并将其提交,并且可以通过执行另一次签出操作放弃在此状态下所做的任何提交而不会影响任何分支...

然后

git checkout -b develop

有用 :)


7
对我有用的不是“ git checkout源/开发”,而是“ git checkout开发”。使用“原始/显影”始终不会导致任何更改,因此保留在“从原始/显影分离的头中”。跳过“原始”部分将修复所有问题。
DrStrangepork 2014年

18

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

git push origin HEAD:master

将您的分离的HEAD从原点发送到master分支。如果您的请求被拒绝,请git pull origin master首先尝试从原点获取更改。如果您不关心原点的更改而被拒绝,因为您进行了一些有意的变基,并且想用当前分离的分支替换原点/主文件,则可以强制执行(-f)。如果您失去了对先前提交的访问权限,则可以始终运行git reflog以查看所有分支的历史记录。


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

git rebase HEAD master
git checkout master

请参阅:Git:“当前不在任何分支上”。有没有一种简单的方法可以在保留更改的情况下返回分支?


2
实际上,这会将分离的提交发送到源/主服务器。要将头连接到本地分支,请执行以下操作:stackoverflow.com/a/17667057/776345
Paschalis

当我这样做时,我得到此存储库已为Git LFS配置,但在您的路径中未找到“ git-lfs”。如果您不再希望使用Git LFS,请通过删除.git / hooks / post-checkout删除此挂钩。
user2568374

16

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

与过去相比,在分析了我到这里所做的事情之后,我发现自己犯了一个错误。

我的正常流程是:

git checkout master
git fetch
git checkout my-cool-branch
git pull

这次我做了:

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

问题是我不小心做了:

git checkout origin/my-cool-branch

而不是:

git checkout my-cool-branch

解决方法(在我的情况下)只是运行上面的命令,然后继续执行流程:

git checkout my-cool-branch
git pull

11

以下对我有用(仅使用分支主机):

git push origin HEAD:master
git checkout master        
git pull

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

第二个移动到分支主机。

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

如果拒绝推送,则在第一个命令上可能会出现问题。但是,这将不再是分离的Head的问题,而是关于分离的HEAD不了解某些远程更改的事实。


不起作用,我得到了:该存储库已为Git LFS配置,但在您的路径中未找到“ git-lfs”。如果您不再希望使用Git LFS,请通过删除.git / hooks / pre-push删除此挂钩。AND您当前不在分支机构。请指定您要合并的分支。
user2568374

11

我今天刚遇到这个问题,很确定我可以通过以下方法解决它:

git branch temp
git checkout master
git merge temp

当我弄清楚如何做到这一点时,我正在工作计算机上,现在我的个人计算机上也遇到了同样的问题。因此,当我回到工作计算机时,必须等到星期一,才能确切地了解我的工作方式。


@StarShine Kenorb修复了它。现在,它将分离的提交保存到新分支temp中,切换到master并将temp合并到master中。
Cees Timmerman

我不知道为什么ppl对此表示反对,它解决了我的问题统计信息,但是您可能要包括delete temp branch命令。
GlassGhost

8

如果您完全确定HEAD是好的状态:

git branch -f master HEAD
git checkout master

您可能无法将其推向原点,因为您的主人已偏离原点。如果您确定没有其他人正在使用该存储库,则可以强制执行以下操作:

git push -f

如果您在某个功能分支上,没有其他人正在使用,则最有用。


6

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

因此,例如,在分支“ dev”中,您检出提交asdfasd14314->

'git checkout asdfasd14314'

您现在处于独立状态

'git branch'将列出类似->

* (detached from asdfasdf)
  dev
  prod
  stage

但要摆脱超脱的头脑状态,然后回到dev->

'git checkout dev'

然后'git branch'将列出->

* dev
  prod
  stage

但这当然是如果您不打算对分离的头部状态进行任何更改,但是我发现自己做了很多这件事并不是要进行任何更改,而只是查看先前的提交


6

克里斯指出,我有以下情况

git symbolic-ref HEAD 失败于 fatal: ref HEAD is not a symbolic ref

但是,这git rev-parse refs/heads/master是指可以从中恢复的良好提交(对于我来说,最后一次提交,您可以通过使用来查看该提交git show [SHA]

之后,我做了很多杂乱的事情,但是似乎已经解决了,

git symbolic-ref HEAD refs/heads/master

和头重新连接!


1
谢谢!我的头已经脱落。我可以赶上master,但是他们恰好指向同一提交,而不是指向master的提交。好提示= D
RagingRoosevelt 18-3-22

4

而不是做 git checkout origin/master

做就是了 git checkout master

然后git branch将确认您的分支。


4

我今天遇到了这个问题,我在那里更新了一个子模块,但是不在任何分支上。我已经承诺过,因此无法进行隐藏,结帐,取消隐藏。我最终选择了超然的头脑。因此,在我提交之后(当推送失败时),我立即:

git checkout master
git cherry-pick 99fe23ab

我的想法去了:我的头很孤单,但是我想成为主人。假设我的分离状态与master并没有很大的不同,如果我可以将提交应用于master,那么我将被设置。这正是樱桃采摘的目的。


3

如果您在master上进行了一些提交而只想在其中进行“向后合并” master(即,您要master指向HEAD),那么单线将是:

git checkout -B master HEAD
  1. master即使它已经存在,这也会创建一个名为的新分支(就像移动一样master,这就是我们想要的)。
  2. 新创建的分支设置为指向HEAD您所在的位置。
  3. 新分支已签出,因此您将继续master

我发现这对于子存储库尤其有用,子存储库也经常处于分离状态。


3

我遇到了同样的问题,并通过执行以下步骤解决了它。

如果您需要保留更改

  1. 首先,您需要运行git checkout master命令以使您回到master分支。
  2. 如果你需要保持你的改变只是运行git checkout -b changesgit checkout -B master changes

如果您不需要更改

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

  2. 然后,您需要清除存储库中所有未分级的更改。为此,您必须运行git checkout --

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


3

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

所以我做了:

git branch -d branchname

然后再次检查分支:

git checkout branchname

1

当我个人发现自己处于不在状态时进行了一些更改master(即HEAD,在上方分离master并且之间没有提交)时,隐藏可能会有所帮助:

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

简而言之,分离的HEAD状态意味着您没有签出到任何分支的HEAD(或小费)

举例说明

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

提交1: 大师-> branch_HEAD(123be6a76168aca712aea16076e971c23835f8ca)

提交2: 大师-> 123be6a76168aca712aea16076e971c23835f8ca-> branch_HEAD(100644a76168aca712aea16076e971c23835f8ca)

正如您在提交顺序中看到的那样,您的分支指向您的最新提交。因此,在这种情况下,如果您签出以提交123be6a76168aca712aea16076e971c23835f8ca,那么您将处于分离头状态,因为分支的HEAD指向100644a76168aca712aea16076e971c23835f8ca,从技术上讲,您在没有分支的HEAD处签出。因此,您处于分离的HEAD状态。

理论解释

在此Blog中,它清楚地表明Git存储库是一个提交树,每个提交都指向其祖先,并且每个提交指针都被更新,并且指向每个分支的指针都存储在.git / refs子目录中。标签存储在.git / refs / tags中,分支存储在.git / refs / heads中。如果查看其中的任何文件,您会发现每个标签对应一个文件,并带有40个字符的提交哈希,如@Chris Johnsen和@Yaroslav Nikitenko所述,您可以查看这些引用。


0

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

git ls-remote origin
0d2ab882d0dd5a6db93d7ed77a5a0d7b258a5e1b        HEAD
6f96ad0f97ee832ee16007d865aac9af847c1ef6        refs/heads/HEAD
0d2ab882d0dd5a6db93d7ed77a5a0d7b258a5e1b        refs/heads/master

我最终解决了

git push origin :HEAD

0

这非常适合我:

1. git stash保存本地修改

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

2. git checkout master切换到主分支(假设您想使用master)
3. git pull从master分支拉出最后一次提交
4. git status为了检查一切看起来不错

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

0

就我而言,我跑步了git status,发现工作目录中有一些未跟踪的文件。

为了使基准工作,我只需要清洁它们(因为我不需要它们)。


0

如果您在Eclipse 中使用EGit:假设您的主人是您的主要开发分支

  • 提交更改到一个分支,通常是一个新分支
  • 然后从遥控器拉
  • 然后右键单击项目节点,选择团队,然后选择显示历史记录
  • 然后右键单击母版,选择“签出”
  • 如果Eclipse告诉您,有两个大师一个本地一个远程,请选择远程

之后,您应该可以重新连接到原点主机。


-1

我有同样的问题。我将更改存储起来, git stash然后将本地分支硬重置为先前的提交(我认为这是造成该问题的原因),然后执行了a git pull,但现在还没有断开该头。不要忘记git stash apply再次进行更改。


-2
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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.