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

时间:2016-11-11 15:47:46

标签: git github merge 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

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

请为经验不足的Git用户写下你的答案。

2 个答案:

答案 0 :(得分:7)

发生了什么

&#34;复制提交&#34;正是git rebase 所做的。它会复制一些提交,然后将分支指针混洗,以便忘记&#34;或者&#34;放弃&#34;原始提交。 (但见下文。)

以下是git rebase如何进行复制的说明。单个字母表示提交,右侧的名称是分支名称,实际上只指向一个提交,即分支的&#34;提示&#34; ;。每个提交都指向它的父提交,即A--B连接符行实际上是指向左箭头(对角线也是指向左侧的箭头,对于早期的提交,后面的提交是向右) ):

     C--D   <-- branch1
    /
A--B
    \
     E      <-- branch2

这是&#34;之前&#34;图片,你只有&#34;原创&#34;提交。您现在决定git checkout branch1git rebase branch2,以便CDE之后来到。但Git实际上根本无法更改原始C--D,因此副本复制到新副本C'和{{ 1}},新的略有不同:它们来自D'(并且还使用您在E中所做的任何代码更改):

E

在这里完全忘记原来的 C--D [abandoned] / A--B \ E <-- branch2 \ C'-D' <-- branch1 会好的,但是如果你认为这毕竟是一个坏主意怎么办? rebase将分支的原始值保留在&#34; reflogs&#34;要记住它。它还使用特殊名称C--D。这样更容易使用,但只有一个 ORIG_HEAD,而且可能有无限数量的reflog条目。 Reflog条目默认保留至少30天,让您有时间改变主意。回顾第二张图并假设添加了ORIG_HEAD

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

D

如果我们现在&#34; rebase&#34; .-----F <-- branch3 / / / C--D <-- branch1 / / A--B \ E <-- branch2 ,我们得到了这个:

branch1

提交 .-----F <-- branch3 / / / C--D [ORIG_HEAD and reflog] / / A--B \ E <-- branch2 \ C'-D' <-- branch1 合并提交:它指向提交F 提交A。所以它保留了原始的D,它保留了原来的D,给我们带来了一团糟。

C可能是普通的普通提交,仅指向F,我们会看到同样的问题。但普通的普通提交更容易复制,所以如果D 合并,那么F只指向F而不是D {1}} - 我们也可以谨慎地重新定位A,将branch3复制到F,其中F'位于新F'之后。也可以重新进行合并,但这有点棘手(不是正确地复制D'就是这么简单 - 它很容易&#34;迷路了#34;并且错误地再次复制F

发生这种情况时

每当您复制您或其他人提交的提交时,您都会遇到此问题,您和其他人#34; (也许&#34;其他你&#34;)也仍在使用原件。例如,我们的提交C--D发生了这种情况:我们仍在使用原始的F链。我们可以通过制作新的C--D并使用来解决此问题,只要我们就是唯一使用F'的人。但是,如果<{1}} 已发布,或者如果我们已发布branch3,那么其他人可能会将其作为branch3或{{ 1}},我们失去了对branch1的原始副本的控制权。

因此,标准建议是仅重新设置私人(未发布)提交,因为您知道谁在使用它们 - 它当然只是您 - 并且您可以自己检查并制作确定您没有使用它们,或者可以复制它们,因为您还计划复制或重新执行origin/branch1等提交。

如果您已经完成了rebase-made副本 - 发布它们(将它们推送到origin/branch3),那么您就会陷入困境。你可以&#34;撤消&#34;无论如何你的rebase,并请求所有使用C--D的人共同确保他们不使用你的F类型副本,因为你&#39;重新放回原件。

(对于更高级的用户群,您甚至可以同意某些分支机构定期进行重新定位,并且您和他们必须全部认识到这种情况发生时,所有您将会照顾切换到新的提交副本。但是,这可能不是你现在想要做的!)

撤消

所以,如果你(a)可以和(b)想要&#34;撤消&#34;你的rebase,现在是reflog,或者保存的origin,真的派上用场了。让我们再次看第二个例子,看看我们忘记了origin仍然记得原始C'-D'提交后我们拥有的内容:

ORIG_HEAD

现在,假设我们从底行删除名称branch3并写入指向提交C-D的新 .-----F <-- branch3 / / / C--D [ORIG_HEAD and reflog] / / A--B \ E <-- branch2 \ C'-D' <-- branch1

branch1

既然我们放弃了<-- branch1,就停止查看它。将此图表与原始图表进行比较,瞧!这就是你想要的!

&#34;移动的命令&#34;像这样的任意方式的分支标签是D(它移动当前分支,因此你必须在 .-----F <-- branch3 / / / C--D <-- branch1 / / A--B \ E <-- branch2 \ C'-D' [abandoned] 上)。在reflog中查找C'-D'的原始提交哈希,或者检查git reset是否正确,或使用reflog拼写来识别提交branch1。 (对于新手,我发现原始哈希的剪切和粘贴是要走的路。)例如,尝试:

D

查看ORIG_HEAD是否为您提供正确的哈希值。如果没有,请尝试D(查看此处$ git log --graph --decorate --oneline ORIG_HEAD 的具体reflog)查找哈希值,然后使用:

ORIG_HEAD

(或者剪切并粘贴原始哈希而不是使用git reflog branch1)。一旦你找到了所需的&#34;原创&#34;提交,你可以:

branch1

(或放入$ git log --graph --decorate --oneline branch1@{1} 或原始哈希ID,代替branch1@{1}照常)。 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 ,来自reflog,或branch1@{1}或原始哈希ID),以便让我们得出最终的图形。 ORIG_HEAD设置我们的索引/登台区域和我们的工作树,以匹配我们刚刚重新指向我们分支的新提交。

1 这里的一般想法,一直在Git中重复出现,我们必须命名一些特定的提交,Git从中找到 rest <如有必要,提交/ em>任何名称都可以使用:分支名称,名称branch1@{1},reflog名称(如ORIG_HEAD)或原始提交哈希ID。 Git并不关心你如何告诉它&#34;看看这里提交&#34 ;;最终,Git将该名称解析为那些丑陋的SHA-1哈希ID,然后使用它。

答案 1 :(得分:1)

使用git reflog还原您的更改。

here (如何恢复上一个头像/如何撤消更改)中阅读所有内容:

怎么做?

输入git reflog,找出&#34;最后的好消息&#34; sha-1你想回去。

git reset <SHA-1> --hard

在你犯错之前,你又回到了之前的提交中。