圖解 Git 基本命令 merge 和 rebase
阿新 • • 發佈:2020-06-23
![green-leaf-merge-git](https://gitee.com/michael_xiang/images/raw/master/uPic/green-leaf-merge-git.jpg)
# Git 基本命令 merge 和 rebase,你真的瞭解嗎?
## 前言
Git 中的分支合併是一個常見的使用場景。
- 倉庫的 bugfix 分支修復完 bug 之後,要回合到主幹分支,這時候兩個分支需要合併;
- 遠端倉庫的分支 A 有其他小夥伴合入了程式碼,這時候,你需要和遠端倉庫的分支 A 進行合併;
以上只是列舉了分支合併的一些常見場景,關於 `merge` 和 `rebase` 命令你足夠了解嗎?
## HEAD 的理解
在介紹本文的主要內容之前,我們先理解一下 `HEAD` 。
`HEAD` 指向**當前所在的分支**,類似一個活動的指標,表示一個「引用」。例如當前在 `develop` 分支,`HEAD` 內容就是 `ref: refs/heads/develop`。
`HEAD` 既可以指向「當前分支」的最新 `commit`,也可以指向歷史中的某一次 `commit` (「分離頭指標」的情況)。歸根結底,`HEAD` 指向的就是某個提交點。
當我們做分支切換時,`HEAD` 會跟著切換到對應分支。
## fast-forward 與 --no-ff 的區別
假如有一個場景:有兩個分支,master 分支和 feature 分支。現在,feautre 分支需要合併回 master 分支。
![fast-forward-初始狀態](https://gitee.com/michael_xiang/images/raw/master/uPic/wOR7JK.png)
`fast-forward` 合併方式是**條件允許**的情況,通過將 master 分支的 HEAD 位置移動到 feature 分支的最新提交點上,這樣就實現了快速合併。這種情況,是不會新生成 commit 的。
![fast-forward](https://gitee.com/michael_xiang/images/raw/master/uPic/QpdH5g.png)
`--no-ff` 的方式進行合併,master 分支就會新生成一次提交記錄。
![--no-ff](https://gitee.com/michael_xiang/images/raw/master/uPic/BIqlQW.png)
> 如果條件滿足時,merge 預設採用的 `fast-forward` 方式進行合併,除非你顯示的加上 `--no-ff` 選項;而條件不滿足時,merge 也是無法使用 `fast-forward` 合併成功的!
## merge 操作
上面用圖解的方式介紹了 `fast-forward` 和 `--no-ff` 的區別。下面,結合實際的程式碼倉進行合併操作,舉幾個栗子理解一下。
> `git merge` 操作是區分上下文的。**當前分支始終是目標分支**,其他一個或多個分支始終合併到當前分支。這個注意點記住了,方便記憶!所以,當需要將某個分支合併到目標分支時,需要先切到目標分支上。
### fast-forward 合併
剛剛一直在強調條件允許的時候,`fast-forward` 才能合併成功。條件指的是什麼呢?
其實指的是源分支和目標分支之間沒有分叉(單詞 `diverge`),這種情況才可以進行快速合併。如果是下圖中的場景,無法通過 HEAD 的快速移動實現分支的合併!
![分叉](https://gitee.com/michael_xiang/images/raw/master/uPic/RQ39Rv.png)
下面進行一個不分叉的場景的示例:
![featuren 分支的初始狀態](https://gitee.com/michael_xiang/images/raw/master/uPic/A87zPe.png)
現在需要將 feature 分支合入到 master 分支,預設使用 `fast-forward` 方式:
```
# 切到目標分支
git checkout master
git merge feature
```
命令列裡顯示了 `Fast-forward` 的提示:
![合入效果](https://gitee.com/michael_xiang/images/raw/master/uPic/J0Zku9.png)
看一眼 master 分支合入的前後對比(注意 HEAD 的位置):
![master 分支合入前](https://gitee.com/michael_xiang/images/raw/master/uPic/KmsbrV.png)
![master 分支合入後](https://gitee.com/michael_xiang/images/raw/master/uPic/M9PM7Q.png)
### no-ff 合併
不分叉的場景是否可以強制採用 `--no-ff` 方式合併呢?可以!
```shell
# master 回到合入前的狀態
git reset --hard d2fa1ae
git merge feature --no-ff
```
![no-ff](https://gitee.com/michael_xiang/images/raw/master/uPic/LTzh9p.png)
這次命令列沒有 `Fast-forward` 的提示了。
看一眼 master 分支圖:
![no-ff](https://gitee.com/michael_xiang/images/raw/master/uPic/Sklh3v.png)
這個圖和上面講解 `no-ff` 命令時的示意圖一致,果然會有新 `commit` 生成。
### 分叉場景的合併
![分叉](https://gitee.com/michael_xiang/images/raw/master/uPic/X69y67.png)
上面的圖展示了我們經常遇到的一個場景,特性分支建立之後,源分支也會有新的提交。這就是形成分叉了。
這時候如果我們進行合併呢?
```
git merge feautre
```
![分支圖](https://gitee.com/michael_xiang/images/raw/master/uPic/g9NjTO.png)
可以看到,雖然預設會嘗試 `fast-forward` 的方式進行合併,但是因為分叉了,所以此時會採用 `no-ff` 的方式進行合併!有新的 `commit` 生成了!
> fast-forward 方式對應的合併引數是 `--ff`
我們試試這個引數 `--ff-only`,顧名思義,就是強制只使用 `ff` 方式進行合併:
```
# 回到合併前
git reset --hard 3793081
git merge feature --ff-only
```
![合併終止](https://gitee.com/michael_xiang/images/raw/master/uPic/yIaYHq.png)
經過測試,當分叉時,因為無法使用 `ff` 方式合併,即使你強制指定使用該方式合併也不行,會提示終止!
附上 Git 官方文件中的解釋,方便理解:
```shell
With --ff, when possible resolve the merge as a fast-forward (only update the branch pointer to match the merged branch; do not create a merge commit). When not possible (when the merged-in history is not a descendant of the current history), create a merge commit.
```
## rebase 操作
`rebase` 命令是一個經常聽到,但是大多數人掌握又不太好的一個命令。`rebase` 合併往往又被稱為 「變基」,我稱為 「基化」