Git應用詳解第三講:本地分支的重要操作


前言

前情提要:Git應用詳解第二講:Git刪除、修改、撤銷操作

分支是git最核心的操作之一,了解分支的基本操作能夠大大提高項目開發的效率。這一講就來介紹一些分支的常見操作及其基本原理。

一、分支概述

在開發當中,往往需要分工合作。比如:小紅開發A功能,小明開發B功能,小剛開發C功能。如何才能做到三者並行開發呢?git為我們提供的分支功能就能實現這一需求,如下圖所示:

image-20200413193720239

在實際的開發過程中,master分支是用來發布項目穩定版本的。新的功能往往是在一個新建的分支上進行開發,等到新功能開發完畢並經過測試,表現穩定后,才會合並到master分支上進行版本更新。這樣就可以在保持一款軟件發行的同時,同步進行新功能的開發。

通常來說,遠程倉庫的Git分支會有如下幾種:master分支、test分支、develop分支,除此之外可能還有緊急修復bughotfix分支;但是,本地的分支可以有很多;本文主要介紹Git本地分支的內容。

二、查看本地分支

1.git branch

查看當前版本庫中的所有分支:

image-20200310151203938

其中的 * 表示當前處於的分支,可見當前處於master分支;

使用git init初始化git倉庫時,git會自動創建一個master分支。但是,如果沒有在master分支上進行任何提交就切換到其他分支,那么在切換分支的時候master分支會被銷毀。並且,無法查看沒有提交記錄的分支,如下圖所示:

image-20200413115148907

2.git branch -a

查看所有本地分支,包括本地分支和本地遠程分支:

image-20200328164045256

3.git branch -v

查看所有本地分支上最近一次的提交記錄:

image-20200413122049091

但是,該指令無法查看本地遠程分支:

image-20200413123220561

4.git branch -r

-r參數用於單獨查看本地遠程分支:

image-20200413174143173

5.git branch -av

該指令不僅可以顯示所有的本地分支,包括本地遠程分支,以及對應分支上的最新提交信息:

image-20200409115958334

6.git branch -vv

-vv參數表示查看所有本地分支與遠程分支的關聯情況。如圖所示,本地master分支有本地遠程分支origin/master與之關聯,說明它已與遠程master分支建立了關聯;

image-20200410121145929

至於上面提到的本地遠程分支,將在下一講中詳細介紹。

三、創建本地分支

1.git branch <branch_name>

可通過上述命令創建新分支new_branch

image-20200310151357642

由於是在master分支上創建的new_branch分支,所以new_branch分支master分支有着部分共同的提交歷史;所以,master分支上的文件,new_branch分支上都有。但是,在new_branch分支上添加的new_branch文件,不會存在於master分支上:

image-20200413120006856

此時兩分支的狀態為:

image-20200413120509219

2.git branch -b <branch_name>

通過上述命令可創建並切換到new_branch分支:

image-20200413120945856

如圖所示,本來所在分支為master,並且沒有new_branch分支。執行上述命令后,創建並切換到了new_branch分支上。

四、切換本地分支

1.git checkout <branch_name>

比如切換到new_branch分支:

image-20200310151539203

2.git checkout -

切換回上次操作的分支:

image-20200310152313013

五、重命名本地分支

1.git branch -m <oldName> <newName>

如下圖所示,將本地分支master重命名為master2

image-20200311115657966

六、刪除本地分支

1.git branch -d new_branch

刪除new_branch分支:

image-20200310163652841

注意點:

  • 不能刪除當前所處的分支;

  • 當需要刪除的分支上有master分支沒有的內容,並且刪除前沒有進行合並(merge)時,會報錯:

image-20200310163934006

此時可以通過git branch -D new_branch 使用參數D,在不合並的情況下強制刪除分支

七、合並分支

注意:這里所講的分支指的是有公共提交節點的分支,如下圖中的devmaster分支所示,提交節點A為它們的公共提交節點:

image-20200411210604441

