優秀 !原來華為是這樣使用 git rebase的

2021-02-19 架構擺渡人

原文:https://www.mdeditor.tw/pl/pMHD

使用git參與多人之間的合作開發大概有三年的時間,大多數場景下使用的git命令一隻手多一點就能數的過來

理論上來說,只要能合理管理項目分支,這幾個命令已經足以應付所有的日常開發工作。但是如果我們偶爾看一下自己的git graph,我的天吶,為什麼會這麼亂。

鑑於分支管理的混亂(或者根本就沒有進行過分支管理),我們經常遇到一些意想不到的問題,因此需要使用很多面生的git命令來解決我們的問題,比如說本文講到的git rebase。

git rebase 和 git merge 區別

Git rebase 的中文名是變基,就是改變一次提交記錄的base。在這一環節,我們不妨帶著這樣一個假設:git rebase ≈ git merge,並用兩種命令實現同一工作流來對比他們之間的異同。

回想我們日常的工作流,假設a和b兩人合作開發,三個分支:develop, develop_a, develop_b。兩個人分別在develop_a和develop_b分支上進行日常開發,階段性地合入到develop。

那麼從a的角度來看,可能的工作流是這樣的:

(1)個人在develop_a分支上開發自己的功能

(2)在這期間其他人可能不斷向develop合入新特性

(3)個人功能開發完畢後通過merge 的方式合入別人開發的功能

git merge上圖為日常merge 工作流,對應的git操作命令如下:
git checkout develop_a

// 本地功能開發...

git pull origin develop = git fetch origin develop + git merge develop複製代碼

git rebase

同樣走完這樣一個工作流如果我們使用git rebase來實現,結果如下:

git rebase 之前,如圖:git rebase 之中,如圖:git rebase 之後,如圖:git rebase 操作對應命令如下:

git checkout develop_a

// 本地功能開發...

git fetch origin develop

git rebase develop

git checkout develop

git merge develop_a

git br -d develop_a 複製代碼

由此可見,git rebase 和git merge的異同之處如下:

(1)兩者都可以用於本地代碼合併

(2)git merge 保留真實的用戶提交記錄,且在merge時會生成一個新的提交

(3)git rebase 會改寫歷史提交記錄,這裡的改寫不僅限於樹的結構,樹上的節點的commit id也會別改寫,因此圖3和圖4用e'代表圖2的e',收益是可以保證提交記錄非常清爽

如何使用git rebase -i 修改歷史提交記錄

git rebase -i,中文名叫交互式變基。意思就是在變基的過程中是可以摻入用戶交互的,通過交互過程我們可以主動改寫歷史提交記錄,包括修改、合併和刪除等。我們以上面使用rebase後得到的提交記錄為例,來進行歷史提交記錄的修改,在修改之前,提交記錄是這個樣子的。


使用git rebase -i 修改歷史提交的過程主要包含三步:

(1)列出一個提交記錄的範圍,並指出你在這個範圍內需要對哪些記錄進行什麼樣的修改

(2)以次執行上述的修改,如果遇到衝突需要解決

(3)完成rebase 操作

以上面截圖中的提交記錄為例,來對歷史提交的commit msg進行修改,操作步驟如下:

// 查看最近6次提交記錄,選擇對哪一條記錄進行修改

git rebase -i HEAD~6複製代碼


執行完上述命令後,會以vim的方式打開一個文件,文件中顯示了最近6次的提交信息,從上到下,由遠到近。

從下面的注釋可以看到,我們分別把每一行前面的pick修改成r, s, d的方式就可以實現對歷史記錄的修改,合併和刪除。

首先我們嘗試修改提交信息,把第二行前面的pick改成r,保存退出。當前頁面關閉的同時會打開一個新的頁面,讓你對選中的提交信息進行編輯。


編輯完信息之後保存退出,就完成了對歷史提交記錄的修改。通過觀察下圖可以發現,develop_a的提交記錄中的commit msg 仍然是feat_c,但是develop 分支中對應的提交記錄,commit msg 已經變成了feat: c-update.。

