Git做爲目前程序界中使用最爲普遍的版本控制系統,熟練使用Git工具是「優秀代碼搬運工」的重要表現。 在多人協做的項目中,分支合併是常常須要處理的事務,而rebase
和merge
是合併的兩大工具。即使是工做甚久,我對rebase
命令也只是略懂皮毛,本文總結了rebase
命令的幾個常見用法。前端
咱們不生產
commit
,咱們只是commit
的搬運工git
git - rebase
來自官方的解釋是:Reapply commits on top of another base tip
。中文兩字就能歸納:變基。使用生活中的例子,就像接線頭同樣,將一段線條接在了另外一個線條後面,最後它們依然是一條線。shell
接下來,咱們將從不一樣應用場景去分析如何「接線頭(變基)」。vim
假如你在本地master
分支中已經開發了部分功能,產生了一些提交。與此同時,你的小夥伴也努力工做,並將他的勞動成果推送到了遠端分支origin/master
上。此時,你須要將遠端分支更新到本地,以後你才能夠繼續推送(push),分支狀況以下圖所示:markdown
一般,咱們的作法是執行git pull
命令,該命令會自動下載分支並完成合並。但git pull
命令默認採用的合併策略是merge
方法,即git pull
是以下兩條命令的縮寫:app
git fetch origin
git merge origin master
複製代碼
最終它會造成以下分支圖:工具
合併遠端分支,你將在本地得到一個嶄新的提交H
,主要內容是遠端分支全部提交內容與你本地分支提交內容的混合大雜燴。 顯然,你並不想每次拉取遠端內容時都會產生一條「醜陋」的旁路分支,久而久之,你的Git Graph
將變的和地鐵線路通常的錯綜複雜。因而,你僅想要「接線頭」,將本地開發分支全部提交直接鏈接在遠端分支的提交後面。rebase
出馬,一馬平川。oop
git pull --rebase // 等價於 git fetch origin git rebase origin master 複製代碼
運行上述命令後,咱們獲得以下分支圖:學習
直觀上看,rebase
會粗暴的將C --> F
的鏈接掐斷,直接鏈接在提交E
的後面,圖中提交F'
和G'
的內容與原提交F
和G
內容是一致的,但commit id
卻徹底不一樣。即「變基」操做會爲原提交分配新的commit id
,並將新提交鏈接在新的變基點上。測試
固然在rebase
過程當中並不是一路順風,rebase
會檢查新提交F'
是否與目前分支上的內容產生衝突,若是出現了衝突,git會暫停變基過程,等待你解決完全部衝突後再繼續:
// solve your conflicts, then git add . git rebase --continue // or git rebase --abort 複製代碼
雖然提交內容沒有變化,但對於Git而言,變基後的提交會被視爲徹底不一樣的提交。
陽光明媚的某一天,你在本地開發了一個邏輯複雜的功能,產生了多條commit
記錄,而commit message
是隨手填寫的,毫無心義:
* 50827 - fix // E * 312ed - a bug // D * bdc5c - done // C * 7a169 - hahaha // B * 8rf4q - Empty commit to fix CI on master // A 複製代碼
這個邏輯複雜的功能經本地測試後完美運行,是時候推送到遠端讓其餘小夥伴們膜拜你的代碼了。可是你的毫無心義的提交信息卻沒有體現出你到底作了些什麼。須要對這些「無聊」的提交修剪修剪了。
咱們的目標是將 B --> C --> D --> E
整合爲一個完整提交B'
,並指定新的commit message
:
運行以下命令:
git rebase -i [start] [end]
複製代碼
-i
會喚起交互式界面讓用戶編輯以完成變基操做,其中[start]
與[end]
分別對應了須要操做的commit id
區間(左開右閉),若是省略了[end]
,則該區間的終點默認是當前分支HEAD
所指向的commit
,在本例中分別爲8rf4q
與50827
。
git rebase -i 8rf4q // or git rebase -i HEAD~4 複製代碼
咱們進入到交互界面(即vim
環境):
在界面上方列出了須要編輯的全部提交,在每一個commit id
前的是指令類型(pick
),在Commands
中有相關的指令說明。
p, pick = 保留該commit
r, reword = 保留該commit,但修改它的提交信息
e, edit = 保留該commit,在合併該請求時暫停
s, squash = 保留該commit,合併到前一個提交中
f, fixup = 相似於squash,但拋棄提交它的提交信息
x, exec = 執行shell命令
d, drop = 丟棄該commit
在本例中,咱們須要保留提交B
,將提交C
D
E
均合併至上一個提交中便可:
完成編輯後保存退出(esc
+ shift :
+ wq
)便可。隨即,進入到修改提交信息的交互界面:
將不須要的message
註釋掉(添加#
),或者修改提交信息便可。咱們給此次變基記錄提供一個漂亮的提交信息:
通過以上步驟後,你就得到了一個整合多條commit
的新的commit
- B'
: This is a very nice commit message
。
合併多個commit
在開源社區提起Pull Request(PR)
時很是重要。在正式提交PR
前須要合併你的提交,整合爲一條或若干條有意義的提交,讓你的工做有跡可循,不要將編輯提交歷史的責任扔給倉庫維護者。
刪除commit
是rebase
中另外一個有趣的用法,考慮以下分支場景:
提交B
和C
是須要刪除的提交,能夠運行以下命令:
git rebase --onto dev~5 dev~3 dev // or git rebase --onto HEAD~5 HEAD~3 HEAD 複製代碼
HEAD
指向當前版本,即dev
分支的F
提交所在處;
HEAD^
指向上一版本,即E
提交所在處;
HEAD~3
是HEAD^^^
的簡寫,依次類推;
依據左開右閉
的規則,git rebase --onto HEAD~5 HEAD~3 HEAD
的語義是將D --> F
提交區間鏈接到提交A
以後,即變基
,以此達到刪除的目的。
變基過程可讓你以線性方式來管理你的提交,但rebase
命令會修改你的提交id,說白了,rebase
改變了歷史,改變了過去,「嚴謹的歷史學家」可不能容忍隨意的更改歷史呀,因此它很危險。
reabse
命令魅力無窮,使用得當它會使你的提交脈絡清晰明瞭,使用不當你會陷入反覆解決衝突的漩渦裏。爲避免錯誤使用rebase
命令,在這裏引出一個使用rebase
命令的「金科玉律」:
永遠不要去rebase本地以外的任何提交!
若是發現你操做的那些commit
已經被推送到遠端或者被其餘小夥伴合併到本地,請停下變基的操做。 只要遵照這條定律,使用rebase
命令修改私有分支的歷史記錄就不會出差錯。
你的小夥伴可不想你隨意篡改他的勞動成果哦!
碼字不易,若是:
您的支持與關注,是我持續創做的最大動力!