當前位置:編程學習大全網 - 編程語言 - git回退git回退操作取消

git回退git回退操作取消

從接觸編程就開始使用Git進行代碼管理,先是自己玩Github,又在工作中使用Gitlab,雖然使用時間挺長,可是也只進行壹些常用操作,如推拉代碼、提交、合並等,更復雜的操作沒有使用過,看過的教程也逐漸淡忘了,有些對不起Linus大神。

出來混總是要還的,前些天就遇到了Git裏壹種十分糟心的場景,並為之前沒有深入理解Git命令付出了壹下午時間的代價。

先介紹壹下這種場景,我們壹個項目從N版本升到A版本時引入了另壹項目的jar包,又陸續發布了B、C版本,但在C版本後忽然發現了A版本引入的jar包有極大的性能問題,B、C版本都是基於A版本發布的,要修復jar包性能問題,等jar包再發版還得幾天,可此時線上又有緊急的Bug要修,於是就陷入了進退兩難的境地。

最後決定先將代碼回退到A版本之前,再基於舊版本修復Bug,也就開始了五個小時的受苦之路。

基礎試探

revert首先肯定的是revert,gitrevertcommit_id能產生壹個與commit_id完全相反的提交,即commit_id裏是添加,revert提交裏就是刪除。

但是使用gitlog查看了提交記錄後,我就打消了這種想法,因為提交次數太多了,中途還有幾次從其他分支的merge操作。”利益於”我們不太幹凈的提交記錄,要完成從C版本到N版本的revert,我需要倒序執行revert操作幾十次,如果其中順序錯了壹次,最終結果可能就是不對的。

另外我們知道我們在進行代碼merge時,也會把merge信息產生壹次新的提交,而revert這次mergecommit時需要指定m參數,以指定mainline,這個mainline是主線,也是我們要保留代碼的主分支,從feature分支往develop分支合並,或由develop分支合並到master的提交還好確定,但feature分支互相合並時,我哪知道哪個是主線啊。

所以revert的文案被廢棄了。

Reset然後就考慮reset了,reset也能使代碼回到某次提交,但跟revert不同的是,reset是將提交的HEAD指針指到某次提交,之後的提交記錄會消失,就像從沒有過這麽壹次提交。

但由於我們都在feature分支開發,我在feature分支上將代碼回退到某次提交後,將其合並到develop分支時卻被提示報錯。這是因為feature分支回退了提交後,在git的workflow裏,feature分支是落後於develop分支的,而合並向develop分支,又需要和develop分支保持最新的同步,需要將develop分支的數據合並到feature分支上,而合並後,原來被reset的代碼又回來了。

這個時候另壹個可選項是在master分支上執行reset,使用--hard選項完全拋棄這些舊代碼,reset後再強制推到遠端。

mastergitreset--hardcommit_idmastergitpush--forceoriginmaster但是還是有問題,首先,我們的master分支在gitlab裏是被保護的,不能使用forcepush,畢竟風險挺大了,萬壹有人reset到最開始的提交再強制push的話,雖然可以使用reflog恢復,但也是壹番折騰。

另外,reset畢竟太野蠻,我們還是想能保留提交歷史,以後排查問題也可以參考。

升級融合

rebase只好用搜索引擎繼續搜索,看到有人提出可以先使用rebase把多個提交合並成壹個提交,再使用revert產生壹次反提交,這種方法的思路非常清晰,把revert和rebase兩個命令搭配得很好,相當於使用revert回退的升級版。

先說壹下rebase,rebase是”變基”的意思,這裏的”基”,在我理解是指commit形成的gitworkflow,使用rebase,我們可以改變這些歷史提交,修改commit信息,將多個commit進行組合。

介紹rebase的文檔有很多,我們直接來說用它來進行代碼回退的步驟。

首先,切出壹個新分支F,使用gitlog查詢壹下要回退到的commit版本N。使用命令gitrebase-iN,-i指定交互模式後,會打開gitrebase編輯界面,形如:pick6fa5869commit1pick0b84ee7commit2pick986c6c8commit3pick91a0dcccommit4這些commit自舊到新由上而下排列,我們只需要在commit_id前添加操作命令即可,在合並commit這個需求裏,我們可以選擇pick最舊的commit1,然後在後續的commit_id前添加squash命令,將這些commits都合並到最舊的commit1上。保存rebase結果後,再編輯commit信息,使這次rebase失效,git會將之前的這些commit都刪除,並將其更改合並為壹個新的commit5,如果出錯了,也可以使用gitrebase--abort/--continue/--edit-todo對之前的編輯進行撤銷、繼續編輯。這個時候,主分支上的提交記錄是older,commit1,commit2,commit3,commit4,而F分支上的提交記錄是older,commit5,由於F分支的祖先節點是older,明顯落後於主分支的commit4,將F分支向主分支合並是不允許的,所以我們需要執行gitmergemaster將主分支向F分支合並,合並後git會發現commit1到commit4提交的內容和F分支上commit5的修改內容是完全相同的,會自動進行合並,內容不變,但多了壹個commit5。再在F分支上對commit5進行壹次revert反提交,就實現了把commit1到commit4的提交全部回退。這種方法的取巧之處在於巧妙地利用了rebase操作歷史提交的功能和git識別修改相同自動合並的特性,操作雖然復雜,但歷史提交保留得還算完整。

rebase這種修改歷史提交的功非常實用,能夠很好地解決我們遇到的壹個小功能提交了好多次才好使,而把git歷史弄得亂七八糟的問題,只需要註意避免在多人同時開發的分支使用就行了。

遺憾的是,當天我並沒有理解到rebase的這種思想,又由於試了幾個方法都不行太過於慌亂,在rebase完成後,向主分支合並被拒之後對這些方式的可行性產生了懷疑,又加上有同事提出聽起來更可行的方式,就中斷了操作。

文件操作這種更可行的方式就是對文件操作,然後讓git來識別變更,具體是:

從主分支上切出壹個跟主分支完全相同的分支F。從文件管理系統復制項目文件夾為bak,在bak內使用gitcheckoutN將代碼切到想要的歷史提交,這時候git會將bak內的文件恢復到N狀態。在從文件管理系統內,將bak文件夾下除了.git文件夾下的所有內容復制粘貼到原項目目錄下。git會純從文件級別識別到變更,然後更新工作區。在原項目目錄下執行add和commit,完成反提交。這種方式的巧妙之處在於利用git本身對文件的識別,不牽涉到對workflow操作。

小結

最後終於靠著文件操作方式成功完成了代碼回退,事後想來真是壹把心酸淚。

為了讓我的五個小時不白費,復盤壹下當時的場景,學習並總結壹下四種代碼回退的方式:

revert適合需要回退的歷史提交不多,且無合並沖突的情景。如果妳可以向master強推代碼,且想讓gitlog裏不再出現被回退代碼的痕跡,可以使用gitreset--hard+gitpush--force的方式。如果妳有些geek,追求用”正規而正統”的方式來回退代碼,rebase+revert滿足妳的需求。如果妳不在乎是否優雅,想用最簡單,最直接的方式,文件操作正合適。git真的是非常牛逼的代碼管理工具,入手簡單,三五個命令組合起來就足夠完成工作需求,又對geeker們非常友好,妳想要的騷操作它都支持,學無止境啊。

關註我,後續更多幹貨奉上!

  • 上一篇:月球車的月球車計劃
  • 下一篇:社團面試自我介紹範文
  • copyright 2024編程學習大全網