将rebase合并到develop分支后重复提交

shr*_*ans 6 git merge github rebase

我最近拆除了另一个开发人员正在处理的远程分支,让我们称之为feature.然后我犯了一个错误,就是做了一个反思develop- 主要的工作分支 - 我现在已经知道这是你不应该做的事情.该feature分支已合并为develop.

我现在develop遇到的问题是有一个奇怪的Git历史.所有提交feature似乎都是重复的,它们出现了两次.但是,它们具有不同的提交ID.

我的历史现在看起来有点像这样(ID用于演示目的):

0007 commit from feature #3  <--- these commits are duplicated
0006 commit from feature #2
0005 commit from feature #1
0004 different commit from another branch #2
0004 different commit from another branch #1
0002 commit from feature #3
0002 commit from feature #2
0001 commit from feature #1
Run Code Online (Sandbox Code Playgroud)

我犯了一个愚蠢的错误!我能做些什么吗?历史看起来很难看,但所有正确的代码似乎都存在.我可以删除重复的提交吗?或者还有其他方法来清理历史吗?

请为经验较少的Git用户写下你的答案.

tor*_*rek 10

发生了什么

"复制提交"就是这样git rebase 做的.它复制了一些提交,然后将分支指针混乱,以便"忘记"或"放弃"原始提交.(但见下文.)

这是一个如何git rebase复制的说明.单个字母表示提交,右侧的名称是分支名称,实际上只指向一个提交,即"分支的尖端".每个提交都指向它的父提交,即A--B连接线实际上是指向左箭头(对角线也是左边的箭头,对于早期的提交,后面的提交是向右):

     C--D   <-- branch1
    /
A--B
    \
     E      <-- branch2
Run Code Online (Sandbox Code Playgroud)

这是"之前"图片,您只有"原始"提交.现在,您决定git checkout branch1git rebase branch2CD之后 E.但Git实际上根本无法改变原始版本C--D,因此它将它们复制到新的副本中,C'并且新的副本D'略有不同:它们之后E(并且还使用您所做的任何代码更改E):

     C--D      [abandoned]
    /
A--B
    \
     E         <-- branch2
      \
       C'-D'   <-- branch1
Run Code Online (Sandbox Code Playgroud)

C--D在这里完全忘记原件会好的,但如果你认为这毕竟是一个坏主意怎么办?rebase将分支的原始值保留在"reflogs"中以记住它.它还使用特殊名称ORIG_HEAD.这更容易使用,但只有一个 ORIG_HEAD,而有可能无限数量的reflog条目.Reflog条目默认保留至少30天,让您有时间改变主意.回头看第二张图并假设ORIG_HEAD已添加.

现在,您遇到的问题是因为它不仅仅是记住以前提交的分支名称.每个提交还会通过那些连接的左箭头记住它自己以前的提交.所以,让我们看看如果有另一个名称,或其他(合并)提交,记住C和,会发生什么D.例如,如果我们有这个更复杂的起始图怎么办?

    .-----F    <-- branch3
   /     /
  /  C--D      <-- branch1
 /  /
A--B
    \
     E         <-- branch2
Run Code Online (Sandbox Code Playgroud)

如果我们现在"改变" branch1,我们得到这个:

    .-----F    <-- branch3
   /     /
  /  C--D      [ORIG_HEAD and reflog]
 /  /
A--B
    \
     E         <-- branch2
      \
       C'-D'   <-- branch1
Run Code Online (Sandbox Code Playgroud)

Commit F是一个合并提交:它指向提交A 提交D.所以它保留了原件D,保留了原件C,给我们带来了一团糟.

F可能是一个普通的普通提交,仅指向D,我们会看到同样的问题.平原普通提交更容易复制,但是,如果F合并,如果我们的F尖回来才D,而不是A-我们可以仔细地重订branch3为好,复制FF'其中F'之后新的来D'.也可以重新进行合并,但这有点棘手(不是F正确复制就是这么简单 - 很容易"迷路"并且错误地C--D 再次复制).

当这个情况发生时

