Git 如何遺棄已經 Push 的提交

2021-09-11 12:54:31 字數 3982 閱讀 5228

相信大家都知道,git 相比於 svn,優勢不言而喻,以致於現在大多數公司的專案都在採用 git 進行管理。作為乙個開發人員,對 git 的使用自然應該是得心應手。

如果你還不會使用 git 的話,那我勸你還是不要聲張,好好的去學習一番,再自己弄個實驗專案走一下流程,以免遭到同事的鄙視。

每個公司都會有自己不一樣的 git 分支管理規範,特別是在開發人員較多的公司,git 的分支管理規範就顯得更加重要。前面比較出名的 git flow 分支管理策略相信不少人都已經了解了,不熟悉的當然也可以去看看:nvie.com/posts/a-suc…

git flow 管理方式把專案分為 5 條線,通常會是下面的管理方式。

實際上,如果你熟悉 git 的話,你會很快發現上面的管理方式會存在歷史提交非常混亂的缺點,但覺得不失為乙個 git 分支管理的經典。實際上,我們可以用 rebase 去替換 merge 讓 commit 看起來更加清晰。對 rebase 和 merge 的優劣對比這裡暫不做講解,感興趣的可以直接 google 搜尋。

下面就給大家分享一下發生在咕咚專案的一次坑爹的 git 體驗。

咕咚專案組並沒有對開發者限制 develop 分支和 master 分支的許可權,我們暫時並沒有乙個專門做** review 和 pr 的角色,其實一定意義上也提現了團隊對每個人的信任。

我們依然會基於 develop 做開發主線,每個需求迭代期,團隊成員會從 develop 拉取自己的分支,並命名於 feture/xx,然後各自在自己的分支上進行開發。

由於大家開發業務上的不同,所以在需求開發完畢,整合**到 develop 分支的時候,一般不會出現太多衝突的情況。

而我這邊交接乙個需求時,採用 merge 的時候出現了乙個奇怪的問題,我們姑且來重現一下事故現場。

首先使用git branch檢視一下當前我們的本地分支。

這裡先簡單提一下我們要做的操作。

"feature8.28_buygifts" 是我們同事的分支,基於 "release8.27.0" 拉取,而 "feature8.29.0_nanchen" 是我的分支,基於 "release8.28.0" 分支拉取,所以我這邊的分支包含了最新的**。

現在由於某些原因,我需要把同事的 "feature8.28_buygifs" 分支**合併到我的分支上,直接接手他的**進行開發。

就不要吐槽為啥不按照功能搞分支開發了,原因是因為他那邊**基本已經完成,現在只需要少量修改。

所以我們就採用git merge

命令進行 merge 操作。

我們用git status更容易看明白衝突了什麼。

可以看到,上面衝突的檔案全是和同事開發的需求出現的衝突,所以出現這個衝突其實令人非常懊惱,因為是不可能有其他同事改動到這些檔案的。

為了驗證自己的想法,我們隨意開啟乙個檔案檢視。這裡就採用vim檢視第乙個檔案。

正如我們所想,確實和同事編寫的需求presents類有關係,但看衝突內容就更一臉懵逼了,因為看起來,這應該是乙個不會衝突的 merge。

於是趕緊使用git merge --abort撤銷這次 merge。再在 "origin/feature8.29.0_nanchen" 檢視我們剛剛的檔案提交歷史。

可以很清晰的看到,確實是最近沒有任何的修改記錄。

乙個 7 個月都沒人動的檔案,居然 merge 的時候發生了衝突!這讓我一臉懵逼。(手動黑人問號)

使用git lg檢視一下該分支的提交歷史,我們希望從中能得到某些思路。

注意其中紅框中的 commit,我們這位同事之前想往 "release8.28.0" 合併他分支的**,後面又因為某些原因,希望撤銷這次提交,他採用了 revert 進行處理。雖然 revert 對檔案沒有提交記錄,但 git 卻認為我們在當前分支更改了這些檔案,所以在我們git merge的時候,git 認為這是一次衝突,並選擇了告知我們。

如若如我們所想,那我們只需要撤銷這次 revert 操作即可。

我們當然知道,可以通過 reset 命令放棄這次提交,但這裡後面已經有了非常多的 commit,顯然我們這樣是不行的,我們需要另闢蹊徑。

