我是 Git 分支复杂性的新手。我总是在一个分支上工作并提交更改,然后定期推送到我的远程源。
最近的某个地方,我对一些文件进行了重置以使它们脱离提交暂存状态,后来又进行了 rebase -i
以摆脱最近的几个本地提交。现在我处于一种我不太了解的状态。
在我的工作区域中,git log
准确地显示了我的预期——我在正确的火车上,有我不想消失的提交,还有新的提交等等。
但我只是推送到远程存储库,但有什么不同——我在变基中杀死的几个提交被推送了,而本地提交的新提交不存在。
我认为“master/origin”与 HEAD 分离,但我不是 100% 清楚这意味着什么,如何使用命令行工具将其可视化,以及如何修复它。
I did a reset of some files to get them out of commit staging
部分做了什么吗?抱歉这些问题:)
首先,让我们澄清一下 what HEAD is 以及它在分离时的含义。
HEAD 是当前签出提交的符号名称。当 HEAD 未分离时(“正常”1 情况:您已签出一个分支),HEAD 实际上指向一个分支的“ref”,而该分支指向提交。 HEAD 因此被“附加”到一个分支上。当您进行新的提交时,HEAD 指向的分支会更新为指向新的提交。 HEAD 自动跟随,因为它只是指向分支。
git symbolic-ref HEAD 产生 refs/heads/master 名为“master”的分支被签出。
git rev-parse refs/heads/master yield 17a02998078923f2d62811326d130de991d1a95a 该提交是主分支的当前提示或“头”。
git rev-parse HEAD 也产生 17a02998078923f2d62811326d130de991d1a95a 这就是“符号引用”的含义。它通过其他一些引用指向一个对象。 (符号引用最初是作为符号链接实现的,但后来更改为带有额外解释的普通文件,以便它们可以在没有符号链接的平台上使用。)
我们有 HEAD
→ refs/heads/master
→ 17a02998078923f2d62811326d130de991d1a95a
当 HEAD 分离时,它直接指向一个提交,而不是通过分支间接指向一个提交。您可以将分离的 HEAD 视为位于未命名的分支上。
git symbolic-ref HEAD 失败并出现致命错误:ref HEAD 不是符号引用
git rev-parse HEAD 产生 17a02998078923f2d62811326d130de991d1a95a 因为它不是符号引用,所以它必须直接指向提交本身。
我们有 HEAD
→ 17a02998078923f2d62811326d130de991d1a95a
使用分离的 HEAD 需要记住的重要一点是,如果它指向的提交是未引用的(没有其他 ref 可以到达它),那么当您签出其他一些提交时,它将变得“悬空”。最终,此类悬空提交将通过垃圾收集过程进行修剪(默认情况下,它们至少保留 2 周,并且可能会通过 HEAD 的 reflog 引用而保留更长时间)。
1 使用分离的 HEAD 进行“正常”工作是完全可以的,您只需要跟踪您正在做的事情,以避免不得不从 reflog 中删除丢弃的历史记录。
交互式 rebase 的中间步骤是使用分离的 HEAD 完成的(部分是为了避免污染活动分支的 reflog)。如果您完成了完整的 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
以推送)。
如果您正在进行 rebase 操作,您可能应该清理它。您可以通过查找目录 .git/rebase-merge/
来检查是否正在进行变基。您可以通过删除该目录来手动清理正在进行的变基(例如,如果您不再记得活动变基操作的目的和上下文)。通常您会使用 git rebase --abort
,但这会进行一些您可能希望避免的额外重置(它将 HEAD 移回原始分支并将其重置回原始提交,这将撤消我们在上面所做的一些工作)。
只需这样做:
git checkout master
或者,如果您有想要保留的更改,请执行以下操作:
git checkout -b temp
git checkout -B master temp
git reset
应带有警告“如果您不知道自己在做什么,请停止它”。刚刚从一个小时的恐惧中恢复过来,以为我失去了最后一周的工作。谢谢!
我遇到了这个问题,当我读到票数最高的答案时:
HEAD 是当前签出提交的符号名称。
我想:啊哈!如果 HEAD
是当前签出提交的符号名称,我可以通过将它与 master
重新设置为基础来将其与 master
协调一致:
git rebase HEAD master
这个命令:
签出 master 识别 HEAD 的父提交回到与 master 不同的点 HEAD 在 master 之上播放这些提交
最终结果是所有在 HEAD
但不在 master
中的提交也都在 master
中。 master
仍处于签出状态。
关于遥控器:
我在变基中杀死的几个提交被推送了,而本地提交的新提交不存在。
远程历史无法再使用您的本地历史进行快进。您需要强制推送 (git push -f
) 以覆盖远程历史记录。如果您有任何合作者,通常与他们进行协调是有意义的,这样每个人都在同一页面上。
将 master
推送到远程 origin
后,您的远程跟踪分支 origin/master
将更新为指向与 master
相同的提交。
git reflog
找到您想要将分支重置为的提交,然后使用 git rest —hard $commit
将您的分支重置为该提交
HEAD
之上播放分歧点和 master
之间的提交.换句话说,在分离的 HEAD
上天真地完成的提交正是我想要的,即历史上的某个地方,而不是在 master
之上。我通常会为此使用交互式变基。旁注:git rebase master HEAD
执行相反的操作,并在 master
之上的分离的 HEAD
上播放提交,如本答案中所述。
在这里查看分离头的基本解释:
http://git-scm.com/docs/git-checkout
可视化它的命令行:
git branch
或者
git branch -a
你会得到如下输出:
* (no branch)
master
branch1
* (no branch)
表明您处于分离状态。
您可以通过执行 git checkout somecommit
等来达到这种状态,它会警告您以下内容:
您处于“分离 HEAD”状态。您可以环顾四周,进行实验性更改并提交它们,并且您可以放弃在此状态下所做的任何提交,而不会通过执行另一个签出来影响任何分支。如果您想创建一个新分支来保留您创建的提交,您可以(现在或以后)再次使用 -b 和 checkout 命令来执行此操作。示例: git checkout -b new_branch_name
现在,让他们进入主人:
执行 git reflog
甚至只是 git log
并记下您的提交。现在 git checkout master
和 git merge
提交。
git merge HEAD@{1}
编辑:
要添加,使用 git rebase -i
不仅可以删除/终止您不需要的提交,还可以用于编辑它们。只需在提交列表中提及“编辑”,您就可以修改您的提交,然后发出 git rebase --continue
继续。这将确保您永远不会进入分离的 HEAD。
将分离的提交放到它自己的分支上
只需运行 git checkout -b mynewbranch
。
然后运行 git log
,您会看到提交现在是 HEAD
在这个新分支上。
mynewbranch
会附加到任何东西吗?
我在搜索 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
如果您只有 master 分支并想返回“开发”或功能,请执行以下操作:
git checkout origin/develop
注意:检查起源/开发。
您处于分离的 HEAD 状态。您可以环顾四周,进行实验性更改并提交它们,并且您可以放弃在此状态下所做的任何提交,而不会通过执行另一次签出来影响任何分支......
然后
git checkout -b develop
有用 :)
如果要推送当前分离的 HEAD(之前检查 git log
),请尝试:
git push origin HEAD:master
将您分离的 HEAD 发送到原点的主分支。如果您的推送被拒绝,请先尝试 git pull origin master
以从源获取更改。如果您不关心来自 origin 的更改并且它被拒绝,因为您做了一些故意的变基并且您想用当前分离的分支替换 origin/master - 那么您可以强制它(-f
)。如果您失去了对以前提交的某些访问权限,您始终可以运行 git reflog
来查看所有分支的历史记录。
要返回主分支,同时保留更改,请尝试以下命令:
git rebase HEAD master
git checkout master
以下对我有用(仅使用分支主控):
git push origin HEAD:master
git checkout master
git pull
第一个将分离的 HEAD 推送到远程原点。
第二个移动到分支主控。
第三个恢复连接到分支 master 的 HEAD。
如果推送被拒绝,第一个命令可能会出现问题。但这将不再是分离头的问题,而是关于分离头不知道一些远程变化的事实。
我今天刚刚遇到这个问题,并且很确定我通过以下方式解决了它:
git branch temp
git checkout master
git merge temp
当我想出如何做到这一点时,我在我的工作计算机上,现在我在我的个人计算机上遇到了同样的问题。所以必须等到星期一我回到工作电脑前才能确切地看到我是如何做到的。
如果您完全确定 HEAD 状态良好:
git branch -f master HEAD
git checkout master
你可能无法推到原点,因为你的主人已经偏离了原点。如果您确定没有其他人在使用该 repo,您可以强制推送:
git push -f
如果您在没有其他人使用的功能分支上,则最有用。
您所要做的就是“git checkout [branch-name]”,其中 [branch-name] 是您进入分离头部状态的原始分支的名称。 (与 asdfasdf 分离)将消失。
因此,例如,在分支“dev”中,您签出提交 asdfasd14314 ->
'git checkout asdfasd14314'
你现在处于超然状态
'git branch' 将列出类似 ->
* (detached from asdfasdf)
dev
prod
stage
但是要摆脱分离的头部状态并返回到开发->
'git checkout dev'
然后'git branch'将列出->
* dev
prod
stage
但这当然是如果您不打算保留分离头状态的任何更改,但我发现自己这样做很多不是为了进行任何更改,而只是为了查看以前的提交
正如克里斯所指出的,我有以下情况
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
头部重新连接!
而不是做 git checkout origin/master
做git checkout master
然后 git branch
将确认您的分支。
我遇到了同样的问题,我通过以下步骤解决了它。
如果您需要保留更改
首先,您需要运行 git checkout master 命令将您放回主分支。如果您需要保留更改,只需运行 git checkout -b changes 和 git checkout -B master changes
如果您不需要更改
要从分支中删除所有未跟踪的文件,请运行 git clean -df。然后,您需要清除存储库中所有未暂存的更改。为此,您必须运行 git checkout -- 最后,您必须使用 git checkout master 命令将分支放回主分支。
对我来说,这就像再次删除本地分支一样简单,因为我没有任何想要推送的本地提交:
所以我做了:
git branch -d branchname
然后再次检查分支:
git checkout branchname
如果您在 master 之上进行了一些提交,并且只想在此处“向后合并”master
(即您希望 master
指向 HEAD
),那么单线将是:
git checkout -B master HEAD
这会创建一个名为 master 的新分支,即使它已经存在(这就像移动 master,这就是我们想要的)。新创建的分支设置为指向 HEAD,这就是您所在的位置。新分支已签出,因此您随后处于 master 状态。
我发现这在子存储库的情况下特别有用,子存储库也经常处于分离状态。
我今天遇到了这个问题,我更新了一个子模块,但不在任何分支上。我已经提交了,所以 stashing、checkout、unstashing 是行不通的。我最终选择了分离头的提交。所以在我提交之后(当推送失败时),我做了:
git checkout master
git cherry-pick 99fe23ab
我的想法是:我是一个超然的人,但我想成为主人。假设我的分离状态与主人没有太大不同,如果我可以将我的提交应用到主人,我会被设置。这正是cherry-pick所做的。
如果您想保存在分离头上所做的更改,只需执行以下操作:创建临时分支并在完成更改后提交它,然后转到 YOUR-BRANCH 并将临时分支与它合并。最后,删除临时分支。
git checkout -b temp
git add . && git commit -m 'save changes'
git checkout YOUR-BRANCH
git merge temp
git branch -d temp
当我个人发现自己处于一种情况时,当我不在 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
简单来说,分离的 HEAD 状态意味着您没有签出到任何分支的 HEAD(或提示)。
用一个例子来理解
在大多数情况下,分支是多个提交的序列,例如:
提交 1:master-->branch_HEAD(123be6a76168aca712aea16076e971c23835f8ca)
提交2:master-->123be6a76168aca712aea16076e971c23835f8ca-->branch_HEAD(100644a76168aca712aea16076e971c23835f8ca)
正如您在上面的提交序列中看到的那样,您的分支指向您的最新提交。因此,在这种情况下,如果您签出提交 123be6a76168aca712aea16076e971c23835f8ca 那么您将处于分离的头部状态,因为您的分支的 HEAD 指向 100644a76168aca712aea16076e971c23835f8ca 并且从技术上讲,您在没有分支的 HEAD 处签出。因此,您处于分离的 HEAD 状态。
理论解释
In this Blog 它明确指出 Git 存储库是一个提交树,每个提交指向其祖先,每个提交指针都会更新,这些指向每个分支的指针存储在 .git/refs 子目录中。标签存储在 .git/refs/tags 中,分支存储在 .git/refs/heads 中。如果您查看任何文件,您会发现每个标签对应于一个文件,具有 40 个字符的提交哈希,正如上面@Chris Johnsen 和@Yaroslav Nikitenko 所解释的,您可以查看这些参考资料。
最简单的解决方案是创建一个新分支,
git checkout -b new-branch-name
然后通过命令检查您的提交日志,
git log
如果一切都匹配然后退出 :q
现在通过命令将所有更改推送到新分支
git push --set-upstream origin new-branch-name
现在,问题已解决,并且您的本地 git HEAD 已附加到新分支,您可以从门户提出拉取请求。
我进入了一个非常愚蠢的状态,我怀疑其他人会发现这很有用....但以防万一
git ls-remote origin
0d2ab882d0dd5a6db93d7ed77a5a0d7b258a5e1b HEAD
6f96ad0f97ee832ee16007d865aac9af847c1ef6 refs/heads/HEAD
0d2ab882d0dd5a6db93d7ed77a5a0d7b258a5e1b refs/heads/master
我最终修复了
git push origin :HEAD
这对我来说非常有用:
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
以检查一切是否正常
br>
On branch master
Your branch is up-to-date with 'origin/master'.
在我的例子中,我运行了 git status
,我发现我的工作目录中有一些未跟踪的文件。
为了使变基工作,我只需要清理它们(因为我不需要它们)。
如果您在 Eclipse 中使用 EGit:假设您的 master 是您的主要开发分支
提交你的更改到一个分支,通常是一个新的
然后从遥控器上拉
然后右键单击项目节点,选择团队然后选择显示历史
然后右键master,选择check out
如果 Eclipse 告诉你,有两个 master 一个本地一个远程,选择远程
在此之后,您应该能够重新连接到 origin-master。
我有同样的问题。我用 git stash
存储我的更改并将本地分支硬重置为以前的提交(我认为它导致了那个)然后做了一个 git pull
并且我现在没有让那个头分离。不要忘记 git stash apply
再次进行更改。
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
man git-symbolic-ref
的有趣:“过去,.git/HEAD
是指向refs/heads/master
的符号链接。当我们想切换到另一个分支时,我们做了ln -sf refs/heads/newbranch .git/HEAD
,当我们想找出我们是哪个分支时on,我们做了readlink .git/HEAD
。但符号链接不是完全可移植的,因此它们现在已被弃用,并且默认使用符号引用(如上所述)。git branch -f master HEAD && git checkout master
就足够了——假设您的目标是保持当前的头脑,但将其指定为master
。其他目标也很有意义,并需要其他食谱。