2012-04-16 125 views
13

好吧,我弄得一团糟。显然,在我家的机器上,开发分支没有更新。我做了一个提交并推送。结果是实际的起源/开发分支已经合并到我的本地开发分支中 - 由于某种原因,它们被认为是不同的分支!撤消已被推送的合并

首先,我真的不明白这是怎么发生的,其次,我可以解决这个问题吗?

来说明吧,现在的网络看起来是这样的:

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

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

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

比如我做的:

git reset --hard HEAD~1 

我不知道这是否撤销合并,我有两个不同的发展,然后合并删除等...?

回答

26

合并不会发生在推,他们发生在git merge(嗯,和pull,但这只是取+合并,因此,合并发生在合并)。

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

<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,它有它自己的单独的提交历史和分支名称等等。在那边,你可以将目录切换到回购站,运行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没有你的东西,但你有他们的。

如果你再取做merge(包括一个由pull暗示),git会,如果有,做一个合并提交...额外的步骤,但是这一切都是你的回购,在分支的尖端(在这种情况下仍然是develop)。除非并且直到你将这个合并提交到X(或者X上的某人从你那里提取你的提交,但是现在让我们忽略它:-)),X将不会拥有它。

无论如何,只要远程(X这里)没有你的合并提交,你是黄金。由于他们没有它,没有人做。您可以执行develop分支的rebase以将您的提交(C*)置于origin/develop之上。这将摆脱合并提交(M),然后你可以推一个简单的快进到origin/develop

如果X确实有您的合并提交 - 即,如果你以后你推pull ED和得到了合并,那么你就完蛋了(在某种程度上),因为想必其他人获得X和目前正在使用你的合并提交。可以在X上回滚回购,类似于您可以在自己的回购中使用git resetgit rebase等的方式执行此操作,但这通常是一个糟糕的主意。


现在,假设你有 事实上被推到其他回购(在机器 X),但你绝对确定没有人看到你的变化,你一定要重置它们,而不是恢复他们(回复数量为“精神抖”“,并留下紧缩记录,这可让所有人轻松恢复,但也让他们看到你的错误:-))。

这里的窍门:你首先需要获得计算机X的回购说“分支devel的尖端提交C7”,其中C7是从早期你自己的图,只是重新编号,使我可以以不同的方式命名每个提交:

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

那么,你怎么能做到这一点?那么,一种方法是登录X, cd进入回购(即使它是--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。 (这有其他优点:特别是你的混帐回购协议就知道origin/develop已倒,一旦push -f成功完成。)只是做一个本地分支尖端指向正确的承诺:

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,并在几个月后,他们将永远消失)。

再次和非常重要:这种“共享回购的回退”的是为共享式回购的其他用户的一个很大的痛苦,所以真的确定你做之前还是可以的。


这类琐碎的合并甚至不需要恢复的。将合并留在那里没有任何根本性的错误。但是,如果你正在处理更严重的错误合并,除了你的“oops”记录之外,还有另一个缺点来恢复合并:它稍后会使“更改”更改变得更困难,因为稍后的“合并故意“会看到早先的合并和想法:好吧,我不需要重新合并那些的变化。您必须“恢复恢复”。

我认为这里的正确结束课程是:看看(git log),然后再推以确保您要推送的内容是您打算推送的内容。

或者更容易和更简单:git ls-remote。这使用获取协议代码来查看远程具有的内容。但它隐藏了我要去的主题,即遥控器就像你的回购一样!

即将推出的Git版本2的发行都有新的“安全功能”,这将是可用以git push -f。但他们还没有出来,所以这对你没有帮助。我会稍后重新编辑它们,以便记录它们。在此之前,请注意请注意:在这一点上,您正在与其他尝试将新内容推送到共享存储库的任何人竞争。

您甚至可以通过原始SHA-1 ID执行此操作。虽然分支名称可以连续多次正确输入。

保留和过期是通过git的“reflogs”。共享服务器可能不会记录所有ref更新;如果没有,只有已经有新提交的私人回购才会保留它们。最短的默认过期时间为30天,即约一个月,对于不再从分支提示中获得的提交。但是请注意,这是一个没有趣味,疯狂的争吵,使得每个人都在通过他们的仓库搜索“丢失”的提交之后,将其从共享仓库中“推卸责任”后进行搜索。

+0

哇,非常感谢这个广泛的答案。不幸的是我把它推到了X.因为我确信没有其他人提出了新的提取,所以我想知道是否可以撤销这些更改。 有一件事我真的不清楚:如果我重置两个步骤,我是否留下了其中一个分支或两者? – dmeu 2012-04-17 07:51:11

+0

好吧,推之后,如果你真的确定* :-) ...你想要做的就是让X关于分支'devel'的提示的想法倒回一点。让我编辑答案... – torek 2012-04-17 07:59:36

+2

呵呵,如果你把git reset HEAD〜2'移回两步,这真的等于告诉git:“设置我现在所在的分支的尖端” - 这很可能是你的'通过查找HEAD〜2开发' - “到您提交的提交ID。如果你想看看是什么提交,首先'git show HEAD〜2'。如果你从上图中提交'M','HEAD〜2'是'M〜2',它通过'M'和'C *'备份。可能不是你想要的。 – torek 2012-04-17 08:55:43