Git rebase交互式错误

时间:2016-10-24 23:49:19

标签: git git-rebase git-squash

我想合并我的分支(让我们称之为my_branch)来掌握。它有一个文件在我的github.com上的pull请求中显示为合并冲突。在重新定位之前我想要压缩我的提交(11次提交)。所以我做了这样的事情:

# On master
git pull
git checkout my_branch
# on my_branch
git fetch
git rebase -i origin/master

这打开了一个包含我所有提交的vim编辑器 - 我将第一个保留为pick并将其余部分更改为s (squash)

pick commit1
s commit2
s commit3
.
.
.
s commit 11

当我保存并退出时,我收到错误 - error: could not apply e7ce468... 'commit1 message'

任何人都可以向我解释这是什么问题吗?我知道我不能因为挤压而无法改变,因为每一次提交都需要解决..

1 个答案:

答案 0 :(得分:5)

你说:

  

在变基之前我想压缩我的提交(11次提交)

(强调我的)。但很快,你在重新定位期间或之后运行了git rebase来进行压扁,这将是一个有点问题。我们马上回过头来。

  

所以我做了这样的事......

总是很难说"我做了类似<填写空白>"因为那时我们不知道你实际上做了什么,这使得很难猜出实际上发生了什么。 :-)幸运的是,这些都很有道理:

# On master
git pull

这在技术上还不是必需的,但让我们来说明它的作用。 git pull命令只是git fetch的便捷快捷方式,后跟第二个Git命令。第二个Git命令默认为git merge,但您可以将其配置为git rebase。我们现在假设第二个命令只是git merge和/或它对我们没什么特别重要的,这可能是真的。

git fetch部分始终是安全的,并没有伤害任何东西。)

git checkout my_branch
# on my_branch
git fetch
git rebase -i origin/master

除非上游正在快速变化,否则第二个git fetch - 第一个git pull - 是不必要的,但一如既往,额外取出是无害的,并且可能是一个好习惯。问题在于git rebase -i origin/master,它开始重新定位。然后你编辑了squash的说明,但所有的南瓜都会在 rebase期间发生,也就是说,在重新定位到之前你不会压缩来自origin/master的新提示。

鉴于即将发生的冲突,它将在您复制的11次提交期间的某个地方发生。它似乎恰好发生在第一个:

  

当我保存并退出时,我收到错误 - error: could not apply e7ce468... 'commit1 message'

这个早期合并冲突的好处是它比后来的合并冲突更容易解释,尽管此时你可以的事情在这里与其他任何地方。

该错误告诉您Git无法完成第一次提交的副本。

您可以停止正在进行的rebase,使用以下内容将所有内容恢复原状:

git rebase --abort

继续阅读以决定是否要这样做并尝试不同的方法。

Rebase通过复制提交

起作用

使用Git时,您需要保留"提交图表"记住,如果只是作为一种背景,阴暗,不祥的迫在眉睫的东西:-)大部分时间。实际上,提交图虽然可能令人生畏,但意味着有用。它经常非常大而且很常见。" loom-ish" (可能把它想象成一个非常大但很友好的狗。)

提交图是我们可以绘制的,如果我们这样做,它会看起来像这样:

...--o--o--*--o--o--o--o--L        <-- origin/master
           |
           |
            \
             A--B--C--D--E--F--G--H--I--J--K   <-- my_branch