最容易想到的大概就是直接在 merge 的時候解決衝突了,但通過一系列檢視以後,我們發現檔案改動量非常大,直接解決衝突並非易事。所以我們還是得想辦法取消掉這次 revert 的 commit,再進行 merge

我們知道,**回滾有三種方式:reset、checkout,還有我們的 revert。直觀感受,我們應該在 reset 上想辦法。

我們來看看 reset 有些怎樣的操作方法。

我們經常會用到git reset --hard做「毀屍滅跡」的操作,常常爽到不能自已,因為這不僅可以回退到我們想要的版本,而且還「直接丟棄」了後面提交的**,真正的「毀屍滅跡」級別的操作。

而另外乙個 --soft 處理,實際上還具備點人性,雖然同樣可以回退到我們想要的版本,但目標版本後面的提交都還會存放在 stage 區域中,以便後面找出證據。

說到這,似乎我們已經有了思路。

使用git reset --soft回退到 revert 操作的版本;

使用git reset --hard乾掉那次 revert 提交;

最後再把 stage 區域的所有改動匯聚成乙個新的提交 commit 到我們的專案倉庫中。

當然,細心的你一定會發現,在第 1 步操作後,我們還必須執行git stash命令把所有的改動存到暫存區,再在第 2 步操作後使用git stash pop命令取出來,直接進行第 2 步操作肯定還是會毀滅證據的。

這樣似乎可以解決我們的問題,不過有個弊端:我們後面那麼多的提交被合併成乙個提交了,以後我們就沒辦法看到了,萬一...

不少小夥伴會想到高階方案:

對 "feature8.29.0_nanchen" 的最新** checkout -b 乙個分支 feature_copy;

然後使用git checkout feature8.29.0_nanchen回到我們的分支;

然後直接對當前分支 reset 到 revert 的前乙個 commit 後,我們採用 cherry-pick 方式進行傻瓜式改寫便可以把歷史重寫了。(誰說的我們不能改寫歷史?)

改寫歷史?等等,好像還有乙個操作:rebase。

rebase 是 git 的乙個神奇的命令,前面我也說了,總會有人不喜歡 merge 之後歷史的分叉,這種分叉再匯合後會讓結構看起來非常混亂,以致於無法管理。如果你不喜歡 commit 歷史出現分叉,那 rebase 絕對是你的救星。

改寫歷史是 rebase 與生俱來的能力。我們可以用git rebase -i進行歷史的改寫。

我們試試看在我們的專案中直接使用git rebase -i會怎樣。

我們會拿到分支後面的提交歷史,並且前面還有乙個 commands。我們可以從提示中看到,上面全寫的 pick 就是代表保持這個提交的意思,edit 代表編輯此次提交...

我們希望刪除此次 revert 這次提交,那當然我們最關心的就是 drop 了,甚至我們可以更加簡單粗暴:直接刪掉這一行

然後我們便開始處理了。

過程中可能會出現衝突,我們只需要解決就好。

解決掉衝突後,再使用git add把它們 merge 進去。

oh,我們看到我們已經 rebase 成功了。我們再使用git lg檢視一下提交歷史。

我們成功改寫了歷史!

歷史改寫結束,我們還要做我們最開始想做的事情,進行 merge 操作。

可以看到,這次我們 merge 確實如我們預期的不再發生衝突,方案親測有效!

Git撤銷已經push的檔案

撤銷已經push的檔案該怎麼做呢?比如我第一次提交了四個修改的 檔案,然後第二次和第三次又提交了部分修改,但是這兩次修改又不想要了,該怎麼辦呢?首先git branch 檢視當前分支 git log 檢視提交日誌 git reset hard db6cde47b337847c2e2fbc22f9de...

git 怎麼回退已經push的版本 git回退版本

前話 在vscode中,安裝git history外掛程式,安裝之後就可以用f1,選擇git view history git log 即可看到git的提交記錄。在每一行的最後面,就是commit id git reset 如果回退乙個只是commit的,並沒有push到遠端分支的提交 則直接執行 ...

git如何忽略已經被追蹤的檔案

有時候我們在專案初始化提交時,將某個無用檔案提交到了遠端倉庫中。之後,在整理專案時,你希望將遠端倉庫中的這個無用檔案新增到.gitignore忽略檔案中,從而令其修改不會被git追蹤到。然而,事與願違,你會發現,一旦這個檔案被改動 幾乎都是由於專案編譯引起的改動 git總能追蹤到他,及時已經正確的被...