上篇博客聊了《git分支管理之rebase 以及 cherry-pick相關操作》本篇博客我們就以Learning Git中的關卡進行展開。下方列舉了LearningGit中的 merge、rebase、reset、revert、cherry-pick 以及交互式rebase相關關卡的操作以及對應的解析。后邊在聊交互式rebase操作是,不單單給出了LearningGit中的內容,而且給出了真正的Git分支在交互式rebase操作時的具體案例。
learngitbranching的地址為:https://learngitbranching.js.org/
一、代碼合並
在下方示例中所有目標的初始化狀態是下方這樣的,然后我們要按照目最終目標,使用相關的git命令來達到相關的目標。

1、git merge
下方就是我們要達到的目標,下方是我們達到下方目標所要做的事情:
-
首先需要做的就是創建一個新的分支bugFix, 並切換到該分支上,然后進行一次C2的提交。
-
然后再切回到master分支上,進行一次新的提交(C3)。
-
最后要做的就是在master分支上執行一次合並操作,將bugFix分支上的提交合並到master分支上,C4就是合並后的節點。

下方對應的就是我們實現上述目標所對應的命令操作,具體如下所示:
-
首先使用 git checkout -b bugFix, 新建並切換到bugFix分支上,並且使用 git commit 命令進行一次提交生成C2節點。
-
然后使用 git checkout master 命令切換到master分支上,並且使用 git commit 命令進行一次提交生成C3節點。
-
最后的話,就是在 master 分支上執行 git merge bugFix命令,將bugFix分支合並到master分支上,合並后會生成一個新的C4節點。具體如下所示:

