2012-04-16 67 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