當兩分支沒有公共提交節點,如下圖所示,應采用rebase進行合並,后面會詳細介紹:

image-20200411210330211

1.git merge <branch_name>

  • 首先,創建並切換到新分支dev中,並為test.txt文件添加內容dev1

    image-20200413213753822

    注意:要將dev分支上的這一修改提交到版本庫,才能進行后續合並。因為合並的是提交對象鏈,詳情見后面講解的合並原理:

    image-20200413124513814

  • 然后,切換回master分支,通過git merge dev指令,將dev分支中的內容合並到當前所處master分支中;合並后master分支與dev分支上test.txt文件的內容達到了同步:

    image-20200413213934629

2.分支合並的原則

git分支的合並采用的是三方合並的原則:找到兩分支最新提交AB的公共父節點C,在這三個節點的基礎上合並為節點D。這個節點D就包含了兩個分支上的所有內容:

image-20200407160615973

八、分支的本質

分支:指向一條commit對象鏈或一條工作記錄線的指針;

快照A~D分別表示四次提交(commit),注意提交的順序為:A -> B -> C -> D

image-20200408150401090

從圖中可以看到每一次提交的對象內都會保存上一次提交的commit id,由此可以從后往前把所有的提交(commit)串起來形成一條(類似單向鏈表),這條鏈就組成了一條完整的分支信息

  • 當版本庫中只有一條分支:該分支的最新提交就包含了整條分支的所有內容,代表版本庫的當前狀態。如上圖的快照D,里面包含了快照A~C中的所有內容,此時快照D中的內容就是整個版本庫中的內容:

    image-20200413132342942

  • 當版本庫中有多條分支:每條分支上的最新提交包含了所處分支的全部內容,將各個分支的最新提交進行合並。合並的節點就包含了所有分支的內容,也就是現階段的版本庫本身;如下圖中的d1m2t3分別包含了devmastertest分支上的所有內容:

    image-20200413132409178

1.分支 == 指針

情景一:

image-20200406131416304

從圖中可以看到:

  • HEAD為一個指針:指向當前分支;

  • master也為一指針:指向提交;

情景二:

image-20200406131503784

上圖中,devmaster分支上創建的新分支,可知:

  • git在創建新分支時,文件本身不變化,只需要創建一個代表新分支,並指向當前分支的指針;如圖中的devmaster指向同一個提交,文件沒有發生任何變化;
  • HEAD指向dev分支,表示當前所處分支為dev,相當於執行了:git checkout -b dev 后的狀態;

相信你已經發現:HEAD是一個始終指向當前分支指針

2.HEAD標識符

HEAD文件是一個指向當前所在分支的引用標識符,也可以理解為一個指針,它與分支之間的關系是這樣的:

image-20200408000947960

查看HEAD

HEAD文件中並不包含SHA1值(每次提交的commit ID),而是包含一個指向另外一個引用的指針。我們可以查看.git目錄下的HEAD文件:

image-20200310172919110

可見HEAD指向的是當前所在的master分支;

當我們通過git checkout -b dev 創建並切換到dev分支后,再次進入.git文件夾查看HEAD,會發現此時HEAD指向了dev

image-20200310173205214

由此證明了HEAD始終指向當前分支;

當執行git commit命令時,git會創建一個commit對象(比如下圖D)。並且將這個commit對象的parent指針指向HEAD 所指向的引用(master)指向的提交(也就是C),這樣就能形成一條提交鏈:

image-20200408001856063

我們對於HEAD修改的任何操作,都會被git reflog完整記錄下來:

image-20200329145707276

但是手動地修改HEAD文件,這些信息就不會被記錄下來,所以十分不建議手動修改git相關的配置文件,而是應該盡量采用命令行的方式來修改。

修改HEAD

實際上,我們可以通過git的底層命令symbolic-ref來實現對HEAD文件內容的修改;

git 中的命令可分為兩類:高級命令底層命令;之前介紹的git add 等都是高級命令;

讀取:

image-20200329150120113

寫入:

image-20200329150239094

要注意格式:refs/heads/develop

