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

最近的某个时候,我对一些文件进行了重置,以使它们脱离提交阶段,后来又进行了重新base-I,以消除最近的几个本地提交。现在我处于一种我不太理解的状态。

在我的工作区,git日志显示了我所期望的一切——我在正确的火车上,有我不想要的提交,还有新的提交,等等。

但我只是推到了远程存储库,这是不同的——我在rebase中删除的几个提交被推送了,而本地提交的新提交不在那里。

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


当前回答

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

HEAD是当前签出提交的符号名称。当HEAD未分离时(“正常”1情况:您签出了一个分支),HEAD实际上指向分支的“ref”,而分支指向提交。因此,HEAD“连接”到分支。当您进行新的提交时,HEAD指向的分支将更新为指向新的提交。HEAD会自动跟随,因为它只指向分支。

git符号ref HEAD产生refs/heads/master将签出名为“master”的分支。git rev parse refs/heads/master产量17a02998078923f2d62811326d130de991d1a95a该提交是主分支的当前提示或“头”。git-rev-parse HEAD还生成17a02998078923f2d62811326d130de991d1a95a这就是“符号引用”的含义。它通过其他引用指向对象。(符号引用最初是作为符号链接实现的,但后来改为具有额外解释的普通文件,以便可以在没有符号链接的平台上使用。)

我们有头→ 参考/主管/主管→ 17a02998078923f2d62811326d130代码991d1a95a

分离HEAD时,它直接指向提交,而不是通过分支间接指向提交。你可以把一个独立的HEAD看作是在一个未命名的分支上。

git符号ref HEAD失败,出现致命错误:ref HEAD不是符号refgit-rev-parse HEAD生成17a02998078923f2d62811326d130de991d1a95a由于它不是符号引用,因此必须直接指向提交本身。

我们有头→ 17a02998078923f2d62811326d130代码991d1a95a

使用分离的HEAD需要记住的重要一点是,如果它指向的提交是未被引用的(没有其他引用可以访问它),那么当您签出其他提交时,它将变为“悬空”。最终,这些悬空的提交将通过垃圾收集过程被删除(默认情况下,它们至少保留2周,并且可以通过HEAD的reflog引用而保留更长时间)。

1.使用分离的HEAD进行“正常”工作是非常好的,您只需跟踪正在进行的操作,以避免从重新记录中提取掉的历史。


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

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

git branch temp
git checkout temp

(这两个命令可以缩写为gitcheckout-b temp)

这将把HEAD重新连接到新的临时分支。

接下来,您应该将当前提交(及其历史记录)与预期要处理的正常分支进行比较:

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

(您可能希望尝试使用日志选项:add-p、off--pretty=…查看整个日志消息等)

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

git branch -f master temp
git checkout master

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

然后可以删除临时分支:

git branch -d temp

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

git push origin master

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

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

其他回答

我陷入了一种非常愚蠢的状态,我怀疑其他人会觉得这很有用。。。。但以防万一

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

我最终解决了这个问题

git push origin :HEAD

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

所以我做到了:

git branch -d branchname

然后再次检查分支:

git checkout branchname

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

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

我想:啊哈!如果HEAD是当前签出提交的符号名称,我可以通过对master重新设置基址来将其与master进行协调:

git rebase HEAD master

此命令:

签出主机标识HEAD的父提交返回到HEAD从master分支的点在master之上播放这些提交

最终的结果是,所有在HEAD中而不是在master中的提交都在master中。master仍处于检出状态。


关于遥控器:

我在rebase中杀死的几个提交被推送了,而本地提交的新提交不在那里。

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

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

我今天遇到了这个问题,我更新了一个子模块,但没有在任何分支上。我已经承诺了,所以藏东西、结账、拆洗都不起作用。我最终选择了那个超然的脑袋。因此,在我提交后(推送失败时),我立即:

git checkout master
git cherry-pick 99fe23ab

我的想法是:我头脑冷静,但我想成为主人。假设我的分离状态与主状态没有太大的不同,如果我能将我的承诺应用于主状态,我就做好了。这正是樱桃树的作用。

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