2012-11-09 91 views
6

很久以前我有一個不好的提交,我想從git歷史中徹底刪除它,就像它從未發生過一樣。我知道提交ID讓我們說1f020。我已經嘗試了git rebase並將其刪除,但重新綁定時發生太多衝突,以至於無法這樣做。該提交是簡單的1行代碼更改,並推送一些與項目無關的文件。 所以我想寫這1行代碼更改並提交它,然後以某種方式替換這個新的提交很久以前。在git中替換提交

回答

6

如果違規承諾

git commit -C <SHA1-for-commit-just-after-bad-commit-1f020> 

重新應用一切都在一個私人倉庫,什麼你想要做的並不是什麼大不了的事情。重寫已發佈的git歷史令您的協作者感到惱火,因此請確保刪除此行是值得的。

git-rebase documentation有一個有用的通道。

git rebase [...] [<--onto newbase>] [<upstream>] [<branch>] 

提交的範圍也可以與底墊被去除。如果我們有以下的情況:

E---F---G---H---I---J topicA 

然後命令

git rebase --onto topicA~5 topicA~3 topicA 

會導致拆除提交˚F

E---H'---I'---J' topicA 

這是如果有用的話F and G以某種方式存在缺陷,或者不應該成爲topicA的一部分。請注意,參數--onto上游參數可以是任何有效的提交。

假設你的歷史是線性的,有問題的承諾是在你的主分支,您可以通過運行

git rebase --onto 1f020~ 1f020 master 

對於多毛的情況下適應上面的例子中,使用的交互變基。您可能會發現跟隨an example that merges two commits一起使用會很有幫助,但是不要使用s作爲壓扁標記提交,請移除整行以從歷史記錄中刪除提交。

+0

如果我這樣做,它會給後來的分支上的合併衝突H'我'... – sonnyhe2002

+0

@ sonnyhe2002它取決於您的存儲庫的細節和有問題的提交。 –

1

你不應該從git的歷史中刪除提交。您稍後會遇到問題。

但是,如果您知道自己在做什麼,則可以在本地回退您的歷史記錄,將當前提交的內容替換掉,然後重新播放所有提交 - 然後對存儲庫執行強制推送。

但我強烈建議不要這樣做。只要做一個新的承諾,並承認歷史上有一個不好的承諾。

+0

我知道如何回放和重放我所有的提交,如上所述。但有數百次提交需要重放數百次衝突。我正在尋求另一種解決方案。如何在不必重播所有數百次提交的情況下刪除該錯誤提交。 – sonnyhe2002

+0

@ sonnyhe2002可以做到這一點,但這並不容易,它需要編寫一個腳本。詳情請參閱我的回答。 – user4815162342

5

這是稍微複雜的,但不管怎麼說,這是怎麼一回事呢:

分離頭和移動承諾只是壞提交。使用git log來查找1f020之後的下一次提交。

git checkout <SHA1-for-commit-just-after-bad-commit-1f020> 

動腦子犯只是BEFORE壞承諾,但留下的指數和工作樹,因爲它是

git reset --soft <SHA1-for-just-previous-to-bad-commit-1f020> 

重做承諾只是壞提交重新使用提交消息,但現在在之前只提交了那個提交失敗。因此,去除壞承諾從呈交只是壞起提交到這個新的地方

git rebase --onto HEAD <SHA1-for-commit-just-after-bad-commit-1f020> master 
+0

這太棒了。 –

1

所以,你想要的是:

  1. 卸下歷史的錯誤提交,並

  2. 是不是一定要重做所有來刪除提交後的合併。即從已刪除的提交中下載的每個提交都應該保持原樣,只需稍作更改即可,不包括從歷史記錄中刪除的更改。基於git rebase --ontogit rebase -i

解決方案未能在第二點,因爲它們需要重做所有後來發生的合併。但是,理論上可以簡單地重新創建所有這些其他提交,就好像違規提交從未發生過一樣,前提是不良提交足夠小以至於從其後繼本身恢復衝突。

正如邁克爾和其他人所指出的那樣,這樣做是非常不可取的。這也是一項重大的任務,幾乎肯定不值得。但是,對於教育價值,以下是完成目標的綜合解決方案的概要:

  • 備份存儲庫。

  • 使用git rev-list生成一個提交列表,該提交列表以提交錯誤開始並導致提交可達的所有分支頭。

  • 初始化一個空映射,以將舊提交映射到新映射。

  • 每個提交列表中,請執行以下操作:

    • 退房的承諾,並恢復從壞的變化提交
    • 創建樹對象進行使用git write-tree
    • 當前樹
    • 使用git commit-tree創建一個新的提交與新的樹,舊的提交信息,父母翻譯從父母使用上述地圖(如果有些父母不在地圖中,只使用舊的父母)
    • 註冊提交地圖中的新提交。
  • 通過標記和標記對象,並重新創建它們以指向新的提交,查閱地圖。

  • 通過分行負責人並致電git update-ref指示他們到新的提交,查閱地圖。

如果提交引入的更改一行(和不必要的文件)不從後提交,這一過程可以完全自動化的變更發生衝突,並執行需要你手動重做以後提交的所有合併和衝突。

缺點是它仍然需要重寫所有後來的提交,如果它們已在其他地方共享,則使其失效,並使提交消息中的提交引用無效,例如由git revert生成的提交。

+0

好吧,我如何'初始化一個空的地圖來映射舊的提交到新的' – sonnyhe2002

+0

@ sonnyhe2002澄清,「初始化一個空的地圖」並沒有提到一個git概念,它是算法中的一個步驟。它告訴實現者創建地圖,稍後將用於將舊提交與新提交相關聯。在Python中,映射將作爲字典來實現,在Perl中作爲散列,在Bash中作爲關聯數組。這張地圖在下面的步驟中被填充和查找,所以它必須事先被初始化。 – user4815162342