首先,让我们澄清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产量17a02998078923f2d62811326d130de991d1a95a
该提交是主分支的当前提示或“头”。
-
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 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
如果远程分支不能“快进”到新的提交(即你删除了,或者重写了一些现有的提交,或者以其他方式重写了一些一点历史)。
如果您正在进行 rebase 操作,您可能应该清理它。您可以通过查找目录.git/rebase-merge/ 来检查是否正在进行变基。您可以通过删除该目录来手动清理正在进行的变基(例如,如果您不再记得活动变基操作的目的和上下文)。通常你会使用git rebase --abort,但这会做一些你可能想要避免的额外重置(它将 HEAD 移回原始分支并将其重置回原始提交,这将撤消我们在上面所做的一些工作)。