1. 程式人生 > 其它 >Git多分支管理

Git多分支管理

Git多分支管理

問題背景

實際工作過程中如果既定版本號為1.0的釋出版本中包含兩個最新的產品特性,而這兩個特性分別由兩個小組並行開發,那麼兩個小組的特性分支開發完畢之後應該如何合併入1.0的釋出版本,不同merge引數帶來不盡相同的效果讓人容易混淆,這裡就專門對比一下常用引數的異同。

舉個例子

這裡我們假定有下面的情況:釋出版本號為1.0對應的分支名稱是release1.0,其下兩個特性分支名稱分別為feature1.0-1、feature1.0-2。初始狀態下release1.0、feature1.0-1、feature1.0-2均從master建立而來。feature1.0-1由team1開發,feature1.0-2由team2並行開發,其中team1、team2各有有三次提交,詳情如下。

commit 5f234c579c204e7a57de79380fd0412e234f37d8 (HEAD -> feature1.0-1, origin/feature1.0-1)
Author: team1 <[email protected]>
Date:   Fri Jun 4 23:39:24 2021 +0800

    team1 update file3

commit 157fe9c1104064c68f8b8b6dee92c60d0a9bc1bf
Author: team1 <[email protected]>
Date:   Fri Jun 4 23:37:54 2021 +0800

    team1 update file1

commit ae8352071aad40348ad287a4f1e7f27e6b37ad66 (origin/release1.0, origin/master, origin/feature1.0-2, origin/HEAD)
Author: elfcafe <[email protected]>
Date:   Fri Jun 4 22:24:34 2021 +0800

    init commit

team2三次提交:

hackun$ git log
commit 0250a1956079ff91454e5251028a292dd941365b (HEAD -> feature1.0-2)
Author: team2 <[email protected]>
Date:   Fri Jun 4 23:39:52 2021 +0800

    team2 add file4

commit 9d3f9edbef2eb8e6f5d392827a64f1c444754a2a
Author: team2 <[email protected]>
Date:   Fri Jun 4 23:38:53 2021 +0800

    team2 upddte file2

commit ae8352071aad40348ad287a4f1e7f27e6b37ad66 (origin/release1.0, origin/master, origin/feature1.0-2, origin/feature1.0-1, origin/HEAD)
Author: elfcafe <[email protected]>
Date:   Fri Jun 4 22:24:34 2021 +0800

    init commit

這裡我們採用ff和no-ff兩種不同的merge引數進行合併來對比下merge不同的行為

ff模式

hackun$ git branch
* release1.0
hackun$ git log
commit ae8352071aad40348ad287a4f1e7f27e6b37ad66 (HEAD -> release1.0, origin/release1.0, origin/master, origin/HEAD)
Author: elfcafe <[email protected]>
Date:   Fri Jun 4 22:24:34 2021 +0800

    init commit
hackun$ git merge origin/feature1.0-1
Updating ae83520..5f234c5
Fast-forward
 file1 | 1 +
 file3 | 1 +
 2 files changed, 2 insertions(+)

ff即fast-forward,作為git merge時候預設選項,merge執行提示中會顯示Fast-forward字樣,檢視執行過後的結果:

hackun$ git log --graph --decorate --pretty=oneline --abbrev-commit
* 5f234c5 (HEAD -> release1.0, origin/feature1.0-1) team1 update file3
* 157fe9c team1 update file1
* ae83520 (origin/release1.0, origin/master, origin/HEAD) init commit

此次合併只是把feature1.0-1中的兩次修改對映到了release1.0上,並沒有產生單獨的merge記錄

下面繼續合併feature1.0-2中的變更:

hackun$ git merge origin/feature1.0-2
......編輯提交資訊
Merge made by the 'recursive' strategy.
 file2 | 1 +
 file4 | 0
 2 files changed, 1 insertion(+)
 create mode 100644 file4

由於產生了獨立的merge提交,所以提交後的分支記錄如下:

hackun$ git log --graph --pretty=format:'%Cred%h%Creset %ad %s %C(yellow)%d%Creset %C(bold blue)<%an>%Creset' --date=short --date-order
*   99f5fab 2021-06-04 Merge remote-tracking branch 'origin/feature1.0-2' into release1.0  (HEAD -> release1.0) <elfcafe>
|\  
| * 0250a19 2021-06-04 team2 add file4  (origin/feature1.0-2) <team2>
* | 5f234c5 2021-06-04 team1 update file3  (origin/feature1.0-1) <team1>
| * 9d3f9ed 2021-06-04 team2 upddte file2  <team2>
* | 157fe9c 2021-06-04 team1 update file1  <team1>
|/  
* ae83520 2021-06-04 init commit  (origin/release1.0, origin/master, origin/HEAD) <elfcafe>

