跪求不要 git push -f
起因
看到一則新聞,美國某程式員槍擊同事案件,很多網友猜測出了各種激怒碼農同事的辦法
9月19日,一名程式設計師在美國某辦公樓向4名同事開槍,導致一人情況危機,兩人傷情嚴重,一人被子彈擦傷。目前,凶手已死,身份被警方查明。
目前,碼農持槍殺人的動機仍然是個謎。有人猜測道:“同事不寫註釋,不遵循駝峰命名,括號換行,最主要還天天 git push -f 等因素” 激怒了這名行凶者。
正常的流程是要在自己的本地解決掉所有的 merge conflict 之後才能 push 到 remote 的。魯莽的 push -f 確實很容易激怒別人,同時也會很大風險把別人有價值的 commit 都覆蓋清空了。
push -f 自然是能不用就不用,但也有一種清空是整個team決定要清一些 commit 把 master 分支回退到某個點上去,自然就想到了 push -f 會是一種非常便捷的做法了。那能不能更優雅的去實現我們的回退操作呢,因為最佳實踐原則上 git 歷史記錄不應該被覆蓋的。
解決辦法
直接在 stackoverflow 就能找到答案
ofollow,noindex">https://stackoverflow.com/questions/1463340/how-to-revert-multiple-git-commits
當前的情況如下圖:我想去掉 B,C,D 這三個 commit,把 F 作為新的 master HEAD
A -> B -> C -> D (HEAD) \ E --------> F
不覆蓋歷史記錄,最終的效果應該如下,會用 commit G revert 程式碼到和 A 一樣狀態,然後把 F merge 回 master
A -> B -> C -> D -> G -> H (HEAD) \/ E ----------------> F
第一種方法:一步步的 revert
$ git revert --no-commit D $ git revert --no-commit C $ git revert --no-commit B
當要 revert 的 commit 數量很多的時候會很麻煩。
第二種方法:revert 一個區間的所有 commit
$ git revert --no-commit A..HEAD
第三種方法:使用 reset,這個屬於比較 “奧祕” 的用法
$ git reset --hard A $ git reset --soft D# ( or ORIG_HEAD, or @{1} ) (or) $ git reset --mixed D
實踐對比
準備了一個樣板模擬場景,我們想要把 master 分支的程式碼回滾到 init base 那裡,然後 merge fix-branch-A 的程式碼成為 master 新的 HEAD
Let's git push -f (⊙⊙!)
首先是最初的 git log

最初的 git branch 圖
然後我們可以直接 reset 到 init base 然後 push -f
$ git reset --hard d194ece $ git push -f
最終效果就是 version 2 和 version 3 都消失了

push -f 的最終效果
大家不要慌,其實這種破壞性的操作之後,仍然有“後悔藥”可以吃的,可以把丟失的 commit 搶救回來。
$ git reflog show --all
使用 git reflof 記錄了所有的操作,可以檢視到我們之前是 ‘moving from b7e4fcc to d194ece ’
$ git reset --hard b7e4fcc
這樣就可以找回遺失的 commit
Let's gracefully revert back
又準備了一個乾淨的模板

最初的 git branch 圖2
使用 revert 或者 reset 的方式生成一個 rollback commit

rollback commit之後
然後 merge fix-branch-A

merge 之後的 log graph
最終的 git 歷史記錄裡面仍然會保留所有我們廢棄掉的 commit,更加優雅。