今天的狀況題是這樣的,某一個開發者,因為手上管控的分支太多,一個不小心把還沒合併到主分支且也沒有更新到遠端的分支刪除了,應該要怎麼救回這個分支?
情境概略可以這樣模擬,剛開始 feature 還有兩個 commit,最後一個 commit 的 commit message 為「add feature 2」。
但不小心刪除 feature 分支之後,只剩下 master 的兩個 commit。
一般的情境下,刪除尚未合併的分支,無論是 SourceTree 或 CLI 底下,都會提示警告訊息,如 SourceTree 會提示,並且詢問是否「Force delete」的選項:
在 CLI 底下如透過一般的刪除指令 git branch -d feature
則是提醒「error: The branch ‘feature’ is not fully merged.」如果真的要刪除,則必須使用 git branch -D feature
才能夠刪除。
那這個參數 -D
是什麼意思呢?根據 git help branch
查詢所得到的內容,-D
參數代表著原本的 --delete --force
也就是強制刪除。
一般實務上,會預設使用 -d
參數作為刪除分支用,也是為了避免如今天所要談的案例發生,所以,如果有習慣性使用 -D
參數刪除分支的邦友,建議將習慣改回使用 -d
參數,避免一些麻煩。但如果還是發生了呢?
一般來說,在刪除分支的時候,訊息尾端都會提示剛刪除的分支所在的 HASH Code 是什麼,如下圖,顯示刪除前 feature 分支,在 0f3eb49
這個位置。找到這個 HASH Code 很重要。
還記得 Day 08 的時候,提到 GIT 的 branch 分支是什麼概念嗎?在 GIT 的版本控制系統裡,branch 只是一張可以移動的標籤貼紙,當該 branch 有新的 commit 產生時,自動移動到新的 commit 所對應的 HASH Code。所以,分支被刪除了,其實,也只是這張標籤貼紙,被撕掉了,背後的 commit 及 HASH Code 都還在。
因此,這時候,我們只要針對目標的 HASH Code 重新貼上標籤貼紙即可。在上一個步驟中,我們記下了 0f3eb49
這個 HASH Code,是被刪除的 feature 最後的所在位置,因此,我們只要執行指令:
git checkout -b feature 0f3eb49
就可以救回 feature 這個分支,重新把 feature 這個可移動的標籤貼紙重新貼回。上面的指令是什麼意思呢?git checkout -b feature
代表的是,建立 feature 這個分支,並且切換 HEAD 到 feature 分支。如果在 feature 後面再加上 HASH Code,則代表在該 HASH Code 建立分支,並且切換過去。
如果不要切換過去呢?那可以執行:
git branch feature 0f3eb49
與 git checkout -b feature 0f3eb49
建立分支的效果一樣,但不會把目前的 HEAD 切換到新建立的分支上。
可以透過指令 git reflog
查詢過去關於分支切換、建立、commit 新增等等的資訊紀錄,如下圖,是這篇文章製作的範例所留下來的紀錄:
在 git reflog
的指令操作上,可以透過鍵盤的上下鍵或 jk 兩鍵,往上或往下移動選擇,也可以透過點選 /
斜線後,輸入關鍵字作搜尋,透過搜尋找到最後的 feature 分支上的 commit 對應的 HASH Code,搜尋結束之後,輸入 q 鍵即可離開。
搜尋的這時候,大家就會開始覺得,平常把 commit message 寫得好很重要,讓標題可以搜尋的到,就顯得非常重要。
步驟二:
其實找到對應的 HASH Code 之後,方法可以跟解法一一樣,直接透過 git checkout -b feature
或 git branch feature
後面接上找到的 HASH Code就好。但這邊其實也可以透過 Day 24 的狀況題 01 一樣的方法,可以透過 git rebase
、git merge
、git cherry-pick --ff
等三個指令,三個方法,把 feature 移動到想移動的分支。這邊就以 rebase 的方式,標示指令。
git checkout -b feature
git rebase 0f3eb49
上面的指令是假設,目前在 master 分支,也就是原本 feature 的基點。因此在原本的基點建立 feature 分支之後,只需要再把 feature 這個分支標籤貼紙,透過 fast-forward 的方法移動到需要移動的位置即可。(什麼是 fast-forward,詳見 Day 13 談關於 git 的 merge 機制 )
這一篇談了兩件事情,一是在刪除分支時,建議預設使用 -d
指令,真的很明確的知道有需求,才使用 -D
參數,強制刪除。二是,再次的強調,GIT 的分支概念,只是一個可移動的標籤貼紙,把貼紙撕掉之後,本體的 HASH Code 及對應的 Commit 是還可以找到的。