no-ff模式

合併步驟大體都一致,不同的是我們在合併feature1.0-1、feature1.0-2時都使用了--no-ff引數

MacBookProEvo:release1.0_bak hackun$ git merge --no-ff origin/feature1.0-1
Merge made by the 'recursive' strategy.
 file1 | 1 +
 file3 | 1 +
 2 files changed, 2 insertions(+)
 
MacBookProEvo:release1.0_bak hackun$ git merge --no-ff origin/feature1.0-2
Merge made by the 'recursive' strategy.
 file2 | 1 +
 file4 | 0
 2 files changed, 1 insertion(+)
 create mode 100644 file4

兩次merge的引數都指定了----no-ff,最後的效果如下:

MacBookProEvo:release1.0_bak hackun$ git log --graph --pretty=format:'%Cred%h%Creset %ad %s %C(yellow)%d%Creset %C(bold blue)<%an>%Creset' --date=short --date-order
*   ed0543b 2021-06-05 Merge remote-tracking branch 'origin/feature1.0-2' into release1.0  (HEAD -> release1.0) <elfcafe>
|\  
* \   287a3c8 2021-06-04 Merge remote-tracking branch 'origin/feature1.0-1' into release1.0  <elfcafe>
|\ \  
| | * 0250a19 2021-06-04 team2 add file4  (origin/feature1.0-2) <team2>
| * | 5f234c5 2021-06-04 team1 update file3  (origin/feature1.0-1) <team1>
| | * 9d3f9ed 2021-06-04 team2 upddte file2  <team2>
| |/  
|/|   
| * 157fe9c 2021-06-04 team1 update file1  <team1>
|/  
* ae83520 2021-06-04 init commit  (origin/release1.0, origin/master, origin/HEAD) <elfcafe>

這裡287a3c8作為feature1.0-1併入release1.0的節點,ed0543b代表feature1.0-2併入release1.0的節點,結構上非常清晰。

回退分支

回退分支主要針對release1.0而言,如果release1.0的兩次合併請求都採用了之前提到的(--no-ff方式)

尚未push

先看下都尚未push到remote的情況

這時候如果需要只保留feature1.0-1的分支特性(剔除team2開發feature1.0-2),那麼直接rest到HEADˆ,便輕鬆回退掉了feature1.0-2相關程式碼。

MacBookProEvo:release1.0_bak_orig hackun$ git reset HEAD^ --hard 
HEAD is now at 287a3c8 Merge remote-tracking branch 'origin/feature1.0-1' into release1.0

hackun$ git log --graph --pretty=format:'%Cred%h%Creset %ad %s %C(yellow)%d%Creset %C(bold blue)<%an>%Creset' --date=short --date-order --all
*   287a3c8 2021-06-04 Merge remote-tracking branch 'origin/feature1.0-1' into release1.0  (HEAD -> release1.0) <elfcafe>
|\  
| | * 0250a19 2021-06-04 team2 add file4  (origin/feature1.0-2) <team2>
| * | 5f234c5 2021-06-04 team1 update file3  (origin/feature1.0-1) <team1>
| | * 9d3f9ed 2021-06-04 team2 upddte file2  <team2>
| |/  
|/|   
| * 157fe9c 2021-06-04 team1 update file1  <team1>
|/  
* ae83520 2021-06-04 init commit  (origin/release1.0, origin/master, origin/HEAD) <elfcafe>

如果release1.0需要只保留feature1.0-2(剔除feature1.0-1),那麼是不是直接reset到HEADˆˆ就好了呢?我們來試試看:

hackun$ git reset HEAD^^ --hard

bogon:release1.0_noff hackun$ git lg
* 0250a19 2021-06-04 team2 add file4  (origin/feature1.0-2) <team2>
| * 5f234c5 2021-06-04 team1 update file3  (origin/feature1.0-1) <team1>
* | 9d3f9ed 2021-06-04 team2 upddte file2  <team2>
| * 157fe9c 2021-06-04 team1 update file1  <team1>
|/  
* ae83520 2021-06-04 init commit  (HEAD -> release1.0, origin/release1.0, origin/master, origin/HEAD) <elfcafe>

可以看到release直接回退到了ae83520初始commit,team1和team2的提交都被無情退回了,顯然結果並不完全符合我們的預期。這時候我們需要重新合併一次team2的feature1.0-2分支。

