13

好吧,我弄得有点乱。显然,在我家里的机器上,开发分支没有更新。我做了一个提交并推送。结果是实际的 origin/develop 分支已合并到我的本地开发分支中 - 由于某种原因被视为不同的分支!

一方面,我真的不明白这是怎么发生的,其次,我可以撤消吗?

为了说明这一点,网络现在看起来像这样:

 local-develop ---------------------------------- C*--- M -
origin/develop --- C --- C --- C --- C --- C --------- / 

我真正想要的是 C* 将致力于起源/开发而不是合并分支。

正如我所说,这已经被推动了。有没有办法删除更改并按照我想要的方式提交?

例如我这样做:

git reset --hard HEAD~1

我不确定这是否会撤消合并并且我有两个不同的开发,然后合并删除等等......?

4

2 回答 2

27

合并不会在推送时发生,它们会发生在git merge(嗯,pull但这实际上只是 fetch + merge,因此,合并发生在合并时)。

你似乎更有可能做了这样的事情:

<get copy from origin/develop>
<wait around for remote's origin/develop to acquire new commits>
git checkout develop      # get onto (local) develop branch
<edit>
git commit -m message     # create some new commit(s)
git push origin develop   # attempt to push -- but this fails!
git pull

这是创建合并提交的最后一步(M上面),因为pull意味着fetch(获取所有现在位于的新提交origin/develop),然后merge(获取您的本地develop并将您的提交与刚刚获取的新提交合并)。

如果你还没有git push编辑这个结果,那么远程仓库没有你的本地提交,你标记的那些C*M. 在这种情况下,你的状态很好!(您可以通过git fetch再次运行来检查以确保您的本地存储库origin/develop与远程存储库中的匹配,然后git log origin/develop查看其中的内容。)

记住这里有两个完全独立的 git 存储库可能会有所帮助:你的,你的东西;和你在origin这里打电话的那个。(我们称那台机器X为 。)如果您要登录到X,它有自己独立的提交历史和分支名称等等。在那里,您将目录更改为 repo,运行git log -1 develop,然后查看该分支的顶端有什么。

现在,如果您注销X并回到您自己的机器上,您可以运行git log -1 origin/develop. 如果这与您在 上看到的相同X,则get fetch无需更新,因为git fetch实际上(但更有效),登录X并查看develop其中的内容。任何X你没有的东西origin/develop,都会fetch带来并添加到origin/develop。现在您与X. X没有你的东西,但你有他们的。

如果你然后采取额外的步骤做 a merge(包括由 暗示的那个pull),git 将,如果它必须,做一个合并提交......但所有这些都在你的repo 中,在分支的顶端(仍然develop在这个案例)。除非并且直到您将此合并提交推送到X(或有人X从您那里拉出您的提交,但我们现在暂时忽略它:-)),X否则不会拥有它。

无论如何,只要远程(X此处)没有您的合并提交,您就是黄金。因为他们没有,其他人也没有。你可以做一个rebase你的develop分支,把你的 commit( C*) 放在origin/develop. 这将摆脱合并提交(M),然后您可以将简单的快进推送到origin/develop.

如果X 确实有您的合并提交(即,如果您在pull编辑后推送并获得了该合并),那么您(在某种程度上)被卡住了,因为可能其他人可以访问X并且现在正在使用您的合并提交。可以回滚 repo on X,类似于您可以在自己的 repo 中使用 etc 执行此操作的方式git resetgit rebase但这通常是一个坏主意。


现在,假设您实际上已经X推送到另一个 repo(在 machine 上),但是您绝对确定没有其他人看到您的更改,并且您肯定会重置它们,而不是还原它们(还原相当于“坦白”并留下糟糕的记录,这让其他人可以轻松地从中恢复,但也让他们看到你的错误:-))。1

这是诀窍:您首先需要让机器 X 的存储库说“分支的尖端devel是提交 C7”,其中 C7 在您之前的图表中,只是重新编号,以便我可以以不同的方式命名每个提交:

--------------------------- C*--- M
--- C4 --- C5 --- C6 --- C7 ---- /

那么,你怎么能这样做呢?好吧,一种方法是登录X, 2 cd进入 repo(即使它是--bare),然后git update-ref在那里使用。假设 C7 的 SHA1 实际上是50db850(如“git log”所示)。然后你可以这样做:

localhost$ ssh X
X$ cd /path/to/repo.git
X$ git update-ref refs/heads/develop 50db850

但是,如果您无法登录X,或者只是不想登录,您可以使用git push -f. 3 (这还有其他优点:特别是,一旦成功完成,您的 git repo 就会知道origin/develop已经倒带push -f。)只需创建一个指向正确提交的本地分支提示:4

localhost$ git branch resetter 50db850
localhost$ git log resetter         # make sure it looks right
...
localhost$ git push -f origin resetter:develop
Total 0 (delta 0), reused 0 (delta 0)
To ssh://[redacted]
 + 21011c9...50db850 resetter -> develop (forced update)
localhost$ git branch -d resetter   # we don't need it anymore

完成此操作后,机器 X 将恢复到您想要的状态,并且您可以继续进行,就好像您从未推送过您不喜欢的合并一样。

请注意,当您执行 时push -f,如果其他人在 之上进行了新提交M,这些lost+found将变得不可见(从技术上讲,它们与您的合并提交一起仍然存在,但它们在git fsck --lost-found, 和几个月后,它们真的会永远消失)。5

同样非常重要:这种“共享存储库的回滚”对于共享存储库的其他用户来说是一个很大的痛苦,所以在你这样做之前一定要确定它是好的。


1这种微不足道的合并甚至不需要还原。将合并留在那里没有根本性的问题。但是,如果您正在处理更严重的错误合并,那么除了“oops”的记录之外,恢复合并还有另一个缺点:它使稍后“重做”更改变得更加困难,因为稍后的“合并故意”将看到较早的合并并认为:好的,我不需要重新合并那些更改。然后,您必须改为“还原还原”。

我认为正确的外卖教训是:在你推动之前看看(git log),以确保你要推动的是你打算推动的东西。

2或者,更简单、更简单:git ls-remote. 这使用获取协议代码来查看遥控器有什么。但它隐藏了我要在这里讨论的主题,即:遥控器和你的一样是一个 repo!

3即将发布的 git 版本 2 具有新的“安全功能”,可用于git push -f. 但他们还没有出来,所以这对你没有帮助。稍后我会重新编辑它,以记录它们。在此之前,请务必小心:此时,您正在与任何试图将新内容推送到共享存储库的人竞争。

4您甚至可以通过原始 SHA-1 ID 执行此操作。不过,分支名称更容易连续多次正确键入。

5保留和过期是通过 git 的“reflogs”。共享服务器可能没有记录所有 ref 更新;如果没有,只有已经拥有新提交的私有仓库才会保留它们。最短的默认到期时间是 30 天,即大约 1 个月,因为提交不再可以从分支提示到达。但请注意,在强制将“丢失的工作”从共享存储库中推出后,让每个人都在他们的存储库中搜索“丢失的”提交是一种无趣、疯狂的争夺。

于 2012-04-16T20:56:56.070 回答
1

这里是 Linus Torvals 的回复 这里是 Linus Torvals 的回复http://opensource.apple.com/source/Git/Git-26/src/git-htmldocs/howto/revert-a-faulty-merge.txt

于 2013-10-18T15:36:49.693 回答