溫故知新之git rebase的用法

Git做爲目前程序界中使用最爲普遍的版本控制系統,熟練使用Git工具是「優秀代碼搬運工」的重要表現。 在多人協做的項目中,分支合併是常常須要處理的事務,而rebasemerge是合併的兩大工具。即使是工做甚久,我對rebase命令也只是略懂皮毛,本文總結了rebase命令的幾個常見用法。前端

咱們不生產commit,咱們只是commit的搬運工git

Rebase

git - rebase來自官方的解釋是:Reapply commits on top of another base tip。中文兩字就能歸納:變基。使用生活中的例子,就像接線頭同樣,將一段線條接在了另外一個線條後面,最後它們依然是一條線。shell

接下來,咱們將從不一樣應用場景去分析如何「接線頭(變基)」。vim

1. 更新本地分支

假如你在本地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'的內容與原提交FG內容是一致的,但commit id卻徹底不一樣。即「變基」操做會爲原提交分配新的commit id,並將新提交鏈接在新的變基點上。測試

固然在rebase過程當中並不是一路順風,rebase會檢查新提交F'是否與目前分支上的內容產生衝突,若是出現了衝突,git會暫停變基過程,等待你解決完全部衝突後再繼續:

// solve your conflicts, then
git add .
git rebase --continue
// or
git rebase --abort
複製代碼

雖然提交內容沒有變化,但對於Git而言,變基後的提交會被視爲徹底不一樣的提交。

2. 合併commit

陽光明媚的某一天,你在本地開發了一個邏輯複雜的功能,產生了多條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,在本例中分別爲8rf4q50827

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前須要合併你的提交,整合爲一條或若干條有意義的提交,讓你的工做有跡可循,不要將編輯提交歷史的責任扔給倉庫維護者。

3. 刪除commit

刪除commitrebase中另外一個有趣的用法,考慮以下分支場景:

提交BC是須要刪除的提交,能夠運行以下命令:

git rebase --onto dev~5 dev~3 dev 
// or
git rebase --onto HEAD~5 HEAD~3 HEAD 
複製代碼

HEAD指向當前版本,即dev分支的F提交所在處;
HEAD^指向上一版本,即E提交所在處;
HEAD~3HEAD^^^的簡寫,依次類推;

依據左開右閉的規則,git rebase --onto HEAD~5 HEAD~3 HEAD的語義是將D --> F提交區間鏈接到提交A以後,即變基,以此達到刪除的目的。

rebase命令的風險

變基過程可讓你以線性方式來管理你的提交,但rebase命令會修改你的提交id,說白了,rebase改變了歷史,改變了過去,「嚴謹的歷史學家」可不能容忍隨意的更改歷史呀,因此它很危險。

reabse命令魅力無窮,使用得當它會使你的提交脈絡清晰明瞭,使用不當你會陷入反覆解決衝突的漩渦裏。爲避免錯誤使用rebase命令,在這裏引出一個使用rebase命令的「金科玉律」:

永遠不要去rebase本地以外的任何提交

若是發現你操做的那些commit已經被推送到遠端或者被其餘小夥伴合併到本地,請停下變基的操做。 只要遵照這條定律,使用rebase命令修改私有分支的歷史記錄就不會出差錯。

你的小夥伴可不想你隨意篡改他的勞動成果哦!

參考

最後

碼字不易,若是:

  • 這篇文章對你有用,請不要吝嗇你的小手爲我點贊;
  • 有不懂或者不正確的地方,請評論,我會積極回覆或勘誤;
  • 指望與我一同持續學習前端技術知識,請關注我吧;
  • 轉載請註明出處;

您的支持與關注,是我持續創做的最大動力!

相關文章
相關標籤/搜索