git:'git reset --hard bd53134后的独立头

Jea*_*eri 2 git reset

我在分支机构工作,需要撤消一些提交.所以我做了

$> git reset --hard bd53134
Run Code Online (Sandbox Code Playgroud)

这有所期望的效果,除了我现在有一个独立的头:(

$> git status
HEAD detached at bd53134
Run Code Online (Sandbox Code Playgroud)

我怎样才能解决这个问题 ?

tor*_*rek 10

看起来你可能仍在四处寻找,所以这里有一些背景知识.你在评论中提到你开始了git rebase并且git rebase --continue总是在抱怨.(这确实应该是原始问题的一部分.)

首先注意:HEAD可以是正常的("未分离"?)或"分离"."正常"只HEAD包含分支名称.如果你是"在分支机构master",HEAD只需说"分支机构主".一个"分离的HEAD",听起来像法国大革命中的东西,而不是原始的SHA-1提交ID.我喜欢在下面的图中编写附加的HEAD,HEAD=branch然后显示分支指向的位置,使用箭头指向一个特定的提交.

在开始一个rebase之前,你有一个提交图,看起来像这样:

...- A - B - E - F         <-- master, origin/master
           \
             C - D         <-- HEAD=branch
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我假设你做了一个git checkout -b branchfrom master,并做了一些提交branch(创建CD),然后可能做了一个git checkout master,git pull它带来了提交EF"远程origin".然后你回去branchgit checkout branch(因此HEAD=branch).

然后,您决定应该重新设置两者CD在其之上F,提供更线性的提交序列:

...- A - B - E - F         <-- master, origin/master
                  \
                   C' - D' <-- HEAD=branch
Run Code Online (Sandbox Code Playgroud)

(我将在片刻展示为什么那些是C',D'而不是CD.)所以你跑去git rebase master"移动"提交CD进入尖端master.

Rebase实际上并没有移动提交.它的作用是复制一些现有的提交,使新的"做同样的事情"并且"同样好"(我们希望!).因此,rebase保持CD周围.它使用一个特殊的标签ORIG_HEAD来跟踪提交D(以及一堆额外的.git/rebase-apply/文件来跟踪整个rebase操作的所有进度 - 这些文件实际上是git知道rebase是"正在进行中").

Rebase通过添加ORIG_HEAD和"分离HEAD"来启动该过程.它将"分离的HEAD"设置为直接指向rebase的目标(F在本例中为commit ).(通过探讨,似乎文档略微关于它重置分支.但是在旧版本的git中可能会有所不同;我认为文档在某一点上是准确的.)因此:

...- A - B - E - F         <-- master, origin/master
          \       \
           \       \...... HEAD [detached]
            \
             C - D         <-- ORIG_HEAD, branch
Run Code Online (Sandbox Code Playgroud)

然后,对于每个提交(CD此处),它获取提交中所做的更改,并尝试将相同的更改应用于HEAD提交.如果一切顺利 - 它通常会 - 它使用与旧提交相同的消息进行新提交.这是承诺C':

...- A - B - E - F         <-- master, origin/master
          \       \
           \       C'..... HEAD [detached]
            \
             C - D         <-- ORIG_HEAD, branch
Run Code Online (Sandbox Code Playgroud)

申请成功后C,以F使C'脱落F,底垫命令继续尝试应用D到新的(但仍然分离的)HEAD,C'.但是,这次出现问题:补丁不适用:

CONFLICT ...
Failed to merge in the changes.
Patch failed at ...
The copy of the patch that failed is found in:
    ...

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
Run Code Online (Sandbox Code Playgroud)

此时,您的提交图看起来像我上面绘制的(有点混乱)图.

作为rebase打印,您可以:

  • 手动解决问题git rebase --continue,或
  • 选择删除一个承诺(D在这种情况下)与git rebase --skip,或
  • 退出了整个事情git rebase --abort.

选择最后一个选项告诉rebase停止rebase尝试,放回HEAD原来的样子,并删除"rebase in progress"状态/跟踪文件.这也会丢弃整个新的链(但尚未标记为分支)提交.(从技术上讲,他们仍然在那里,在reflog中.你通常不会看到它们.)

执行第一个("手动解析")或选择中间("跳过")选项,让rebase继续向前.假设你解决问题了git rebase --continue.一旦rebase用完提交 - 并且D是最后一个 - 它将分支名称移动到最终提交,并HEAD再次设置为分支名称.所以在这种情况下,你得到这个:

...- A - B - E - F         <-- master, origin/master
          \       \
           \       C' - D' <-- HEAD=branch
            \
             C - D         <-- ORIG_HEAD
Run Code Online (Sandbox Code Playgroud)

由于犯下指出,由ORIG_HEAD不正常显示,它看起来像CD都消失了,和副本(C'D')是唯一的提交离开.(一如往常虽然,CD实际上仍然在那里,并坚持了一段时间,直到引用日志条目到期.)


考虑到所有这些,一旦你处于"超级HEAD"状态,git reset --hard就没有分支名称可以影响.它将移动HEAD并更改工作目录,但HEAD仍将直接指向提交.所以git reset你在一个篮子中间做的任何事情,最好是奇怪的.(如果你继续下去的话,他们会弄清楚rebase会发生什么,因为继续一个rebase只是不断增加到HEAD当时的任何一点.)

在更正常的情况下(当你在"分支上"时),HEAD具有分支的名称.然后(并且只有那时),git reset可以并且确实更改了branch-name指向的提交.

你怎么知道git reset将采取哪些行动,以及发生了什么?答案是使用git status.如果你不确定发生了什么,那git status就是有帮助的.在过去的几年里,它实际上也在帮助它变得更好!:-)


小智 7

如果你在一个分支上,git reset --hard bd53134应该更新分支,并让你离开那个分支.因此,我怀疑你实际上并不在分支机构.

如果这是正确的,通过回到分支来解决它的一种方法:

git checkout <branch>
Run Code Online (Sandbox Code Playgroud)

然后git reset再次运行您已经使用过的相同命令.

  • 当你处于一个rebase的中间时,`HEAD`总是被分离.使用`git rebase --abort`来终止rebase操作,保持原始分支不变(git会将`HEAD`放回到未重定位后的分支的顶端). (2认同)