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

前言

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

分支是git最核心的操做之一,瞭解分支的基本操做可以大大提升項目開發的效率。這一講就來介紹一些分支的常見操做及其基本原理。git

1、分支概述

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

image-20200413193720239

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

一般來講,遠程倉庫的Git分支會有以下幾種:master分支、test分支、develop分支,除此以外可能還有緊急修復bughotfix分支;可是,本地的分支能夠有不少;本文主要介紹Git本地分支的內容。編輯器

2、查看本地分支

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

查看全部本地分支上最近一次的提交記錄:code

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

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

3、建立本地分支

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分支上。

4、切換本地分支

1.git checkout <branch_name>

好比切換到new_branch分支:

image-20200310151539203

2.git checkout -

切換回上次操做的分支:

image-20200310152313013

5、重命名本地分支

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

以下圖所示,將本地分支master重命名爲master2

image-20200311115657966

6、刪除本地分支

1.git branch -d new_branch

刪除new_branch分支:

image-20200310163652841

注意點:

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

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

image-20200310163934006

此時能夠經過git branch -D new_branch 使用參數D,在不合並的狀況下強制刪除分支

7、合併分支

注意:這裏所講的分支指的是有公共提交節點的分支,以下圖中的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

8、分支的本質

分支:指向一條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-of 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的修改都進行保存,刪除三、五、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中,就存在所謂的"後悔藥"!那麼咱們下一節再見。

相關文章
相關標籤/搜索