如何将分离的HEAD与主分支/远程分支合并?

1740

我对Git分支的复杂性还不太熟悉。通常我只在一个单一的分支上进行工作,然后提交更改,定期将其推送到远程仓库。

最近我重置了一些文件以便将它们从提交暂存区中移出,然后又执行了rebase -i命令以删除几个本地的最近提交。现在我处于一种我不太理解的状态。

在我的工作区,git log显示的正是我所期望的——我有我想要的提交,新的提交也在那里等等。

但是我刚刚推送到远程仓库的内容却不同,其中一些我已经删除的提交出现了,并且本地新提交的内容也没有推送上去。

我认为"master/origin"与HEAD脱离了关系,但我不完全清楚这意味着什么,如何使用命令行工具进行可视化操作以及如何解决该问题。


你在变基之前推送了提交吗? - manojlds
@manojlds:不确定您的意思。我在变基之前有一段时间进行了一些推送,但并非立即推送。 - Ben Zotto
你之前是否推送了在rebase -i中删除的提交?从你的回答来看,我认为没有。 - manojlds
@manojlds:没错。我只删除了比最近一次推送更新的提交。(尽管我已经重新推送了,因为我认为一切都没问题) - Ben Zotto
你能解释一下 我重置了一些文件以将它们移出提交暂存区 这部分的操作吗?抱歉问这么多问题 :) - manojlds
@manojlds:很不幸,我想不起来了,这就是为什么我说得含糊的原因 :) 我记得做了一些关于取消暂存提交的事情,那是我注意到“分离的 HEAD”注释的第一次。感谢您下面的回答。我将尝试从那里学习。 - Ben Zotto
28个回答

2771

首先,让我们澄清HEAD是什么以及当它被分离时意味着什么。

HEAD是当前检出提交的符号名称。当HEAD未被分离(“正常”的情况:您已经检出一个分支),HEAD实际上指向分支的“ref”,而该分支指向提交。因此,HEAD“附加”到分支上。当您创建新的提交时,HEAD所指向的分支将更新为指向新的提交。由于HEAD只是指向分支,因此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处于分离状态时,它直接指向一个提交,而不是通过分支间接地指向一个提交。你可以将分离的HEAD视为位于未命名分支上。
  • git symbolic-ref HEAD会提示错误:fatal: ref HEAD is not a symbolic ref
  • git rev-parse HEAD得到结果17a02998078923f2d62811326d130de991d1a95a
    由于它不是符号引用,因此必须直接指向提交本身。
我们有HEAD17a02998078923f2d62811326d130de991d1a95a 分离的HEAD需要记住的重要事情是:如果它所指向的提交在其他引用无法到达的情况下,则在你检出其他提交时它将变成“悬空”的。最终,这样的悬空提交将通过垃圾回收过程被剪枝(默认情况下,它们被保留至少两周,并可能通过HEAD的reflog被保留更长时间)。 1使用分离的HEAD进行“正常”工作完全没有问题,只需要跟踪你正在做什么,以避免从reflog中挖掘丢失的历史记录。

交互式变基的中间步骤使用分离的HEAD进行(部分是为了避免污染活动分支的reflog)。如果完成整个变基操作,它将使用变基操作的累积结果更新您的原始分支,并重新附加HEAD到原始分支。我猜你从未完全完成变基过程;这会让你拥有一个指向最近被变基操作处理的提交的分离HEAD。

要从您的情况中恢复,您应该创建一个分支,该分支指向当前由您的分离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才能推送。

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


