我最近拆除了另一位开发人员正在处理的远程分支,我们称之为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用户写下你的答案。
答案 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 branch1
和git rebase branch2
,以便C
和D
在 E
之后来到。但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_HEAD
和C
会发生什么。例如,如果我们有这么复杂的起始图怎么办:
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
在你犯错之前,你又回到了之前的提交中。