這裡需要留意到的一個現象是develop 和develop_a 分支上相同提交的commit id 已經發生了變化,這個在後面會再次提到。


除了修改提交的commit msg 之外,我們也可以通過把pick 改為e,結合git reset --soft HEAD^ 的方式對檔次提交的改動內容進行修改。

合併與刪除歷史提交的操作步驟與編輯類似,只需要把pick分別改為s 和d 即可,各位看官可以自行嘗試。如果在rebase的過程中遇到了衝突,需要手工解決,然後使用git rebase --continue 完成rebase 操作。

git rebase 的提示還是非常友好的,它會告訴你需要進行哪些操作解決當前的問題。


使用git rebase -i 必須遵循的規則是什麼?

從修改歷史提交記錄這個功能來看,交互式變基是一個非常強大的功能。但是使用這個功能必須要遵循一個鐵則:不要對線上分支的提交記錄進行變基!

引用git 官方指導文檔的話來說大概是這樣:

如果你遵循這條金科玉律,就不會出差錯。否則,人民群眾會仇恨你,你的朋友和家人也會嘲笑你,唾棄你。

在說為什麼不能對線上提交執行交互式變基之前,先說一下如果要對線上功能執行這個操作要怎麼做。

首先,你需要在自己本地變基成功,然後使用git push -f 強行push 並覆蓋遠程對應分支,之所以需要執行覆蓋式push 是因為如果你不覆蓋,當前變基過後產生的新提交會與遠程合併,導致你在本地的變基行為失去意義。

因為我們上面提到過,從變基那個節點開始往後的所有節點的commit id 都會發生變化。

同樣的原因,即使你使用git push -f 使遠程分支發生了變基,如果你的同事的開發分支中還存在你執行變基操作(不論是修改、合併還是刪除)時針對的那些分支,那麼當你的同事merge 你的提交之後,你所有想使用變基改變的東西都回來了!

如果打破了git rebase -i 的使用規則應該如何補救

此處我們嘗試通過要點描述的方式,說明線上提交執行變基會導致什麼結果以及如何避免這個結果:

(1)你在本地對部分線上提交進行了變基,這部分提交我們稱之為a,a在變基之後commit id 發生了變化

(2)你在本地改變的這些提交有可能存在於你的同事的開發分支中,我們稱之為b,他們與a的內容相同,commit id 不同

(3)如果你把變基結果強行push 到遠程倉庫後,你的同事在本地執行git pull 的時候會導致a 和b 發生融合,且都出現在了歷史提交中,導致你的變基行為無效

(4)我們想要的是你的同事拉取線上代碼時跳過對a 和b 的合併,只是把他本地分支上新增的修改合併進來

講了這麼多,最終的結論就是,使用變基解決變基帶來的問題。即你的同事使用git rebase 的方式把他本地的修改rebase 到遠程你執行過rebase 的分支上。

簡言之,就是你的同事使用git pull --rebase 而不是git pull 來拉取遠程分支。在這個操作的過程中,git 會對我們上面提到幾個要點的信息進行檢查並把真正屬於同事本地的修改合入遠程分支的最後。

文字描述可能有些乏力,更多詳細信息可以參考這裡:git-scm.com/book/zh/v2/…

所以我們應該如何使用git rebase

鑑於上面描述的git rebase 可能帶來的問題,最後要回答的一個問題是我們應該如何在日常工作中使用git rebase,同樣借用git 官方文檔中的一句話:

總的原則是,只對尚未推送或分享給別人的本地修改執行變基操作清理歷史, 從不對已推送至別處的提交執行變基操作,這樣,你才能享受到兩種方式(rebase 和merge)帶來的便利。

架構擺渡人,助你通往架構師方向的領路人。本號會定期分享架構相關的文章,專注於架構方向,關注我們,下一個架構師就是你。