hackun$ git merge -no-ff origin/feature1.0-2
error: did you mean `--no-ff` (with two dashes ?)
MacBookProEvo:release1.0_noff hackun$ git merge --no-ff origin/feature1.0-2
Merge made by the 'recursive' strategy.
 file2 | 1 +
 file4 | 0
 2 files changed, 1 insertion(+)
 create mode 100644 file4
 
hackun$ git lg
*   1bc9be6 2021-06-08 Merge remote-tracking branch 'origin/feature1.0-2' into release1.0  (HEAD -> release1.0) <elfcafe>
|\  
| * 0250a19 2021-06-04 team2 add file4  (origin/feature1.0-2) <team2>
| | * 5f234c5 2021-06-04 team1 update file3  (origin/feature1.0-1) <team1>
| * | 9d3f9ed 2021-06-04 team2 upddte file2  <team2>
|/ /  
| * 157fe9c 2021-06-04 team1 update file1  <team1>
|/  
* ae83520 2021-06-04 init commit  (origin/release1.0, origin/master, origin/HEAD) <elfcafe>

己經push

如果release1.0的兩次合併請求(--no-ff方式)已經push到了remote。

這時候如果需要只保留feature1.0-1的分支特性(剔除team2開發feature1.0-2)就需要revert對應的merge-c00569

MacBookProEvo:release1.0_noff hackun$ git show
commit c00569074fab8629823b3382411f9d2ddc231bc7 (HEAD -> release1.0, origin/release1.0)
Merge: 287a3c8 0250a19
Author: elfcafe <[email protected]>
Date:   Mon Jun 7 16:42:44 2021 +0800

    Merge remote-tracking branch 'origin/feature1.0-2' into release1.0

......

MacBookProEvo:release1.0_noff hackun$ git revert HEAD -m 1
[release1.0 f9ef0bb] Revert "Merge remote-tracking branch 'origin/feature1.0-2' into release1.0"
 2 files changed, 1 deletion(-)
 delete mode 100644 file4
MacBookProEvo:release1.0_noff hackun$ git lg
* f9ef0bb 2021-06-08 Revert "Merge remote-tracking branch 'origin/feature1.0-2' into release1.0"  (HEAD -> release1.0) <elfcafe>
*   c005690 2021-06-07 Merge remote-tracking branch 'origin/feature1.0-2' into release1.0  (origin/release1.0) <elfcafe>
|\  
* \   287a3c8 2021-06-04 Merge remote-tracking branch 'origin/feature1.0-1' into release1.0  <elfcafe>
|\ \  
| | * 0250a19 2021-06-04 team2 add file4  (origin/feature1.0-2) <team2>
| * | 5f234c5 2021-06-04 team1 update file3  (origin/feature1.0-1) <team1>
| | * 9d3f9ed 2021-06-04 team2 upddte file2  <team2>
| |/  
|/|   
| * 157fe9c 2021-06-04 team1 update file1  <team1>
|/  
* ae83520 2021-06-04 init commit  (origin/master, origin/HEAD) <elfcafe>

對比當前版本與預期版本差異,no difference

hackun$ git diff HEAD 287a3c

如果需要只保留feature1.0-2的分支特性(剔除team1開發feature1.0-1)就需要revert對應的merge

hackun$ git revert 287a3c8 -m 1
[release1.0 4c0e9ae] Revert "Merge remote-tracking branch 'origin/feature1.0-1' into release1.0"
 2 files changed, 2 deletions(-)
MacBookProEvo:release1.0_noff hackun$ git lg
* 4c0e9ae 2021-06-17 Revert "Merge remote-tracking branch 'origin/feature1.0-1' into release1.0"  (HEAD -> release1.0) <elfcafe>
*   c005690 2021-06-07 Merge remote-tracking branch 'origin/feature1.0-2' into release1.0  <elfcafe>
|\  
* \   287a3c8 2021-06-04 Merge remote-tracking branch 'origin/feature1.0-1' into release1.0  <elfcafe>
|\ \  
| | * 0250a19 2021-06-04 team2 add file4  (origin/feature1.0-2) <team2>
| * | 5f234c5 2021-06-04 team1 update file3  (origin/feature1.0-1) <team1>
| | * 9d3f9ed 2021-06-04 team2 upddte file2  <team2>
| |/  
|/|   
| * 157fe9c 2021-06-04 team1 update file1  <team1>
|/  
* ae83520 2021-06-04 init commit  (origin/release1.0, origin/master, origin/HEAD) <elfcafe>

比較當前版本與預期版本差異 nodifference

hackun$ git diff 4c0e9ae 0250a19