6
man git-symbolic-ref中很有趣的一句话是:"在过去,.git/HEAD是一个指向refs/heads/master的符号链接。当我们想要切换到另一个分支时,我们会执行ln -sf refs/heads/newbranch .git/HEAD,当我们想要知道我们在哪个分支上时,我们会执行readlink .git/HEAD。但是符号链接并不完全可移植,因此现已弃用,并且默认使用符号引用(如上所述)。" - Dmitry Minkovsky
4
这个答案是帮助我将本地代码与远程主分支同步的最后一步,此前我在Eclipse中不小心执行了git reset --hard <sha>。第一步是运行git reflog命令,并恢复本地提交记录(请参见https://dev59.com/HXVD5IYBdhLWcg3wWaVh)。谢谢。 - jlpp
13
我同意 @AntonioSesto 的观点:对于大多数项目(即使是相当大的项目),你并不需要 Git 这种令人难以置信的复杂性。我的大脑拒绝与明显过度设计的东西进行斗争。我不需要它,也不想要它。 - Jasper Sprengers
46
这是一个不错的答案,但我认为没有必要使用临时分支(尽管我自己通常会使用)。git branch -f master HEAD && git checkout master足以实现你的目标——假设你的目标是保留当前的HEAD,但将其指定为“master”。其他目标也是有意义的,并需要其他方法。 - Adrian Ratnapala
50
嘲笑评论中关于长度的部分。然而我们其余的人只需浏览直到找到“要从您的情况中恢复过来[...]”这一行,然后从那里开始阅读 - 同时心里记下有一个有用的、详细解释的背景故事可以在雨天看。阅读更多的选项不会伤害你,但却有益于其他人。 - underscore_d
显示剩余7条评论

672

只需要这样做:

git checkout master

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

git checkout -b temp
git checkout -B master temp

86
这是一个危险的回答。达成这个答案的人有不同的情况,"只需做这个来修复它"的回应并不能解答问题。这个回答可以轻易毁掉工作。 - Archonic
23
如果分离头部不属于主分支,运行"git checkout master"将导致所有更改被丢失。 - Tony
3
你可能已经检出了提交记录而不是分支。该分支仍然指向相同的提交记录,但你处于不同的“模式”中。 - Daniel Alexiuc
1
“git reset” 应该带有一个警告:“如果你不知道自己在做什么,请停止操作”。我刚从一小时的恐慌中恢复过来,以为我失去了上周的所有工作。谢谢! - Opus1217
1
同意 @Archonic。 在盲目运行任何命令之前,了解git的工作原理非常重要。您可以节省不阅读大量答案的时间,但如果您的工作丢失了,可能会浪费更多的时间。 - Yusufali2205
显示剩余7条评论

151

我遇到了这个问题,当我读到最受欢迎的答案时:

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

我想:啊哈!如果HEAD是当前检出提交的符号名称,我可以通过将其在master上进行变基来与master协调:

git rebase HEAD master

这个命令:

  1. 检出master
  2. HEADmaster分叉点标识了父提交记录
  3. master上播放这些提交

最终结果是所有在HEAD中但不在master中的提交都会在master中。 master 仍然被检出。


关于远程:

一些我在rebase中删除的提交被推送了,而本地新提交的内容不存在。

使用本地历史记录无法快速转发远程历史记录。 您需要强制推送(git push -f)以覆盖远程历史记录。 如果有任何合作者,通常最好与他们协调,以便每个人都在同一页上。

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


5
Git:"首先,将HEAD回滚以重放您的工作..." 快进主分支到最新提交(HEAD)。我:"太棒了!" - Benjamin
这个建议创建了各种平行宇宙 FML。 - Sentry.co
哎呀,听到这个消息很抱歉。考虑使用 git reflog 找到你想要重置分支的提交,然后使用 git reset --hard $commit 将你的分支重置到该提交。 - Dmitry Minkovsky
这非常好,完全符合我的工作流程需求,但实际上与我认为在第3点下所描述的有些不同:它在分离的HEAD顶部播放分歧点和master之间的提交。换句话说,在分离的HEAD上天真地完成的提交正是我想要它们在历史记录中的位置,而不是在master的顶部。我通常会使用交互式变基来完成这个操作。顺便提一下:git rebase master HEAD则相反,将在分离的HEAD上完成的提交放在master的顶部,就像在这个答案中所描述的那样。 - mvds

89

这里是关于 "detached head" 的基本解释:

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

通过命令行可视化它:

git branch
或者
git branch -a

您将获得如下输出:

* (no branch)
master
branch1

* (no branch) 表示您处于分离头状态。

可能是通过执行git checkout somecommit等操作导致的,并且会显示以下警告:

您处于“detached HEAD”状态。您可以查看、进行实验性更改并提交它们,也可以通过再次执行checkout命令来放弃在此状态下进行的任何提交而不影响任何分支。

如果您想创建一个新分支以保留创建的提交,则可以使用 -b 参数与 checkout 命令(现在或以后)来完成。例如:

git checkout -b new_branch_name

现在,要将它们合并到主分支上:

执行git reflog或者只执行git log查看您的提交记录。现在切换到主分支git checkout master,然后使用git merge合并这些提交。

git merge HEAD@{1}

编辑:

补充一下,使用git rebase -i不仅可以删除/撤销您不需要的提交,还可以对它们进行编辑。只需在提交列表中提到“edit”,然后您就可以修改提交并发出git rebase --continue以继续。这将确保您永远不会进入分离的HEAD状态。


感谢您在这里提供详细和有用的信息。看起来似乎不需要进行显式合并,但这些概念的可视化让我受益匪浅。谢谢。 - Ben Zotto

44

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

只需运行git checkout -b mynewbranch

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


如果我这样做,mynewbranch 会附加到任何东西上吗? - Benjohn
1
是的,它附加到已分离的HEAD应该附加的位置,这正是我想要的。谢谢! - Benjohn

40

在搜索过程中,我发现了这个问题: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

1
完美的解决方案。 - Thirumal Sakthivel
@user664833 感谢您提供这个完美的答案。事实上,我也遇到了类似的情况。 - Imran Rafiq Rather
我的情况下也起作用了。 - ddlr
看起来我犯了和你一样的错误。感谢你在这里指出并分享你的经验! :) - Sasho Andrijeski

