gitlab上程式碼回滾把自己坑了後, 陷入思考?"bug是誰"?
少年, 你點選過gitlab
下圖中的金燦燦按鈕麼? 本篇文章將告訴你如何正確使用這個按鈕。
一、 背景
原計劃xx號下午3點我開發的功能要上線, 下午2點將開發分支合併入master
分支準備跑上線流水線, 但是不巧這一天server同學遇到了點問題, 導致上線時間延期了, 與此同時前端還有其他分支今天要上線, 已經準備合併入master
分支, 當時我要做的就是快速將master
回滾。
我提交的"mr"
資訊裡面有"revert"
按鈕, 一看就知道這個按鈕負責回滾程式碼, 鬼使神差的就點選了這個按鈕, 當然啦程式碼被成功回滾並且也沒有影響後續同學的上線, 但故事並沒有這麼簡單。
當n天后我負責的程式碼計劃再次上線時, git merge branch
在masetr
上merge
我的分支竟然失效了, 我的程式碼中新增的部分無法合併入master
, 當時情況緊急眉頭都皺成了一個"川"字, 焦急獸進化(?)鋼鐵焦急獸。
二、 強推灰飛煙滅
當時現場求助了同組的其他同學, 將本地我的分支git merge master
, 此時我的分支就是最新的, 再去gitlab
上關閉master
的保護機制, 強行將分支內容推到master
上進行覆蓋, 再將master
的保護機制重新開啟。
這一套操作當然存在問題, 強推master
可還行? 並且這種操作需要相關人員的稽核, "費力+有風險"。
當一切塵埃落定了也該開始思考了, 那麼到底為什麼會出現程式碼無法合併入master
, 到底是哪一步出了問題, 當時我的gitlab
使用的語言是中文, 按鈕只顯示"還原"
兩個字, 我趕快將語言切換至英語, 此時按鈕的文案變成了"revert"
, 我就從嫌疑人"revert"
開始調查吧, 看看他與不在場的"reset"
有什麼關聯。
三、靜下來學習reset與revert的區別
revert
移除某個commit
記錄, 並且生成一條新的commit
記錄。
假設當前的分支狀態是下圖:
執行
git revert -n gt56th
git add .
git commit -m'feat: rm a'
reset
移動HEAD
到某個commit
上, 這個commit
之後的commit
全部捨棄, 並且你本地的程式碼是沒有變化的。
假設當前的分支狀態是下圖
執行git reset gt56th
四、解釋為何程式碼合不進去
通過對比我們就可以知道為什麼, 那天下午我無法把程式碼合併入master
分支的原因了。
由上面的步驟圖可知, revert
後的程式碼裡是明確記錄了刪除目標commit
也就是commit id
為gt56th
的這條資料, 那麼我們再將含有commit id
為gt56th
的提交merge
到master
分支時, git
的演算法會判斷出這個commit
已被移除, 所以git
會認為我分支的程式碼落後於master
, master
裡面移除了這條commit
的程式碼才是最新的。
git
這樣判斷是沒問題的, 我們多人開發的時候, 假設'a'與'b'一起開發一個專案, 'a'刪除了a.js
檔案並且merge
到master
, 第二天'b'改動了其他地方也merge
到master
, 此時就算b的程式碼上沒有移除a.js
, merge
後也不會在master
分支上增加a.js
檔案。
五、預設revert此事有蹊蹺
我們來聊聊既然gitlab
預設使用revert
功能來回退程式碼, 也就是說官方認為這種回滾方式是最棒的, 那麼它棒在哪裡?
第一: 連續性
就算是回退操作, 也算是對master
的正常操作, 但是直接reset
會導致時間線的缺失, 讓我們不知道中間發生了什麼, 長期來看這樣不利於解決問題。
第二: 中途有人拉程式碼
'a'的程式碼push
到了master
上, 2個小時後a將master
程式碼reset
掉, 看似一切正常, 但是殊不知'b'剛剛pull
了master
的程式碼到本地, 此時就埋下了隱患, 因為如果'b'進行master
的合併操作, 會將'a'之前刪掉的程式碼再次釋出到master
分支, 導致程式碼的錯誤上線。
第三: 方便回滾
比如說我們的gitlab
是預設merge
完畢就刪除源分支的, 此時我們可以直接拉取master
的最新程式碼, 因為可以在git log
裡面找到所有的commit
這樣就不怕玩壞了分支程式碼找不到了。
六、reset 裡的大學問
講了不少revert
的好處與壞處改說說reset
了:
假設我當前專案裡有 a.js
、b.js
、c.js
三個檔案, a.js
被git commit -m''
, b.js
被git add
, c.js
沒有被git監控:
git reset --hard CommitId (暴力刪除)
這個寫法直接將HEAD
回退到目標分支, 並且刪除所有當前分支之後編寫的程式碼, 也就是說會將你的程式碼清除哦。
這個方法適合完全捨棄某些程式碼的場景, 因為你在git log
命令裡面都無法查到被刪除的commit
記錄了。
git reset --soft CommitId (舒服的?)
這個寫法直接將HEAD
回退到目標分支, 並且保留你在目標commit
之後的修改, 這些修改都在暫存區
, 我們可以繼續開發相關的功能, 最後統一 git add. && git commit -m ''
一次即可。
這種方式讓我想到了我們可以平時在自己電腦上提交多個commit
, 但是push
之前我們可以先回退, 然後把commit
合一再提交。
git reset --mixed CommitId (預設的, 當我們不寫引數就是這個指令)
將目標提交之後的程式碼還原成未被監控的狀態, 也就是你的程式碼需要git add .
一下才可以進行管理, 這個招式相當於重置了提交態, 但是也有一些小小問題, 比如我們有一些沒有被git add
的檔案會與其他檔案不好分割開, 給我的感覺就是向我們當前的程式碼裡面塞入很多新的程式碼, 使用git status
檢視的話就全是紅色的。
七、流程梳理得出方案
已經瞭解了上述的知識點, 那麼可以推匯出一套比較可靠的回滾流程了, 當我們要將已經merge
到master
的程式碼暫時回滾, 並且後續還會上線這些程式碼時, 先點選gitlab
上的revert
按鈕, 再將自己本地的程式碼git reset 線上commit版本
這樣就可以將這些程式碼變成新的commit
, 這樣就可以再次申請merge
到master
也不會合並不上了。
八、問題的出現與思索
剛出現這個問題的時候, 習慣性的認為gitlab
出問題了, git
的某些演算法出問題了, 但是通過系統性的分析才明白, 出問題的是自己的操作。
將gitlab
平臺設定成了中文, 導致某些英文可以表達的含義無法表達, 這也是個問題點, 寫程式碼最好能更清楚的知道程式碼的本意, 而不是翻譯過後的意思。
end
這次就是這樣, 希望與你一起進步。