查看ORIG_HEAD文件:

image-20200329150514036

里面是一個SHA1值,查看當前的提交信息:

image-20200329150643422

可以發現,ORIG_HEAD里面的SHA1值就是最新一次提交的SHA1值。

查看FETCH_HEAD文件:

image-20200329150904196

里面有兩個信息,一個是最新提交的commit ID,另一個是提交信息。

所以,對於git而言commit ID是十分重要的信息,通過這個SHA1值可以回溯或查找需要的提交。

3.git merge原理

過程圖解
  • 在新分支上進行提交操作

    image-20200406131605850

    上圖表示在dev分支上進行了一次提交,此時:

    • 分支master的提交記錄由:ABC組成;
    • 而分支dev的提交歷史則由:ABCD組成;
  • 對兩分支進行合並操作

    image-20200406131708763

    master分支上執行:git merge devdev分支的內容合並到了master分支上;這種合並方式叫做:Fast-forward沒有沖突,改變的只是master指針的指向;

    image-20200310180632403

詳細過程

在執行合並操作前:

  • master分支上查看該分支的提交記錄:

image-20200413135649151

  • dev分支上查看該分支的提交記錄:

image-20200413135632944

可以看到dev分支只是比master分支多進行了一次提交(dev1),兩分支狀態如下圖所示:

image-20200413140621228

執行合並操作:

先切換到master分支,然后執行git merge dev合並dev分支:

image-20200413135914872

可以看到使用了Fast-forward方式進行合並,合並后兩分支狀態如下圖所示:

image-20200413140634456

合並后,HEAD同時指向了masterdev分支;並且masterdev分支的提交歷史完全一致;這就說明了:使用Fast-forward(快進合並)方式進行分支合並,只會改變master分支指針的指向;

4.Fast-forward

  • 默認情況下,合並分支時git會使用Fast-forward模式;
  • 在這種模式下,刪除分支會丟棄分支信息;
  • 進行分支合並操作時加上--no-ff 參數會禁止使用Fast-forward方式,這樣會多出一次提交記錄;

ff表示Fast-forward

具體演示如下:

使用Fast-forward

首先,查看master分支上最新的3次提交:

image-20200413140911195

此時兩分支的狀態為:

image-20200413141208043

隨后在dev分支上新增一次提交:dev2。查看dev分支上最新的3次提交:

image-20200413141111031

此時兩個分支的狀態為:

image-20200413141246153

切換回master分支,通過git merge dev合並dev分支,此時默認采用Fast-forward方式:

image-20200413141412571

可以看到合並后,master直接指向了dev的最新提交,並沒有產生新的提交。合並后兩分支的狀態如下所示:

image-20200413141554807

由此驗證了Fast-forward方式只會改變分支指針的指向。

禁用Fast-forward

合並時可以通過:

git merge --no-ff dev

禁用Fast-forward模式。

  • 繼續在dev分支新增一次提交:dev3。然后查看dev分支上最新的3次提交:

    image-20200413142011002

  • 不修改master分支,查看其最新的3次提交:

    image-20200413142116641

    此時兩個分支的狀態為:

    image-20200413142145430

  • 然后,在master分支上不使用Fast-forward方式合並dev分支。合並命令采用:

    git merge --no-ff dev
    

    執行后進入如下的vim編輯器界面,要求我們填寫提交注釋:

    image-20200413142507135

這說明不使用Fast-forward方式合並分支,會觸發了一次提交。填寫提交注釋后完成提交操作,合並完成后,查看master分支的提交記錄:

image-20200413142907282

可以發現禁用了Fast-forward模式的合並會比dev分支多產生一次提交:Merge branch 'dev',即使合並后的內容是一樣的。此時兩分支的狀態為:

image-20200413144533217

由此驗證了,禁用Fast-forward方式合並,會多出一個表示合並的提交記錄。

5.合並沖突

合並的兩分支只有一條分支發生了改變,並且其中一分支是基於另一分支創建的。比如上述的masterdev分支,兩分支沒有分岔,此時不會出現合並沖突;git會通過Fast-forward方式自動完成合並操作;

