GIT如何撤銷已經Merge的Branch

Posted by JSON on March 21, 2016

history

如上圖情境在master上已經merge了三條branches, 今天想要revertC10,也就是想丟掉tv/rebse-stat, 保留jk/post-checkout以及db/push-cleanup的其中一個作法如下:

  1. 返回到C8
  2. 重新mergejk/post-checkoutdb/push-cleanup這兩條branches
$ git checkout master
$ git reset --hard [sha_of_C8]
$ git merge jk/post-checkout
$ git merge db/push-cleanup

reset + remerge 後結果如下:

git reset and remerge

好景不常,如果今天在merge後又產生了多個commits,如下圖在merge多了C9C10:

new history

這時候若想撤銷jk/post-checkout,使用前述的方法將會失去C9C10, 當然也可以使用cherry-pick的方式把C9C10一個個撿回來, 但假設在merge之後所產生是C9C10C100會撿到累死, 因此我們可以使用第二種方法git revert -m 1來撤銷merge commit:

$ git revert -m 1 [sha_of_C8]

操作完成後,我們會得到下面的結果,產生了^C8, 裡面的內容是對整條branchjk/post-checkout所含commits的撤銷, 與前述的方法比較,前者可看成是回到過去修改歷史,後者可看成是現在對過去的行為做修正

git revert branch

將暫時撤銷的Merge再次復原

從上一張圖開始看,若對jk/post-checkout的撤銷只是暫時的,之後想重新merge這一支branch, 透過git merge jk/post-checkout,我們會得到Already up-to-date, 原因是在過去的歷史中我們已經merge過這一支branch,另外一種可能如下圖:

git merge again after reverting

如果之後在jk/post-checkout我們增加了C11, 而過去的歷史是在C6merge回master,這時候進行git merge jk/post-checkout, 我們只會拿到C11的更新。

回到下面這張圖:

git revert branch

我們可以用git revert [sha_of_^C8]來達成Reverting the Revert, 讓C5C6回到master,得到下面這張圖的結果:

git revert the revert

C11是在之後才新增的commit,因此這時候只要再執行git merge jk/post-checkout, 就能完整的將整條jk/post-checkout的commits都merge回master上,如下圖:

git revert and merge

另外,在進行merge的時候使用--no-ff來確保不會進行fast forward, 這樣的好處是確保在merge時會產生一個commit以便我們revert也能看清楚整條master發生的行為。

Reference:Undoing Merges