2、git rebase
闖完git merge的關,我們來看一下git rebase的關。下方就是我們最終要實現的目標。實現下方目標和上面的merge操作差不多,只不過最后一步不是使用合並操作,而是在bugFix上執行變基操作,具體分析如下:
-
首先需要做的就是創建一個新的分支bugFix, 並切換到該分支上,然后進行一次C2的提交。
-
然后再切回到master分支上,進行一次新的提交(C3)。
-
然后在切換到 bugFix 分支上,執行變基操作,將bugFix的父節點變成master分支,之前的C2節點就被新的提交C2`所替代了。

下方是我們的具體命令操作:
-
首先使用 git checkout -b bugFix, 新建並切換到bugFix分支上,並且使用 git commit 命令進行一次提交生成C2節點。
-
然后使用 git checkout master 命令切換到master分支上,並且使用 git commit 命令進行一次提交生成C3節點。
-
然后再使用 git checkout bugFix 命令切回到 bugFix 分支上。
-
最后在bugFix分支上執行 git rebase master 命令,經其父類變成master。執行變基后,C2會和C3節點的內容進行合並生成新的節點C2`,而bugFix分支的指針也會從C2節點移動到C2`上,移動后bugFix之前的分支就會被廢棄掉,取而代之的是從master延續下來的新分支。
二、分離HEAD
1、移動HEAD指針
HEAD指針是指向當前所在的操作分支,比如我們現在是在master分支,那么HEAD就指向master分支,然后master分支指向的是我們的commit號。分離的 HEAD 就是讓其指向了某個具體的提交記錄而不是分支名。下方左邊的圖就是我們要完成的目標,右邊是我們分支的初始化狀態。

實現上述目標一行命令足矣,可以使用 git checkout C3 命令就可以將HEAD命令指向C3提交上。git checkout HEAD^ 命令可以將HEAD指針向上移動一個距離,git checkout HEAD~3 則可以將HEAD向上移動三個距離。具體操作如下所示:

2、在提交樹上移動分支
下方要完成的不單單是HEAD指針的移動,而是分支指針的移動,在Git上可以移動你所創建分支的指向,使其指向任意提交過的分支上。下方就來看一下如何在git上移動分支指針,下方左邊是我們要完成的目標,右邊是分支的初始化狀態。需要做的事情如下:
-
將 bugFix 分支移動到C0上。
-
然后將master分支移動到C6上。
-
最后將HEAD分支上移。

需要操作的命令如下所示:
-
首先使用 git branch -f bugFix C0 命令將bugFix指向C0節點。(C0表示的是相關提交的哈希值)。
-
然后使用 git branch -f master C6 命令將master分支指向C6節點。
-
最后使用 git checkout HEAD^ 命令將分離的HEAD指針進行上移。

3、撤銷操作
接下來我們來看一下撤銷操作,同樣左邊是我們要完成的目標,右邊是初始狀態。從下方的目標中我們可以看出 local 分支的撤銷操作是使用的 git reset 操作的, 因為是在本地來向上移動的,進行reset后是不可以push到遠端的。而push分支使用的是revert操作,撤銷了C2的提交后,再C2的基礎上又生成了一個新的提交。reset 操作是不可以被push到遠端的,而revert則可以,稍后會進行實驗。下方會有具體的操作。

下方就是我們為了完成目標而又的具體的操作:
-
首先在 local 分支上執行 git reset HEAD^1 或者 git reset HEAD C1 操作來撤銷本地的C3操作。
-
然后我們再通過 git checkout pushed操作切換到 pushed 分支上,然后執行 git revert HEAD^1 操作,撤銷C2的提交。

如果你reset某個提交,想在將分支號移動到之前的提交上,可以使用上面的 git branch -f 操作,將相應的分支移到相應的提交上。下方是將 local 分支又移動回了C3, 如下所示。

接下來我來看一下對 reset 后的分支進行push, 以及對 revert 分支后進行push。
-
首先在local分支上執行reset操作,然后進行push會提示本地倉庫和遠程倉庫產生了分歧,先git pull 或者 git pull --rebase。
-
而在pushed分支上的revert操作就不會有這樣的提示,因為revert是在原來的分支下方產生了一個新的提交,和正常提交一樣對待,所以是可以push的。

下方我們再做個嘗試,在一個分支上進行了reset , 然后在reset后的分支上做了一些提交。最后我們將這些提交進行push,然后看一下具體的效果。
-
首先我們對clone到本地的local分支進行了reset操作,操作后在新的分支上進行兩次commit。
-
然后我們進行push , 會提示先pull或者 pull --rebase , 然后在進行push.
-
下方先執行了 git pull 操作,執行pull操作后,就是將 o/local 分支和 local分支進行合並,合並后就可以進行push了。這樣一來,我們之前reset操作就不起什么作用了。因為 pull 操作后進行了merge, 就等效於在C3上直接進行commit。
-
然后我們進行回退,又試了一下 git pull --rebase 操作,其實該操作就是將merge操作改成了變基操作。將我們后來的C4, C5兩個提交變基到C3上,從效果上看,就和沒有執行reset操作一樣。具體如下所示:

三、cherry-pick和交互式rebase
之所以將這兩個放在一塊,是因為使用兩者都可以達到相同的目標,只是操作不同。下方會分別介紹。
1. cherry-pick
下方我們來看一下cherry-pick這一關,下方我們需要將 bugFix 分支上的 C3 、side 分支上的 C4 以及another分支上的C7通過cherry-pick的形式拿到 master分支上。

下方主要還是使用了cherry-pick來達到我們的目標的,主要還是一個命令的使用 , 在 master 分支上執行 git cherry-pick C3 C4 C7, 可以將C3 C4 C7這三個提交摘到master分支上了。具體如下所示:

2、交互式rebase
解析我們來使用交互式rebase來做節點的遷移,當然下方的操作也是可以使用cherry-pick來完成的。左邊是我們要完成的目標,右邊則是初始化狀態。我們需要將C2 C3 C4 C5的提交順序轉換成C3, C5, C4的順序。

下方就是我們交互式rebase操作的具體步驟,本質上就一個命令 git rebase -i HEAD~4, 然后操作相關的節點即可。

3、交互式rebase實踐
接下來我們來看一下在真正的git分支上是如何使用交互式rebase操作的。下方是做交互式rebase操作之前的分支關系。目前所在的分支是bugFix, 其中有4個提交。

現在要做的是在bugFix上進行交互式rebase, 在終端中輸入 git rebase -i master, 目的是將 bugFix 分支上的提交通過交互式rebase的方式將其變基到master分支上。下方是輸入git rebase -i maste命令后所出現的界面,我們可以通過vim編輯器編輯將要執行的變基操作。下方是對應的幾種交互式命令
-
pick 應用相關提交。
-
reword 修改commit信息。
-
edit 對提交進行編輯,然后使用 git commit -amend進行提交。
-
squash 是把多個提交合並成一個提交
-
fixup 與squash差不多,不過會拋棄掉本次提交的log信息
-
exec 執行shell命令
-
drop 刪除提交

下方我們對相關操作執行的交互式的操作:
-
首先使用 reword 來操作下方截圖中的第一條操作,用來修改message。
-
然后交換了第二行和第三行的pick的位置
-
然后對第四行的提交執行edit命令對其進行修改
-
然后刪除 編號為04的提交

點擊回車鍵的話會彈出下方的vim編輯器來讓你修改 f53560c 這個操作的commit message,修改完畢后進行保存即可。

下方是在rebase合並時產生了沖突,我們需要對沖突進行解決。解決完畢后,執行 git add 將沖突文件進行存儲,並且執行git rebase --continue 來繼續我們的rebase操作。
經過一系列解決沖突的操作,最終我們的rebase操作是成功的,會提示下方的 Successfully。

交互式rebase操作成功后,接下來我們來看一下當前分支的情況,,從結果中我們不難看出:
-
bugFix 分支上的提交已經變基到了master分支上。
-
“change aa.text 04”的提交已經被移除了。
-
“change aa.text 01” 和 “change aa.text 02” 的提交順序進行了交換。
-
並且 "create aa.text" 的log變成了 "create aa.text reword"

4、交互式rebase的squash操作
接下來我們來看一下squash的操作,下方我們會在當前所在分支和上次提交上執行squash操作,其對應的命令的為:git rebase -i HEAD~1,如下所示:

我們對該操作執行squash命令,如下所示,編輯完進行保存即可。

保存后會出現下方的操作,目的是用來編輯兩次提交合並后的commit message 的。

編輯完保存即可,下方就是我們進行上述操作后所對應的相關信息。

5、交互式rebase的另一個示例
接下來我們來看一下另一個交互式rebase的示例,完成下方的目標,我們需要做下方幾步:
-
首先我們通過交互式rebase將caption變基到master分支上,在變基操作時交換 C2 和 C3的位置。
-
然后通過 git commit --amend 往 C2 上追加提交內容。
-
最后再通過 git rebase -i 操作將C2和C3進行交換。
下方就是對應的具體命令操作:
-
首先在caption分支上執行 git rebase -i master, 將caption分支通過交互式rebase的方式變基到master分支。
-
在交互式變基時,修改了C2和C3的提交順序。
-
然后在通過git commit -amend 操作往C2上追加了一些修改。
-
然后再通過git rebase -i master , 將C2和C3進行交換回來。
-
最后將master使用git rebase 操作進行快速移動到caption上。

今天博客就先到這兒吧,下篇博客繼續聊git相關的內容。