但是,當合並的兩分支都發生改變時,即分支出現分岔,如下圖所示。此時就需要解決沖突后手動合並分支了:

image-20200413145058222

具體演示如下:

合並前

首先,分別對兩分支上的test.txt文件進行修改,並分別將修改提交到各自的分支;

  • master分支上進行新的提交:mas3,然后查看文件test.txt內容和分支提交記錄:

image-20200413150045837

  • dev分支上進行新的提交:dev1,然后查看文件test.txt內容和分支提交記錄:

image-20200413150139932

可見兩分支的提交記錄只有最新一次提交不一樣:

image-20200413153145295

合並后

master分支上,通過git merge dev合並dev分支時,會在共同修改的test.txt文件中出現合並沖突,如下圖所示:

image-20200413150334009

出現沖突的原因為:兩個分支都對同一個文件test.txt進行了修改,git合並時並不知道以哪個分支的修改為標准。所以不能采用Fast-forward方式自動合並,需要解決沖突,手動合並。

手動合並過程

手動合並操作需要分如下三步進行:

  • 第一步:選擇需要保留的內容,手動解決合並沖突;

此時進入發生合並沖突的test.txt文件:

image-20200413150400943

會出現典型的沖突呈現方式(此時HEAD指向的是master分支),其中:

  • <<<HEAD>>>dev之間的內容表示:兩分支上test.txt文件的不同之處;

  • <<<HEAD===之間的內容表示:當前分支mastertest.txt文件的修改;

  • ===>>>dev之間的內容表示:dev分支對test.txt文件的修改;

此時只需要在test.txt中保留想要的內容即可,例如:將兩分支對test.txt的修改都進行保存,刪除3、5、7行多余的內容:

image-20200413150810935

除此之外,還可以通過git mergetool指令,調用vimdiff工具進入vim編輯器,來解決test.txt文件的沖突:

image-20200413212456463

image-20200413212331710

在實際開發中,我們很少手動進行合並,而是借助於一些工具來實現。

  • 第二步:使用git add test.txt將手動解決沖突時對test.txt的修改提交到暫存區;

編輯完畢后,可以看到此時仍然處於合並過程中(MERGING)。通過git status 查看狀態,發現手動解決沖突時對test.txt文件的修改操作還在工作區中,需要通過git add test.txt將這一修改納入暫存區,繼續進行合並:

image-20200413151548133

  • 第三步:通過git commit -m '合並注釋'將手動解決沖突時對test.txt的修改進行提交,完成合並操作;

如果需要填寫較多的合並注釋,可以通過git commit進入vim編輯器編輯。提交后,完成整個手動合並過程。

image-20200310185125770

完成手動合並分支后,查看兩分支的提交歷史:

  • master分支上的提交歷史:

image-20200413152813941

  • dev分支上的提交歷史:

image-20200413152928725

可以發現此時兩分支轉變為了可以通過Fast forward方式合並的形式了,如圖所示:

  • 手動解決沖突前:

image-20200413153154756

  • 手動解決沖突后:

    image-20200413153427160

同步兩分支

若想將devmaster分支上的內容進行同步,只需要在dev分支中通過git merge master 合並master分支即可。此時就可以使用Fast-forward方式進行合並了,合並結果如下圖所示:

image-20200413153555178

驗證:

合並后,查看dev分支的提交歷史:

image-20200413154132053

可以看到HEAD同時指向devmaster,即三個指針都指向了最新的一次提交,符合上述分析得出的結論;

經過上面的討論,不難看出:合並分支的實質就是不同提交的合並,以及HEAD和分支指針的移動;

以上就是今天介紹的本地分支重要操作,相信看到這里的你已經對git本地分支的操作了如指掌了。在下一講中將介紹git最神奇的功能:版本回退。俗話說得好:世上沒有后悔葯。但是在git中,就存在所謂的"后悔葯"!那么我們下一節再見。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM