前一篇 Day 24 聊到透過幾個解法,把目前所在的分支移動到與自己相同基點的其他分支上。今天的這個題目是在 2019 年 ModernWeb 聽龍哥(高見龍)的演講時聽到的,我覺得很生活化,且在實務上常常會遇到。
在許多開發流程中,都會建議,當有新的需求要修改原始碼時,需要從主分支建立 feature 分支,而後透過 MR 或 PR 機制回到主分支上。而一般情境下,開始實作新的需求之前,會先在目前的主分支上直接思考可能可以怎麼做,而後做一些簡單的測試或調整,也常常在這時間點,忘記還沒開啟分支,就直接修改原始碼,並且順手 commit 目前修改的內容,等到 commit 完才發現還沒建立 feature 分支。也或者是已經先透過 git branch feature_xxx
建立了分支,但卻忘記要 git checkout feature_xxx
直接在主要分支上實作了?
如上圖,feature 分支還在比較前面的原點(origin/master) commit 上,而原定的主要分支 master 已經超前兩個 commit 了。調整的目標是讓畫面上 master 與 feature 兩個分支的位置互換,且 master
對應 upstream origin/master
的這個設定不變。(成果概略如下圖)(什麼是 upstream 請見 Day 18)
在這時候可以怎麼處理呢?
解法基本上分為兩類,首先要確認的點,目前已經建立好的 Commit 對應的 HASH Code 是否可以變動?首先是不變動 HASH Code 的解法。
在 Day 08 的時候提過對於分支的概念,可以想像「它只是一個可以移動的貼紙,當分支上有新的 Commit 貼紙就貼到新的這個 Commit 上」,基於這個概念,我們可以怎麼做?對已經存在的 master 及 feature 分支修改名稱。怎麼實作呢?
步驟一:
git branch -m master tmp
git branch -m feature master
git branch -m tmp feature
步驟二:
git branch --unset-upstream feature
git branch --set-upstream-to origin/master master
指令 git branch
的參數 -m
主要的功能是修改分支的名稱,所以步驟一的三行指令在做的事情就是使 feature 與 master 兩分支的名稱互換。
但由於 GIT 系統預設的關係,會發現這時候 feature 還保有著原本 upstream 為 origin/master
的設定,這時候需要把 feature 的 upstream 移除,並且為新的 master 設定回 upstream 的設定,這部分也就是步驟二。
如果覺得步驟二這樣設定很麻煩,有一個比較快的方法,在Day 18 有提到,本地分支與遠端的分支怎麼對應,都在 .git/config 這個檔案裡頭可以找到,因此,也只需要把目前設定為 feature 的字眼,改為 master 就可以完成步驟二需要完成的工作了。
上面這個方法似乎有點繁瑣,有沒有再簡單一點的?有,結合 Day 24 的方法,透過 rebase/merge 在同一個分支上有 fast-forward (ff)的機制(什麼是 ff 請見,Day 13),先把 feature 分支變更到目前 master 的位置,而後 switch 到 master,接著 reset master 回 origin/master 的位置。
git checkout feature
git rebase master
git checkout master
git reset origin/master --hard
步驟一,第一行指令是切換到 feature 分支上,第二行指令是以 feature 針對不小心超前的 master 分支作 rebase(也可以用 merge),執行完畢後可以看到 feature 及 master 都指向同一個位置。
步驟二,回到 master 分支,第四行,將 master 分支退回到原始位置,為什麼使用 --hard
呢?因為很明確的,不小心在 master 建立的 commit 都已經因為 feature 分支 rebase master 已經在 feature 分支上可以找到了,因此 master 可以直接回復到最原始的狀態,所有新增個過程都完全不留。(什麼是 git reset --hard 請見 Day 09)
完整的執行過程如下:
如果在可以允許變動 HASH Code,使用 git cherry-pick
指令,把目前在 master 分支上的 commit 帶回來 feature 分支,會是相對直覺的方法。步驟如下:
git checkout feature
git cherry-pick f17fbe9
git cherry-pick c59f7d7
git checkout master
git reset origin/master --hard
步驟一的部分,是把目前 master 多出的分支步驟取回到 feature 分支來。而兩個 git cherry-pick
的指令,也可以合併為(且新指令不管 master 多了幾個步驟,都可以一律使用 ..master 取代):
git cherry-pick ..master
步驟二的部分則是如解法二的步驟二一樣,回到 master 分支重置 master 的位置。
當然,看過Day 24的解法三、解法四,就會知道,其實可以透過 --ff
參數,就變成跟解法二的方法一致了,對應的解法修改如下:
git checkout feature
git cherry-pick --ff ..master
git checkout master
git reset origin/master --hard
取代後的完整執行過程如下:
今天的解法我個人後期比較常用的反而是解法二,解法一的流程要修改的東西太多,相對容易發生失誤。一樣的,可能還有其他的解法,如果我有想到會再更新,而如果邦友們有想到不一樣的解法,也歡迎再次的跟我分享。