相關焦點

  • 深入git rebase使用
    但是很多時候我們更需要的是rebase,我們也可以通過設置,設置rebase為pull時候默認執行的動作:git config --global pull.rebase true使用git rebase改變分支依賴當然git最初始的功能是和其字面上意義一樣,改變分支的依賴分支。
  • 【譯文】Git merge 和 Git rebase比較
    但如果使用得當的話,它能給你的團隊開發省去太多煩惱。在這篇文章中,我們會比較git rebase和類似的git merge命令,找到Git工作流中rebase的所有用法。概述   你要知道的第一件事是,git rebase 和git merge 做的事其實是一樣的。它們都被設計來將一個分支的更改併入另一個分支,只不過他們實現方式有些不同。
  • Git: 聊聊Rebase命令
    這樣就起到了「變基」的作用,這種改動是從commit的最後一個公共部分開始的。所以叫「變基」。不過需要重點搞清的兩點:merge和rebase合併後的結果是一模一樣的,形象的說,二者是殊途同歸。使用rebase「變基」後的commit與之前沒有變基前的commit,即上圖中C6』與C6是不同的,它們的SHA-1值不同,Git會把它們看成兩次提交。
  • Git命令解析 - merge、rebase
    如下圖:branch命令可以輕鬆創建一個新分支,就像這樣:$ git branch new_branch這一命令實際是為當前提交對象添加了一個新的指針。這種分支形式比大多數版本控制系統更為輕量,無論是創建還是切換都幾乎可以在瞬間完成。
  • 我到底應該用git-merge還是git-rebase呢?
    本文一共3165字,將通俗的解釋git中兩種合併策略——merge和rebase的不同、運用方式和使用的場景,並輔以我在工作中的運用來介紹;什麼是git merge?git merge是我們在git操作中頻繁會用到的一個命令,它主要實現的功能便是為我們進行分支代碼的合併,也就是將兩個或兩個以上的開發歷史合併在一起的操作。
  • 從零開始寫git:理解rebase與merge
    rebase(變基)git-rebase - Reapply commits on top of another base tip——git官方文檔什麼是rebasegit的初學者可能對rebase了解不深,它的使用頻率沒有pull、add、commit、push那麼高,但了解rebase的使用還是有必要的,在一些場景下很可能會用到rebase。
  • [笨叔點滴5] git rebase和git merge究竟有啥區別?
    大家可能都知道git這個生物吧,他是由Linux祖師爺創造出來的,短短十來年時間已經霸佔了代碼版本管理軟體的頭把交椅,連不可一世的微軟也在偷偷摸摸的開始偷偷使用git了,你說git的發展速度是不是和細胞繁殖一樣。大家知道git天生就喜歡分支,那麼分支管理裡有一個重要的方面就是分支合併了。
  • 【Git】616- git命令的進階和複習(帶動圖效果)
    小姐姐用動畫圖解Git個人比較推薦第二個Git學習網站猴子都能懂的git,圖文結合,簡單明了,本文將介紹一些常用 Git 指令,作為一個學習總結git rebasegit mergegit resetgit revertgit cherry-pickgit fetchgit
  • 這才是真正的Git——Git實用技巧
    在這篇文章裡我會使用操作錄屏的方式來介紹例子,希望這種方式可以讓你更直觀的了解命令的使用方法。將幾個commit壓縮成一個rebase -i 是個很實用且應用廣泛的工具,希望大家都學會它的使用。它還可以用來修改commit信息,拋棄某些commit,對commit進行排序等等。具體命令如下,操作方式跟動圖一致,都是在vim裡面進行編輯。這裡不展開,感興趣的同學可以自己操作一下。
  • 大牛總結的 Git 使用技巧,寫得太好了!
    安裝後,還要在命令行輸入$git config --global user.name "你的名字"$git config --global user.email "你的郵箱"global表示全局,這臺機器所有的Git倉庫都會使用這個配置。允許單個倉庫使用其他的名字和郵箱。
  • git分支概念和分支相關操作
    分支是git中最容易被誤解的概念之一,雖然git分支並不難理解。使用分支時候是不有點不知所措,"老虎吃天,無處下爪"的感覺?還有那一系列的merge和rebase黑魔法操作,甚至是那些許的衝突都曾讓你頭痛不已?
  • git命令的進階和複習(帶動圖效果)
    小姐姐用動畫圖解Git個人比較推薦第二個Git學習網站猴子都能懂的git,圖文結合,簡單明了,本文將介紹一些常用 Git 指令,作為一個學習總結git rebasegit mergegit resetgit revertgit cherry-pickgit fetchgit
  • 詳解Git圖形界面的使用
    gitk 和 git-gui 在安裝 Git 的同時,你也裝好了它提供的可視化工具,gitk 和 git-gui。 gitk 是一個歷史記錄的圖形化查看器,你可以把它當作是基於 git log 和 git grep 命令的一個強大的圖形操作界面,當你需要查找過去發生的某次記錄,或是可視化查看項目歷史的時候,你將會用到這個工具。
  • 帶你深入理解 Git 原理
    只需使用add 將改動添加到暫存區,然後執行 git rebase --continue,衝突將在剛剛的提交中解決。在合併時,當你執行 git rebase --abort之前,你始終可以停止並放棄到目前為止所做的一切。
  • 面試題:闡述一下GitFlow(git工作流)
    包含如下幾個關鍵分支:名稱說明master主分支develop主開發分支,包含確定即將開發的代碼feature新功能分支,一般一個新功能對應一個分支,對於功能的拆分需要比較合理,以避免後面一些不必要的代碼衝突release發布分支,發布時候用的分支,一般測試時候發現的BUG在這個分支進行修復hotfixhotfix分支,緊急修復BUG的時候使用的分支
  • 幾個常用的 Git 高級命令
    高級命令.htmlGit 是一款開源優秀的版本管理工具,它最初由 Linus Torvalds 等人開發,用於管理 Linux Kernel 的版本研發。相關的書籍和教程網上琳琅滿目,它們多數都詳細的介紹其基本的使用和命令。本人根據自己的經驗,整理出幾個較為高級而常用的命令。推薦資料 Git Book。https://git-scm.com/book/en/v2Git blameGit blame 可以查詢每一行代碼的 commit ID、提交者和提交日期。
  • 12個git實戰建議和技巧
    1.使用「git diff」來摺疊多行用git diff經常會出現很多內容,導致很多內容被遮住了,讓人很是苦惱,幸運的是這裡有個解決方案。如果你使用less作為默認的pager,只需要輸入-s,就可以保證不會被diff刷屏了。
  • Git入門實戰教程
    Git 的內容完整性要優於 SVN:Git 的內容存儲使用的是 SHA-1 哈希算法。這能確保代碼內容的完整性,確保在遇到磁碟故障和網絡問題時降低對版本庫的破壞。 git 初始化任意選擇一個文件夾,本文選擇新建一個文件夾。使用git init創建一個空的Git倉庫或重新初始化一個已存在的倉庫。
  • 你們仍未掌握那天所學的 git 知識
    要做到這些,離不開掌握git rebasegit rebaseReapply commits from one branch on top of another branch.merge信息(commit message),所以我是推薦在自己的分支開發時,永遠使用 fetch,rebase(不會出現多餘信息,處理衝突更加自由)Author: YongHao Hu <hyh@vincross.com>Date: Fri Dec 23 17:55:49 2016 +0800 install skill: Fix
  • 10個非常有用的Git Tips
    在過去的幾年中,git已經大大成熟,成為最常用的版本控制系統之一。它被各種語言和各種規模的團隊開發人員使用,從小型開源項目到像Linux內核這樣的龐大代碼庫。在本文中,我們將與您分享一些可以改善您的git體驗和工作流程的技巧。