通过多次提交撤消git pull?

时间:2015-07-29 19:00:18

标签: git

这个问题与我今天提出的one有关。我在帖子和其他帖子上都阅读了不同的方法,这让我很困惑,所以我在这里澄清。

让我们说我在branch1中,我将branch2拉入其中,合并可以是快进或递归。 pull会在branch1中引入很多提交,我想要撤消。我在SO上看到的一种方法是使用命令

git reset --hard SHOW-HEAD

似乎工作正常。

我也试过这个通过这个命令完成同样的事情

git reset --hard HEAD~1

我已经阅读了一些评论,如果pull带来了多次提交,那么这个命令就不会起作用。但是,根据我的观察,无论提交的数量如何,HEAD~1总是指拉动之前的最后一次提交。那是对的吗?或者是否存在可能无法保留的情况?

这是我的git reflog,ce5fceb pull引入了多次提交,但是git reflog仅提升了1次提交。通过git reset --hard HEAD~1,我可以撤消那个git pull。

f8e8370 HEAD@{0}: reset: moving to HEAD~1
ce5fceb HEAD@{1}: pull origin master: Merge made by the 'recursive' strategy.
f8e8370 HEAD@{2}: commit: commit at 11:35
b2c928b HEAD@{3}: commit: commmit at 11:32
a6fdbef HEAD@{4}: commit: commit at 7:35
40a147c HEAD@{5}: commit: commit at 7:27

2 个答案:

答案 0 :(得分:1)

我想我知道为什么你会感到困惑(好吧,因为" git可能令人困惑" :-)但我的意思更具体。)

