有問題為什么不問問神奇的 man 呢?
rebase
也算是我比較常用的一個指令了,但是很長時間以來,對這個指令的認識還是不夠深刻,於是就找了個時間認真地讀了一下 git rebase 的文檔。這份文檔不需要在網絡上查找,在自己的電腦上直接使用 man git-rebase
就可以查看了。在這份文檔中,被提到的幾個重要的 rebase 參數就是 newbase
、upstream
、branch
。除此之外,-i
也是一個能夠極大的提升使用體驗的選項,允許我們交互式的選取需要操作的提交。
git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>]
[<upstream> [<branch>]]
三個參數
在執行 git rebase 的之前,如果我們指定了 branch
這個參數,那么 git 會在開始 rebase 操作之前簽出到 branch
。
接着,git 會把所有在當前分支但不在 upstream
分支的提交保存到一個臨時區域。如果想要在 rebase 開始之前了解哪些提交會被移到臨時區域,可以使用 git log <upstream>..<branch>
查看。
然后,如果我們設置了 newbase
,那么 git 會把當前分支重置到 newbase
,否則,就重置到 upstream
,這里的效果就相當於 git reset <newbase>
。
接下來,git 會將之前存放到臨時區域內的提交逐個按順序地重新作用到當前分支上。
根據上面介紹的執行過程,我們可以總結出以下幾個知識點:
-
branch 分支或者執行 git rebase 的當前分支,最終會被移動到 upstream 或者 newbase 分支上
-
branch 與 upstream 構成了一個選擇器,可以通過這兩個參數來把 branch..upstream 之間的提交移動跟隨 branch 移動到新的地方
這樣看來,rebase 做的事其實是使用 upstream
以及 branch
選取一段提交,然后在把這段提交從原先的分支上剪下來,然后嫁接到 upstream
或者 newbase
上去。
接着結合一個簡單的例子來熟悉一下 rebase 中這三個參數的作用,例如,我們有這樣一個倉庫:
A---B<start>---C---D<feature><HEAD>
/
E---F---G<master>
我們目前位於 feature 分支,現在我們需要使用 rebase 將 feature 分支上的 C、D 兩個提交移植到 master 上面。因為最終的目的地是 master 分支,所以可以確定 newbase 參數的值是 master;為了選中 C、D 兩個提交,我們可以使用 feature 分支作為 branch 參數,使用 start 分支作為 upstream 參數。最終,我們需要執行的指令就是:
git rebase --onto master start feature
因為,我們目前就在 feature 上面,所以 branch 參數可以省略掉:
git rebase --onto master start
最終我們得到的結果如下所示:
C'---D'<feature><HEAD>
/
E---F---G
\
A---B<start>
交互式操作后可能會遇到的問題
在交互式 rebase 中,我們可以選取需要操作的提交,但這種可選擇的操作有時候可能會給 git 新手造成一種令人慌亂的局面。例如,我們有這樣一個倉庫:
A---B---C---D<feature><HEAD>
/
D---E---F<master>
然后,我們執行下面的命令:
git rebase -i master
假設只有 B、C 被選取了,那么這個倉庫接下來就會變成:
B'---C'<feature>
/
D---E---F<master>
現在,你可能已經發現了一個問題,A 與 D 這兩個提交“消失”了,這時候先不要慌,可以先想想辦法研究出時光機器回到過去阻止自己。當然,這只是一個玩笑。這兩個提交並沒有真正的消失,他們只是沒有被某個分支引用而已。要讓它們重新回到分支樹上,我們需要先查詢這兩個提交的 hash :
git reflog feature
reflog
可以幫助我們查看指定分支的操作歷史,包括 commit、rebase 等操作。
查詢到 hash 之后,只要在這些被歷史遺忘的 commit 上創建新的分支,我們就可以在 git log
中重新見到他們了。