分支的整合:git rebase Or git merge
Git 整合分支一般有 git merge 和 git rebase 兩種。rebase 通常就是我們所說的「變基」、「衍合」。
假設我們現在有兩個分支,master和 develop 分支,master上的分支永遠都是穩定版本的,develop上的分支存放的是暫定的開發版本的代碼。我們需要將自己開發分支上的代碼先合併到 develop 上。
假設你從 master 新拉了一個分支,但是此時你的小夥伴已經上傳了她的代碼到了 develop 分支,此時分支的提交歷史如下:
此時你將你的分支整合到 develop 上就會有兩種整合方案,一是 git merge 另一種就是 git rebase。
git merge
使用 git merge 命令它會把兩個分支最新的快照(C4 和 C5)以及二者最新的共同祖先(C3)進行三方合併,合併的結果是產生一個新的提交對象(C6)。
由於當前 develop 分支所指向的提交對象(C4)並不是 lyt/bugfix 分支的直接祖先,Git 不得不進行一些額外處理。就此例而言,Git 會用兩個分支的末端(C4 和 C5)以及它們的共同祖先(C3)進行一次簡單的三方合併計算 並重新做一個新的快照,並自動創建一個指向它的提交對象,因此我們會看到git merge 之後會創建一個默認為Merge branch lyt/bugfix into develop
的commit信息。
最後的提交歷史如圖:
用 git merge 是最方便的合併操作,也會保留真實完整的 commit 信息。但是也會導致一個問題,就是會使得提交歷史發生很多分叉,如下圖:
由圖可見,提交從 master 開始分叉,分成 develop 和 lyt/bugfix 兩個分支,最後又合併到 develop 分支上。
這樣如果提交變得多了,分支分叉就會導致提交歷史歷史線變得十分混亂。因此,就有 rebase 操作,可以使得分支的線變得十分乾淨。
git rebase
使用 git merge 命令會使得提交的歷史出現分叉,使得提交變得十分混亂。
如果此時我們使用git rebase develop
就會使得 lyt/bugfix 這個分支變基到 develop 上,也就是說提取在 C5 中引入的補丁和修改,然後在 C4 的基礎上應用一次。
簡單來說,使用 rebase 命令將提交到某一分支上的所有修改都移至另一分支上,就好像「重新播放」一樣。
rebase 的原理就是會找到當前分支(lyt/bugfix)和變基的基底分支(develop)這兩個分支的共同提交祖先C3,然後對比當前分支相對於祖先之後的歷次提交,將這些提交存儲為臨時文件,最後找到基底分支的目標提交C4,將這些暫存的臨時文件依次應用。
之後我們看到的提交歷史就如下:
變基之後再回到 develop 分支,就可以進一次快進合併。
合併時出現了「Fast forward」的提示就是快進合併。由於當前 develop 分支所在的提交對象是要併入的 lyt/bugfix 分支的直接上游,Git 只需把 develop 分支指針直接右移。換句話說,如果順著一個分支走下去可以到達另一個分支的話,那麼 Git 在合併兩者時,只會簡單地把指針右移,因為這種單線的歷史分支不存在任何需要解決的分歧,所以這種合併過程可以稱為快進(Fast forward)。
同時快進合併不會創建上面使用 git merge 直接整合分支的過程中自動創建的一個第三方提交信息。
最後合併之後的提交歷史如圖:
現在的 C5』 對應的快照,其實和普通的三方合併,即 git merge 中的 C6 對應的快照內容一模一樣了,但是會使得歷史更清晰。
如下圖,我們使用圖形化的界面可以看到現在的提交歷史是一根線,沒有分叉的地方,看上去十分乾淨,但是我們也可以注意到此時更改了提交歷史。lyt/bugfix 這個分支能向前追溯到原來不屬於它的提交(其餘人在 someOtherCommits 上提交的 commit 信息),這樣就會篡改提交歷史。
解決衝突
如果在 rebase 的操作過程中遇到了衝突,比如我重新從 master 新建了一個分支,同時修改了 develop 分支中修改的一個文件。再 rebase 的時候就會遇到這樣的問題。
上面說的意思就是遇到了衝突,此時我們使用git status
查看是哪個文件衝突之後,再手動解決衝突。之後添加到暫存區再繼續進行 rebase。 rebase 完成之後通過 git log
就能看到之前在 develop 分支上的提交出現在 lyt/bugfix11 這個分支上了,說明已經變基成功。
git merge or git rebase?
關於這個問題,每個團隊都會有不一樣的標準。有的團隊會覺得 Git 最關鍵的就是要保留最原始最真實的提交信息,方便後面進行查閱。有的團隊會覺得認為提交歷史是項目過程中發生的事,最核心的是要使得提交歷史一目了然。選擇哪種操作還是根據團隊去決定。
但是對於變基操作,最關鍵的一點就是如果自己的分支被別人使用了成為了一個公共的分支,此時千萬不要再進行變基操作。如果使用 rebase 操作了那些已經公開的提交對象,並且已經有人基於這些提交對象開展了後續開發工作的話,就會使得提交歷史變得十分混亂。
借用一句話:只對尚未推送或分享給別人的本地修改執行變基操作清理歷史,從不對已推送至別處的提交執行變基操作,這樣,你才能享受到兩種方式帶來的便利。
參考資料:
Pro Git
3.6 Git 分支 - 變基
推薦閱讀:
※為什麼git默認不區分文件夾大小寫?
※使用git,用命令好還是客戶端好?
※Git算不算程序員的必備技能?
※github 自己創建了一個項目A,我的同事fork一個B,當我的項目更新的時候,怎麼樣在他fork的repo上進行相應的更新?
※github上怎麼刪除一個文件夾?