2019年10月24日 星期四

[Git 文章收集] git merge 的三種操作 merge, squash merge, 和 rebase merge

Source From Here
Preface
git 進行 merge 有三種操作 merge, squash merge, 和 rebase merge. 底下說明其在 commit tree 上的差別. 舉例來說, 假設在 master 分支的 B點 拉出一個新的分支 dev,經過一段時間開發後:
master 分支上有兩個新的提交 M1 和 M2
dev 分支上有三個提交 D1,D2,和 D3


如下圖所示:



現在我們完成了 dev 分支的開發測試工作,需要把 dev 分支合併回 master 分支。

方法一: merge
這是最基本的 merge,就是把提交歷史原封不動的拷貝過來,包含完整的提交歷史記錄:
$ git checkout master
$ git merge dev



此時還會生產一個 merge commit (D4'),這個 merge commit 不包含任何代碼改動,而包含在 dev 分支上的幾個 commit 列表(D1, D2 和 D3)。查看 git 的提交歷史(git log)可以看到所有的這些提交歷史記錄。

方法二: squash merge
根據字面意思,這個操作完成的是壓縮的提交;解決的是什麼問題呢,由於在 dev 分支上執行的是開發工作,有一些很小的提交,或者是糾正前面的錯誤的提交,對於這類提交對整個工程來說不需要單獨顯示出來一次提交,不然導致項目的提交歷史過於復雜;所以基於這種原因,我們可以把 [b]dev 上的所有提交都合併成一個提交;然後提交到主幹[/b]。
$ git checkout master
$ git merge --squash dev



在這個例子中,我們把 D1,D2 和 D3 的改動合併成了一個 D。

注意,squash merge 並不會替你產生提交,它只是把所有的改動合併,然後放在本地文件,需要你再次手動執行 git commit 操作;此時又要注意了,因為你要你手動commit,也就是說這個 commit 是你產生的,不是有原來 dev 分支上的開發人員產生的,提交者本身發生了變化。也可以這麼理解,就是你把 dev 分支上的所有代碼改動一次性 porting 到 master 分支上而已。

方法三: rebase merge
由於 squash merge 會變更提交者作者信息,這是一個很大的問題,後期問題追溯不好處理 (當然也可以由分支 dev 的所有者來執行 squash merge 操作,以解決部分問題),rebase merge 可以保留提交的作者信息,同時可以合併 commit 歷史,完美的解決了上面的問題
$ git checkout dev
$ git rebase -i master
$ git checkout master
$ git merge dev

rebase merge 分兩步完成:
第一步:
執行 rebase 操作,結果是看起來 dev 分支是從 M2 拉出來的,而不是從 B 拉出來的,然後使用 -i 參數手動調整 commit 歷史,是否合併如何合併。例如下 rebase -i 命令會彈出文本編輯框:
  1. pick Message for commit #1  
  2. pick Message for commit #2  
  3. pick Message for commit #3  
假設 D2 是對 D1 的一個拼寫錯誤修正,因此可以不需要顯式的指出來,我們把 D2 修改為 fixup:
  1. pick Message for commit #1  
  2. fixup Message for commit #2  
  3. pick Message for commit #3  
rebase 之後的狀態變為:


D1' 是 D1 和 D2 的合併。

第二步:
再執行 merge 操作,把 dev 分支合併到 master 分支:



注意:在執行 rebase 的時候可能會出現衝突的問題,此時需要手工解決衝突的問題,然後執行 (git add) 命令;所有衝突解決完之後,這時不需要執行 (git commit) 命令,而是運行 (git rebase --continue) 命令,一直到 rebase 完成;如果中途想放棄 rebase 操作,可以運行 (git rebase --abort) 命令回到 rebase 之前的狀態。

A Real rebase merge Case
底下我們來看一個實際的 rebase merge 範例:
// 檢視目前所在 branch
# git status
# On branch dev
nothing to commit, working directory clean


// 檢視目前提交紀錄
# git log --oneline | head -n 5
c9dbafb D3
8fc7461 D2
fd091f5 D1

bb134c5 Update 105M.img
faa8827 Upload large file >50M

接著我們要對 master 進行 rebase :
// 對 master 進行 rebase
# git rebase master
First, rewinding head to replay your work on top of it...
Applying: D1
Applying: D2
Applying: D3


// 檢視目前 commit 紀錄
# git log --oneline | head -n 8
87bd527 D3
dc2c6f7 D2
8e39f33 D1
57c0f58 M2
6039665 M1

bb134c5 Update 105M.img
faa8827 Upload large file >50M
66f0f1a Another commmit for merge testing

接著回去 master branch, 並對 dev 進行 merge:
# git checkout master
# git merge --squash dev
Updating 57c0f58..87bd527
Fast-forward
Squash commit -- not updating HEAD
test_dev.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 test_dev.txt


// 檢視目前狀態, 使用 --squash 並不會自動提交, 因此還需要手動 commit
# gis
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
# (use "git push" to publish your local commits)
#
# Changes to be committed:
# (use "git reset HEAD ..." to unstage)
#
new file: test_dev.txt
#


// 檢視 commit 紀錄確定這一點
# git log --oneline | head -n 3
57c0f58 M2
6039665 M1


// 進行提交並檢視 commit 紀錄
# git commit -m 'Merge from dev'

# git log --oneline | head -n 4
53eade5 Merge from dev
57c0f58 M2
6039665 M1

bb134c5 Update 105M.img

通常如果 dev 日後還需要使用, 則需要再回到 dev 並將 master merge 回來:
# git checkout dev
# git merge master
Merge made by the 'recursive' strategy.

# git log --oneline | head -n 8
61677cf Merge branch 'master' into dev
53eade5 Merge from dev
87bd527 D3
dc2c6f7 D2
8e39f33 D1
57c0f58 M2
6039665 M1

bb134c5 Update 105M.img

如果要看更完整的 commit 紀錄, 可以使用 git log --graph --all --decorate:

沒有留言:

張貼留言

[Git 常見問題] error: The following untracked working tree files would be overwritten by merge

  Source From  Here 方案1: // x -----删除忽略文件已经对 git 来说不识别的文件 // d -----删除未被添加到 git 的路径中的文件 // f -----强制运行 #   git clean -d -fx 方案2: 今天在服务器上  gi...