首先,我要提一下pull只是fetch后跟merge 1 fetch步骤不会影响你的工作(这是将fetch与其他所有内容分开的一点,fetch让你的其他人工作但不接触你自己的任何事情。这是影响您自己工作的merge步骤。 (它在这里并不重要,但它是我最初发现令人困惑的另一部分git。)

让我们来看看修订版ID,特别是像e59f6c2d348d465e3147b11098126d3965686098那样丑陋的40个字符的内容。您将在整个地方看到这些内容,通常采用您在上面引用的缩写形式:f8e8370 HEAD@{2}: ...

这些东西 - 它们是SHA-1哈希值,因此它们被称为#sha; 1-shas" - git在内部使用它来识别提交。好吧,他们在存储库中识别所有,而不仅仅是提交,但重点是,这些是"真正的名称"提交。它们让人类使用起来很难看,但是这些数字始终有效,事实上,大多数时候你给git一个更人性化的名字,git只是立刻变成了一个sha -1。当您使用名称HEAD时会发生这种情况,并且通常在您使用名称branch1时也会发生。

("名称变成了sha-1"规则,这是你正在做git checkout时的一个例外。这里保留了一个分支名称作为一个分支名称。你可以给checkout一个sha-1代替,它可以让你获得git所称的"分离的HEAD&#34 ;;用一个分支名称,你的头不会被断头关闭,而git会把你放在分支上,但是其他命令,包括git resetgit merge,并不在乎。合并会将分支名称保存到放入提交消息,但合并操作使用sha-1。)

根据定义,任何提交只有一个sha-1。那个sha-1是真正的名字"提交。提交可以有许多其他名称 - 如果我们想要,我们可以称之为Joe-Bob:严重的是,您可以为任何提交分配名称 2 - 但最终git需要sha-1。

让我们再看看你引用的reflog输出(或者它的一点点):

f8e8370 HEAD@{0}: reset: moving to HEAD~1
ce5fceb HEAD@{1}: pull origin master: Merge made by the 'recursive' strategy.
f8e8370 HEAD@{2}: commit: commit at 11:35

这使用缩短的7个字符的sha-1而不是完整的sha-1,但显然第一个和第三个是相同的(如果我们查看剩余的33个字符,它们也匹配)。他们还有其他更人性化的名称:HEAD@{0}HEAD@{2}

这意味着HEAD@{2}只是提交f8e8370...的用户友好名称,HEAD@{0}也是如此。这个@{text goes here}后缀的内容还有很多,但目前只需注意有一个名称(HEAD),at符号@,然后卷曲数字周围的括号。该名称告诉git使用哪个reflog-- HEAD,在这种情况下;每个分支都有一个 - 这个数字告诉git在reflog中看多久。所以HEAD@{0}是" HEAD解决了0次之前",即,返回零次到HEAD的早期版本。这意味着使用它现在拥有的值f8e8370...。同时HEAD@{2}是" HEAD在最后两次更改之前解决的问题"。之前有两个更改,HEAD解析为与现在相同的提交sha-1。

现在,当您引用此位时,您还提到了HEAD~1

git reset --hard HEAD~1

虽然这看起来很像HEAD@{1},但它非常不同。它使用波形符~字符而不是at符号和大括号。为了描述这是如何工作的,我们必须进入"提交图表"。我在这里完全没有完全涵盖这一点,但一般的想法是,这会重新计算一些"父提交"。所以git现在找到HEAD的SHA-1,然后一次回到父提交。

(要查看所有规则,请使用git help revisions或查看gitrevisions page here。)

最后,让我们看一下git reset。这个命令可以做很多事情, 3 但是你使用它的方式git reset --hard <revision>只做了一件大事,至少在概念上:它解析了{{1提交sha-1的参数,然后它将当前的分支,索引和工作目录重新设置为该提交。

您可以随意拼写提交ID。 <revision>解析为sha-1。 HEAD@{number}解析为sha-1。 gitrevisions page为您提供了指定sha-1的所有其他方法,包括缩短的sha-1,例如HEAD~number(您可以从f8e8370git log剪切和粘贴这些sha-1输出,例如)。

要撤消真正的合并(但不是&#34;快进合并&#34;,技术上根本不是合并),名称git reflog实际上总是有效。这是因为真正的合并会创建一个包含两个父项的合并提交。 &#34;第一个父母&#34;是当前分支的前一个提示(第二个父项是合并的提交),波形语法总是使用第一个父项。 &#34;第一个父母&#34;是提交图中的一个重要概念,因为它确定了大多数人大多数人认为的主要发展方式&#34;。

名称HEAD~1 这是因为最近的更改是HEAD@{1}所做的合并。之前的值HEAD就是git pull合并之前的值HEAD。即使合并是快进的,这个版本也可以正常运行

HEAD语法的一个大问题是,如果您对git pull进行了任何其他更改,则必须不断提高数字:可能需要更改值2 -ago,或三个变化前。

(当HEAD@{n}执行失败的合并时,会出现其余问题。在这种情况下,HEAD尚未更改,撤消合并所需的内容是运行git pull。)

&#34;原始SHA-1&#34;方法总是有效,但你必须找到(然后剪切和粘贴)SHA-1(通常不是那么大的交易,真的)。

1 您可以对此进行更改:您可以HEAD执行git merge --abort,然后执行pull。重新定位可能更适合大多数人,但它不是默认值。

2 实际上,这就是git中的标签:提交的名称。 (Git有两种标签,&#34;注释&#34;标签提供额外的东西,不仅仅是一个名字,而轻量级标签只提供一个名字。)

3 Git通常会将两到五个单独的命令放入一个fetch CLI命令中,通常是因为它们在内部相关,即使外部不是很多。例如,rebase可以让您进入某个分支,只需使用重新创建合并冲突等选项提取文件。第二个只与第一个相关,第一个(&#34;上一个分支&#34;)需要提取文件。

答案 1 :(得分:0)

为了撤消pull(或其他),你只需要在拉动操作之前使HEAD指向HEAD指向的提交。你可以在reflog中找到它(你知道的)。根据您显示的reflog,我可以说这是HEAD@{2}(或f8e8370),然后:

git reset --hard HEAD@{2}

或者您可以指定参考的旧值

git reset --hard f8e8370