這些Git事故災難, 你經歷過幾個?

前夕Sama發表於2024-04-17

前言

關於Git, 相信大家最常用的就是pull和push. 但隨著協作規模的提升, 遇到的問題也會越來越多. 本篇文章並不科普一些命令的詳細用法, 更多的是分享在工作中遇到的Git場景問題以及踩過的坑

難辦? 那就別辦咯

先來個開胃小菜. 一般公司都會有個dev分支用來部署測試版的功能. 也因為是非正式環境, 所以比較隨意, 更新的也很頻繁. 每次我們使用dev分支之前都需要保證本地的dev分支是最新程式碼.

image-20240417210647271

可怕, 我非常清楚pull一下一定都是衝突.

image-20240417210801641

老實人可能還想著如何一個個去解決衝突.

難辦那就別辦了什麼梗圖

但是為什麼要解決呢? 我們又沒在dev分支上開發, 我們不過是想獲得最新版的分支內容, 所以我們完全可以直接把dev刪了, 然後再切個dev就好了. 當一個分支本地不存在的時候, 他就會從遠端切.

image-20240417211148894

Revert的陷阱

業務背景

相信大家對這個命令並不陌生. 當你想要回退某一條commit的時候, 可以這樣做.

git revert xxx

現在我們來看一個場景問題, 今天是你負責合程式碼. 你的同事告訴你, feat/talk分支的任務需要上線, 於是你熟練地敲下合併命令

image-20240417195806346

剛剛才執行完, 你的同事就跑來和你說弄錯了, 這個分支不應該被合併的. 此時你捏緊了拳頭, 非常想揍他. 但是你忍了. 你想到revert可以解決這個問題. 於是你找到了合併的commit id化解危機.

image-20240417195615168

輕輕鬆鬆~ 此刻的你猶如殺敵的將軍般英姿颯爽. 而你的同事又跑來和你說, 不好意思又搞錯了. 這個任務確實是要上的. 此刻你非常後悔, 後悔剛剛為什麼沒揍他. 但是這可怎麼辦呢? 於是你就故技重施.

image-20240417200758022

非常神奇的是, 這次的merge, 卻提示已經是最新程式碼了. 於是你檢視檔案變更、log日誌, 都和剛剛revert完的一模一樣. 臥槽, 鬼打牆了?!

解決方案

問題其實就出在merge這個命令, 你理解錯了. merge, 對比的是2個分支的commit. 什麼意思呢. 我們分別看看2個分支的commit有什麼不一樣

master:

image-20240417201604495

feat/talk:

image-20240417201623043

那麼當你想在master分支merge feat/talk的時候, 是不是feat/talk有的commit在master都已經有了? 雖然你revert了, 但是也並沒有影響他該有的commit還在啊. 所以你merge的時候他認為沒有新的commit產生, 也沒毛病.

cherry pick

可以單獨cherry pick相應的commit id. 因為cherry pick是針對單個提交的操作, 它不會關心或受到該提交是否已被撤銷的影響, 它只關注將指定提交的更改重新引入到當前分支.

image-20240417202329267

revert你的revert

這句話聽起來有點繞, 但是仔細想想應該能理解, 簡單說就是負負得正.

image-20240417203825193

衝突多到爆炸

如果你的團隊協作規模在10個人以上, 則很有可能出現非常複雜的衝突. 根據大家的發版週期、任務持續週期等多個因素影響, 會有各種不同型別的衝突出現. 今天我的粉絲群就有個同學問了這樣的一個問題

"遠端master是v19的版本, 而本地的master是v18的版本, 在一個月前, 從v18的master切出來任務分支開發功能, 我一共提交了50次程式碼, 現在要同步下master的程式碼, 發現衝突有300多個檔案. 我要崩潰了!"

莫慌! 解決300個檔案的衝突確實不太現實. 首先我們把他這個分支情況畫出來

image-20240417205531273

大概就是上面這個樣子, 遠端和本地的1號和2號是共同的祖先commit. 但是後面就自立山頭了. 圖中只畫了4個不同的節點, 按這位同學的說法, 他提交了50個節點, 而遠端不確定有多少個, 反正衝突是300多個檔案.

首先, 這50個commit我們可以使用squash壓縮成一個commit.

squash的作用就是將多個commit合併成一個. 用法不解釋了, 有點小麻煩, 百度都有.

那此刻, 我們的圖就變成了下面這樣

image-20240417205947122

先不說你的新程式碼改動. 單單說你本地的v18master和origin的v19master絕對都是有衝突的. 參考本文的開頭案例, 不需要去合併, 只需要把v18刪了就行了. 那麼你的本地master也就保持了最新. 最後, 直接cherry 5就可以了.

這裡再提另一個思路, 其實這個場景最核心的就是將50個commit轉化為1個commit. 除了squash還有個辦法, 就是reset. 可以reset到第一次提交的內容, 再次commit, 也可以實現這個效果.

stash的陽奉陰違

我們經常會在做完一些改動後, 可能由於各種原因, 需要暫存下程式碼. 這裡就會用到stash. 你可能以為你的程式碼修改都會被儲存下來. 但並不是這樣的, stash只會儲存modify的檔案, 不會儲存untracked檔案. 說人話就是新建的檔案是不會被暫存的. 而實際上, 我們幾乎不可能出現只想儲存檔案變更, 不想儲存新建的檔案. 所以一定要加上引數--include-untracked才可以.

另外, 取出stash的內容, 很多人喜歡用stash pop. 我更推薦你用stash apply. 區別是pop在取出stash的內容後會直接刪除掉暫存的stash. 這時候一旦你的更改又出現問題, 你就回不去了. 而apply則隨時可以放棄所有變更從頭再來.

核彈洗地

及時提交commit, 避免了程式碼丟失, 也保證了程式碼片段的獨立性. 我們有時候會在編輯程式碼後發現改了不少東西, 但是這些都是不需要了的, 想要全部放棄掉. 比較常見的情況是給同事寫個demo臨時演示下, 搞完就不需要了. 可以使用以下命令.

git checkout . && git clean -fd

前半部分會恢復你的程式碼變更, 後半部分則會刪除新建的檔案. 但是這麼長的命令誰記得住啊. 所以我建議使用git的別名設定

別名的配置用法網上很多, 搜尋 git alias

ddd = !git checkout . && git clean -fd

為什麼我設定的是3個d呢? 這裡想和大家說個趣事兒. 幾年前我的名別設定如下

ad = add .
d = !git checkout . && git clean -fd

因為discard這個單詞, 所以我把這個命令設定了為單字母d. 和往常一樣, 寫完程式碼我的肌肉記憶就敲出瞭如下操作

git ad
git commit

但是當時可能鍵盤的a鍵有點不靈敏, 就敲出了git d並且回車了. 當場心態崩了. 大家可能說現在的vsc就算撤銷了程式碼, cmd+z還是會回來部分程式碼的. 但是幾年前並沒有. 而且當時我在開發的任務非常複雜, 我寫了一整天, 就這樣沒了. 最後找回來了嗎? 沒有, 我重新寫了一遍. 之後我就改為了3個d, 確保絕對不可能誤觸.

如果是現在發生這種情況, 也許reflog還能救一下, 也只是也許, 因為防範於未然一定優於亡羊補牢.

總結

以上都是筆者在多年的實際工作中出了事故踩了坑而得出的總結, 不知道你是否也有類似的git事故經驗呢? 歡迎在評論區分享.


我是前夕, 專注於前端和成長. 公眾號: 前夕小課堂

image-20240403101717261

轉載需註明出處且不得刪減內容

相關文章