記一次Git分支合併引發的問題和修復

背景

Git工做流

在咱們的一個項目中,有3-4我的在同時開發,目前採用的是AoneFlow的變體進行分支管理,簡單來講,分爲四種類型的分支:主幹分支(master)、特性分支(feature)、發佈分支(release)、開發分支(develop/test)。git

每當新功能需求過來後,會從master切出feature分支進行開發,開發完成後合併到test分支中進行測試。多個功能需求測試完成後,將對應的feature分支合併到release分支,再合併到master分支進行發佈。如圖: shell

這種開發模式下的特色:

  1. test分支是一個「大雜燴」,全部開發完成(包括未測試完成)的feature分支都會合併到test分支。
  2. master分支根據多個feature測試結果來進行選擇性合併到release,做爲當前版本最終須要發佈的分支,合併到master進行上線發佈。

問題

在最近一次項目代碼合併的時候發現了兩個問題:編輯器

  1. 一些新開發中的feature分支沒法被合併到master分支,提示已經up-to-date。
  2. 把master分支往開發分支develop合併時發現部分代碼被刪除了。

回顧

錯誤的合併

在發現問題後, 通過一個小時的排查,終於發如今某次提交過程當中,咱們的一位開發不當心把test分支合併到了本身的featureA分支上。測試

這意味着,許多開發中的功能被合併到了featureA上。爲了解決這個問題,這位同窗經過對比,手動刪除了多餘的代碼,保持了featureA分支只有本身開發的那部分代碼,也通過測試經過了。spa

因而分支變成了這個樣子:code

發佈上線

在上線代碼合併到master的時候,我檢查merge request進行例行的code-review,發現確實featureA 功能的提交,因而將featureA 合併到了master準備上線,上線後也沒有任何問題。後續的功能也一個一個都提交上去了。cdn

發現問題

幾天後,一個同窗像我反映,在一個新功能的開發中,基於master切出了featureD分支,開發完成後合併到test分支進行測試的時候,出現了不少不是本身開發的代碼,我當時就很納悶,這種模式下test分支永遠是領先於master分支的,基於master的分支開發完成後提交到test應該只會有包含本身開發功能的那部分代碼纔對呀,最可能是產生一些衝突,不會有太多的文件變動。blog

最初懷疑是否是拉錯了分支,一看commit記錄,發現了問題的根本緣由:開發

在上次featureA 合併到master的時候,雖然沒有多餘的文件變動,可是featureA合併過test代碼,即featureA包含了添加featureB/C, 刪除featureB/C的兩次commit記錄工作流

因此在後續基於Master拉出來的feature分支都會帶上這個錯誤的commit記錄,致使:

  1. test分支上只有添加featureB的代碼,基於master的新feature分支卻帶上了刪除featureB的commit且領先於test,合併到test的時候會刪除掉featureB的代碼。
  2. featureB分支再也沒法合併到master分支,由於master上最新的commit已經刪除了featureB的代碼。

如何解決

方案一:在featureA 分支上revert 撤銷那次合併test的Commit

git checkout featureA
git revert commit_id
複製代碼

提示失敗了

緣由是這個commit是一個merge commit,此次commit包含兩個父節點,須要指定你要revert哪個,即若是A,B合併到C,須要告訴git你要revert的是A提交仍是B提交。 這裏使用 —m參數就能夠了,1和2表明你要revert哪個父提交,大多數狀況是1,即撤銷「剛纔merge進來的那一個提交」

git revert commit_id -m 1
複製代碼

可是這裏有個須要注意的地方,若是revert掉某次修改後,若是須要再次merge該分支, 須要再作一次反向revert。 即Revert 掉以前的Revert,不然該分支以前的修改沒法被提交,由於已是「落後」於主幹分支了。

方案二:在featureA 分支上reset掉那一次提交記錄

reset和revert不一樣的是:

  • revert是根據某次的變動記錄,生成一個新的commit,反向刪除/添加對應以前某一次commit的代碼。
  • reset是「消除」本身的commit記錄從新提交,這種狀況下若是reset提交到遠程的分支,須要push -f強制更新,所以強烈不建議在多人協做的分支上這麼作

這裏要注意的是,若是在出錯的commit版本後還有若干次正確的提交,reset後也會一併消失,須要從新加回來,否則後續正確的代碼也會丟失,按照以下操做進行cherry-pick:

git reset --hard commit_id #退回到出錯前的一個commit

git cherry-pick commit_id_right #將出錯後的commit提交從新加回當前的時間軸

git push -f #強制更新
複製代碼

方案三: 在featureA分支上進行rebase操做丟棄錯誤的提交

rebase即變基 使用git rebase -i 命令能夠壓縮合並屢次提交

格式:git rebase -i [startpoint] [endpoint]

其中-i的意思是interactive,即彈出交互式的界面讓用戶編輯完成合並操做,[startpoint] [endpoint]則指定了一個編輯區間,若是不指定[endpoint],則該區間的終點默認是當前分支HEAD所指向的commit(注:該區間指定的是一個前開後閉的區間)。

操做方式爲:

  1. 首先根據git log,找到出錯前的一個commit

  2. 執行git rebase -i commit_id 合併以前的提交記錄

  3. 會彈出一個vi編輯器以下:

    將出錯的那兩次提交,pick改成drop,即丟棄。

  4. Esc :wq保存

  5. 若是出現衝突

    須要手動解決衝突後,執行

    git add xxx
    git rebase --continue
    複製代碼

總結:

  • 在多人合做中,儘可能不要把test分支往feature分支上合併
  • 多人協做的分支上出現已經提交到遠程的錯誤的合併時,優先使用revert來進行錯誤回退,並注意後續分支再次合併是否有問題。
  • resetrebase儘可能在私人分支或者未提交到遠程的時候使用,若是過去的分支已經提交到遠程,會對其餘人的本地分支形成影響。
相關文章
相關標籤/搜索