24

如果你只有主分支并想返回到“develop”或一个功能分支,请执行以下操作:

git checkout origin/develop

注意:检出 origin/develop

您正处于分离头指针状态。您可以浏览、进行实验性更改和提交,也可以在此状态下放弃任何提交,而不影响通过执行另一个checkout来进行的任何分支...

然后

git checkout -b develop

它起作用了 :)


9
对我来说管用的不是'git checkout origin/develop'而是'git checkout develop'。使用'origin/develop'总是导致没有任何更改,从而停留在“HEAD detached at origin/develop”。跳过'origin'这一部分可以修复所有问题。 - DrStrangepork

24

如果您想推送当前分离的 HEAD(在执行之前,请检查 git log),请尝试:

如果您想推送当前分离的 HEAD(在执行之前,请检查 git log),请尝试:

git push origin HEAD:master

将你的分离 HEAD 推送到远程的 master 分支。如果推送被拒绝,请先尝试 git pull origin master 获取来自远程的更改。如果你不在意来自远程的更改,但因为你进行了一些有意的变基操作并想要用当前的分离分支替换 origin/master 而被拒绝,那么你可以强制推送 (-f)。如果你丢失了之前提交的某些访问信息,可以随时运行 git reflog 查看所有分支的历史记录。


保留更改的同时回到 master 分支,可以尝试以下命令:

git rebase HEAD master
git checkout master

参见:Git:“Not currently on any branch。”有没有一种简单的方法可以回到一个分支,同时保留更改?


2
这确实将分离的提交发送到 origin/master。要将头附加到本地分支,请执行以下操作:https://dev59.com/_2Mm5IYBdhLWcg3wX-DT#17667057 - Paschalis
当我执行此操作时,会出现以下内容:该存储库已配置为Git LFS,但在您的路径中找不到“git-lfs”。如果您不再希望使用Git LFS,请通过删除“.git/hooks/post-checkout”来移除此挂钩。 - user2568374

14

以下方法对我有效(仅使用主分支):

git push origin HEAD:master
git checkout master        
git pull

第一个命令将分离的 HEAD 推送到远程 origin。

第二个命令切换到主分支 master。

第三个命令会将 HEAD 恢复并附加到主分支 master 上。

如果第一个命令被拒绝,可能会出现问题。但这不再是分离的 HEAD 的问题,而是由于分离的 HEAD 不知道某些远程更改而导致的问题。


无法工作,我得到了以下错误信息:此存储库已配置为使用Git LFS,但在您的路径中未找到“git-lfs”。如果您不再希望使用Git LFS,请通过删除“.git/hooks/pre-push”来删除此挂钩。同时,您当前没有处于任何分支上,请指定您要合并的分支。 - user2568374

13

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

git branch temp
git checkout master
git merge temp

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


@StarShine 已经修复了它。现在它会将您的分离提交保存到一个新分支temp中,切换到master,并将temp合并到master中。 - Cees Timmerman
我不知道为什么有人会对此进行负面评价,它解决了我的问题,但你可能需要包括删除临时分支命令。 - GlassGhost

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接