这些单个字母名称中的每一个都是实际提交ID的缩写(其中一个像7c56b20857837de401f79db236651a1bd886fbbb这样的丑陋的40个字符的哈希值。圆形o节点代表了我不需要说些什么的更多提交,因此它们很无聊。 *是一个提交,其ID我不知道,并且没有任何名称,但它的非常有趣,所以我给了它是明星。

(较旧的提交位于左侧,分支名称指向每个分支的 tip 提交。最后一点是Git如何工作:分支名称指向提交提交,每个提交向后指向它早期的提交。内部向后箭头有点令人分心和烦人,很难用ASCII绘制,所以我只是将它们画成线条。)

请注意,AK是11个提交,而Lorigin/master点提示(在git fetch es带来你的Git之后)最新的origin上的Git存储库。我只是随机猜测有多少普通的无聊o提交,但这并不重要。

当您运行git rebase origin/master(有或没有-i)时,Git将尝试复制所有11次提交,副本将会出现&#34;后&#34;提交L。也就是说,它想要将图形更改为:

...--o--o--*--o--o--o--o--L        <-- origin/master
           |               \
           |                A'-B'-C'-...-J'-K'   <-- my_branch
            \
             A--B--C--D--E--F--G--H--I--J--K   [abandoned]

(当你将其设为交互式rebase并使用squash时,Git会简单地修改复制过程,使其首先像往常一样A',然后从{{B'中取出A' + B 1}}并链接到L,然后从C'中取出B' + C,依此类推。这里的关键是Git一次只做一步。)< / p>

这里的问题是每个步骤都可能导致合并冲突。我们不清楚每个步骤是否 - 我们知道第一个步骤是什么,但我们对其余步骤知之甚少。

如果只有一种方法可以压缩所有11个提交,而离开它们附加到我标记为*的提交。也就是说,不是一次复制每个提交,而是将它们添加到L,如果我们可以让Git只创建一个代表AK总和的新提交A+B+C+...+J+K,那该怎么办? ?我们可以绘制这样的好结果:

...--o--o--*--o--o--o--o--L        <-- origin/master
           |\
           | AK   <-- my_branch
            \
             A--B--C--D--E--F--G--H--I--J--K   [abandoned]

现在我们可以做一个AK的简单变换(它仍会与L冲突,但我们只需要解决一次问题。)

Rebase:选择目标

嗯,事实证明这真的很容易。我们需要的是像以前一样运行git rebase -i,但要告诉它:不要重新加入L,重新加入*。也就是说,将链从另一条链上的链条复制到现在的位置。

我们基于*,但如果我们&#34; rebase&#34;在我们现在的位置,我们可以轻松地压制一切:没有冲突,南瓜就会起作用。我们只需要以某种方式命名提交*

如何找到提交*?一种方法是使用git log,它会显示所有提交ID。剪切并粘贴您关注的提交的ID:

$ git rebase -i <id-of-commit-*>
像以前一样压制壁纸,Git会做你想做的事情:压扁之前压扁。 (嗯,不完全是&#34;之前&#34;,但作为一个更小的,压扁的基础,在基础之前。)

查找提交* ID的另一种方法是使用git merge-base

$ git merge-base my-branch origin/master

这会打印出ID,准备剪切和粘贴(或者您可以使用shell语法将ID直接插入到命令中,或者设置变量,或者其他)。

还有一条捷径

您可以随意忽略此快捷方式,但您可以:

$ git checkout my_branch
$ git reset --soft <id-of-commit-*>
$ git commit

这样做是放弃A--B--...--J--K链(使my_branch指向提交*),但将文件保留在工作树和索引中因为它们在提交K中,然后使用当前索引进行新提交。因此,这会使 tree K的提交匹配,但其父提交为*。 (您必须编写一个全新的提交消息,而压缩则允许您编辑原始11提交的消息。)

或者,只需进行冲突解决

无论如何,您最终都希望将新的AK提交或原始A--B--...--J--K链重新绑定到提交L。这将需要解决合并冲突。

使用AK执行此操作的优点是,您只需解决一次,而不是每次冲突提交一次。缺点是,在一次解决一个较小的冲突时,可能更清楚该怎么做:A可能存在明显的冲突,而J有一个不同且也很明显的,K有一个与A类似的但仍然很明显......但是当你将AK组合在一起时曾经,AK之间的冲突加上J的分心,使得很难看到如何解决它。

因此,首先挤压的好处是可能发生冲突的提交次数减少。首先压缩的负数是可能冲突的提交次数减少。这取决于你尝试哪一个。

最后的一些想法

无论你做什么,请记住Git始终通过添加新提交来工作。旧的,即使他们已经放弃&#34;,仍在您的存储库中。默认情况下,它们会至少保留30天,并且他们的ID会保存在Git&#34; reflogs&#34;中。如果您想要恢复旧提交,只需使用保存的ID创建新的分支或标记名称。

HEAD有一个reflog,每个分支有一个。 my_branch reflog更容易用于从rebase中恢复。