每当您复制您或其他人提交的提交时,您将遇到此问题,并且您和"其他人"(可能是"其他人")也仍在使用原件.这发生在我们的提交中F,例如:我们仍在使用原始C--D链.我们可以通过创建一个新的F'并使用它来解决这个问题,只要我们是唯一使用它的人branch3.但是,如果branch3公布,或者在这方面,如果我们公布了branch1,让其他人可能有他们作为origin/branch1或者origin/branch3,我们已经失去了控制权的原件C--D.

因此,标准建议是仅重新私有(未发布)提交,因为你知道谁在使用它们 - 当然只是你 - 你可以自己检查并确保你没有使用它们,或者可以复制它们因为你还计划复制或以其他方式重新提交像F.

如果你已经完成了rebase制作的副本并将它们发布(将它们推送到origin),那么你就会陷入困境.无论如何,你可以"撤销"你的rebase,并请求所有共享使用的人origin,以确保他们不会使用你的C'-D'类型副本,因为你正在放回原件.

(对于更高级的用户组,您甚至可以同意某些分支机构定期进行重新定位,并且您和他们必须都知道何时发生这种情况,然后所有人都会注意切换到新的提交副本.但是,这可能不是你现在想要做的!)

撤消它

所以,如果你(a)可以和(b)想要 "撤消"你的rebase,现在reflog或者保存的ORIG_HEAD,真的会派上用场.让我们再看第二个例子,看看我们忘记了branch3仍然记得原始C-D提交后我们有什么:

    .-----F    <-- branch3
   /     /
  /  C--D      [ORIG_HEAD and reflog]
 /  /
A--B
    \
     E         <-- branch2
      \
       C'-D'   <-- branch1
Run Code Online (Sandbox Code Playgroud)

现在,假设我们branch1从底行删除名称并写入一个新的<-- branch1指向commit D:

    .-----F    <-- branch3
   /     /
  /  C--D      <-- branch1
 /  /
A--B
    \
     E         <-- branch2
      \
       C'-D'   [abandoned]
Run Code Online (Sandbox Code Playgroud)

现在我们已经放弃了C'-D',只是停下来看看它.将此图表与原始图表进行比较,瞧!那正是你想要的!

以这种方式"移动"分支标签的命令是git reset(它移动当前分支,因此您必须打开branch1).D在reflog中查找原始提交哈希,或检查ORIG_HEAD是否正确,或使用reflog拼写来识别提交D.(对于新手,我发现原始哈希的剪切和粘贴是要走的路.)例如,尝试:

$ git log --graph --decorate --oneline ORIG_HEAD
Run Code Online (Sandbox Code Playgroud)

看看ORIG_HEAD你是否得到了正确的哈希.如果没有,请尝试git reflog branch1(查看branch1此处的特定reflog )查找哈希值,然后使用:

$ git log --graph --decorate --oneline branch1@{1}
Run Code Online (Sandbox Code Playgroud)

(或者剪切并粘贴原始哈希而不是使用branch1@{1}).一旦找到所需的"原始"提交,您就可以:

$ git status     # to make sure you're on the right branch
                 # and that everything is clean, because
                 # "git reset --hard" wipes out in-progress work!
$ git reset --hard ORIG_HEAD
Run Code Online (Sandbox Code Playgroud)

(或者放入branch1@{1}原始哈希ID,代替ORIG_HEAD原样).1 移动当前分支(我们刚检查过),使其指向给定的提交(branch1@{1}来自reflog,或ORIG_HEAD原始哈希ID),以使我们得到最终的图形.该--hard套了我们的指数/舞台区,我们的工作树,以符合新的承诺,我们刚重新指向我们的分公司.


1这里的一般想法,一直在Git中重复出现,我们必须命名一些特定的提交,如果有必要,Git会从中找到其余的提交.任何名称都可以使用:分支名称,名称,类似HEAD的reflog名称master@{1}或原始提交哈希ID.Git并不真正关心你如何告诉它"看看这里提交"; 最终,Git将该名称解析为那些丑陋的SHA-1哈希ID,然后使用它.