[教學] git應用-合併分支實況 - 討論區

[教學] git應用-合併分支實況

文章瀏覽次數 64 文章回覆數 0

特種兵

特種兵圖像(預設)

2020-12-25 13:40:18

From:211.23.21.202

背景與做法

我現在有兩個分支,分別是 dev 開發分支與 master 主分支

通常我在 dev 分支寫程式,確定沒問題後就會到 master 主分支 cherry-pick 我要的 dev 提交回來

不過有時總有意外,例如忘記切回 dev 就在 master 開始寫,或者是很急也很簡單的 bug 修正也會直接在 master 修正

另外,dev 做了很多提交,但有些提交可能暫時先不合併到 master,也許是還沒測試或還沒寫好之類的

所以,時間一長,master 跟 dev 就不會同步了。但我們會希望 dev 以正式環境的 master 來繼續開發下一個任務,這時候就會有兩種做法:

  1. 把 dev 分支刪掉,從 master 重新建一個 dev 分支出來
  2. 在 dev 拉 master 回來合併

做法與分析

第一種方法最簡單,但不實際,像是 dev 有 stash 還沒做完,或者有一些提交還沒測試好,就不能刪掉分支

第二種方法只是把 dev 缺乏的 master 提交補上,當然,有時候合併沒那麼順立,這就是我們今天要實做的重點。

基本上,我們是在 dev 把 master 分支從遠端倉庫拉回來合併。

也就是 pull, 其實就是做 fetch 跟 merge 的動作,之前都有提過,那就直接實做。

拉回

# 確定自己在 dev 分支
$ git branch                                                                    
 * dev                                                                           
   don_accton                                                                    
   master                                                                        
# 從遠端倉庫拉回 master
$ git pull origin master
password: # 打密碼
Auto-merging sys/static/js/admin_leave_data.js
# 自動合併這個檔案
CONFLICT (content): Merge conflict in sys/static/js/admin_leave_data.js
# 發生內容衝突,合併不了
Auto-merging sys/models/leaveAdminModel.php
CONFLICT (content): Merge conflict in sys/models/leaveAdminModel.php
# 狀況同上
Automatic merge failed; fix conflicts and then commit the result.
# 自動合併失敗,請修好它重新提交

觀察

看來訊息還不是很完整,讓我們再仔細確認一下狀況:

$ git status
On branch dev
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Changes to be committed:
	modified:   sys/self/leaveCalculation.php
# 這個沒問題,已修改但尚未提交,因為我們合併發生衝突了
Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   sys/models/leaveAdminModel.php
	both modified:   sys/static/js/admin_leave_data.js
# 就是這兩個檔案發生合併衝突了

簡單講,就是後悔可以打 git merge --abort 也適用於 cherry-pick, 那就回到還沒 pull 的狀態

如果要解決衝突,那就開啟衝突的檔案來修改

解決衝突

我用 notepad++ 開啟 leaveAdminModel.php, 搜尋 <<<

<<<<<<< HEAD
					$where .= " and li.itemsName = '".$data['itemsName']."'";
=======
					$where .= " and li.itemsName like '".$data['itemsName']."%'";
>>>>>>> 2c428fc952f29efcf30890a82e4761fccf45631c
  • <<< 到 === 之間的內容是 dev 原本的檔案內容
  • === 到 >>> 之間的內容是 master 那邊新的檔案內容

接下來就是要確定最後想改成怎樣,通常是新的留下,舊的刪掉

反正改的人要很清楚就是了,記得那些多餘的符號都要刪掉喔

這邊我們不講程式碼,直接秀修改的結果,我留下 master 版的程式碼:

					$where .= " and li.itemsName like '".$data['itemsName']."%'";

接著繼續搜尋 <<< 看看,因為一個檔案可能會有多個衝突點

都處理好後存檔離開

另一個檔案也是一樣的做法,這個檔案我改比較多,就不詳列了

當然,你也可以直接修改成不是這兩個版本的最終版,反正都會重新提交

都處理好後就進入我們熟悉的 git add .git commit

git add .git status 可以看到目前是處在解決衝突的狀態

其中,在 git commit 會有一個預設的訊息:

Merge branch 'master' of 192.168.0.12:/xxxx/yyyy into dev

當然可以改成我們想要的 commit 訊息

合併 master 
# 存檔繼續會看到
$ git commit                                                                    
 [dev a1fb85b] 合併 master                                                       
# 再次確認
$ git status                                                                    
 On branch dev                                                                   
 nothing to commit, working tree clean                                           
# git log -1
 commit a1fb85bb1d2fbbb4c4e83064b852aa854da5394e (HEAD -> dev)                   
 Merge: 0a867c0 2c428fc                                                          
 Author: Logo-Kuo <logo@forblind.org.tw>                                         
 Date:   Fri Dec 25 11:03:06 2020 +0800                                          

     合併 master                                                                 

好了,合併完成

補充

在 dev 中,原本 master 提交的 commit 訊息也還在,再加一個我們剛剛推的 合併 master 提交

如果在 commit 時加個參數 --no-edit 就不會要你寫 commit 訊息,但還是會有預設的,跟原本預設的 merge 內容不同

這次多了你處理衝突的檔名資訊,如下:

 commit fc06b254d95eb4c81ee18cc14e5574102837c13c (HEAD -> dev)                   
 Merge: 0a867c0 2c428fc                                                          
 Author: Logo-Kuo <logo@forblind.org.tw>                                         
 Date:   Fri Dec 25 11:41:27 2020 +0800                                          

     Merge branch 'master' of 192.168.0.11:/xxxx/yyyy into dev                     

     # Conflicts:                                                                
     #       sys/models/leaveAdminModel.php                                      
     #       sys/static/js/admin_leave_data.js                                   

其實處理衝突的檔案資訊原本就在,只是 git commit 時是被 # 註解掉了而已

那如果你完全不想要有這個 merge 的訊息,可以考慮使用 git pull origin master --rebase

為什麼說是「考慮」呢?因為我們知道 rebase 會把所有的 commit 重做一遍,所以假設兩個分支差很多

中間有一些 merge 之類的,那你 rebase 就要處理所有的衝突,但 merge 只要解決這次的衝突,所以使用時機自己評估

我自己是非不得已不會使用 rebase, dev 分支有合併訊息是很正常的,讓正式主分支保持乾淨就好了

衝突的由來

針對這個問題,我之前也一直無法理解,就是怎樣的狀況會發生衝突。

當然,不同人相互寫同一個分支拉下推上的會衝突不意外,但怎樣叫做衝突?

我們以這次 dev 合併 master 的例子來說,事實上,master 我修改的檔案不只兩個,那為什麼只有兩個檔案發生衝突?

其實是 git 會根據最近的提交去比對,假設最近的提交改了 a 檔案的第 5 行

那在合併時新的提交也修改到 a 的第 5 行,這樣 git 不知道誰才是最終需要的版本,就會發生衝突,

然後列出衝突點讓我們決定最終的版本。

我以前還以為 git 是會自動以最新日期來直接蓋掉舊的衝突點,但預設應該不是這樣

但不知道有沒有這樣的設定就是了