圖解git,用手繪圖帶你理解git中分支的原理和應用
大家好,今天我們來聊git當中一個非常非常重要的特性,就是branch。
git branch可以說是git當中最重要的概念了,甚至沒有之一。因為git最重要的使用場景就是協同開發,大家一起在一個專案當中開發不同的功能。正是由於有了分支的概念,可以讓大家在開發的時候互不影響。如果沒有這個功能,git的其他功能做的再好,可能都沒有用。
所以某種程度上可以理解為,學git最重要的就是學習分支的相關內容。當然分支的相關內容和命令非常多,我們想要瞬間全部都學會顯然不太現實。但對這個概念有一些理解,懂得一些基本命令的用法應該還是做得到的。
在理解分支這個概念之前,我們需要先來介紹一下Git的結構。
Git的結構
branch的英文就是樹枝,後來衍生出了分叉、支路等意思。這個單詞非常形象,因為git倉庫的所有提交節點之間的關係,其實就是一棵樹,所以一個分支也可以看成是樹上的一條鏈路。但是這樣有一個小問題是,如果說分支是一條鏈路的話,那麼這個鏈路上的每一個節點代表的是一個commit提交,意味著一份程式碼快照,有些像是遊戲存檔。
一個branch上有多個commit,一個遊戲也可以有多個存檔,但是當下顯然只能載入一個。所以git當中用一個指標指向當前載入的commit,也就是說縱向來看一個分支代表的是一連串的提交,但在git當中我們使用的分支其實是一個指標,一個在commit當中切換的指標。
我們來看個例子,比如一開始的時候我們只有預設分支master,它指向當前的一個提交。
現在我們使用git branch test命令建立一個測試分支,執行之後,其實只不過是多了一個指標也指向當前的commit。git當中的結構變成這樣:
當我們在test分支上做了改動提交之後,git會產生一個新的提交,並且移動test指標,而master指標會留在原地。
如果我們再回到master也進行了改動和提交之後,又會產生新的節點,並且這個節點會和test的節點區分開,形成新的鏈路,於是就形成了一棵樹的樣子。
分支與HEAD指標
怎麼分支建立,我們剛才已經講過了,可以通過git branch加上我們想要的分支名來完成。使用了這個命令之後,git內部會建立一個新的指標指向當前的commit。
有一個問題是git怎麼知道我們當前的程式碼在哪裡呢?即使知道了程式碼在哪個分支上,又怎麼確定在哪一個節點呢?其實git內部還有一個特殊的指標叫做HEAD,它指向的是當前程式碼倉庫的位置。當我們提交程式碼的時候,不止只有分支的這些指標會往前移動,HEAD指標也會隨著移動。
其實HEAD指標不僅可以往前移動,還可以移動到任意節點上,哪怕不再當前的分支上也可以。移動HEAD指標需要用到git checkout命令,它可以指定HEAD指標移動到其他位置。既可以是某一個分支,也可以是根據commit id來確定的節點。
比如我們當前在master分支,我們要切換到test分支上,我們只需要執行:
git checkout master
另外,使用git checkout命令加上引數-b,我們還可以建立分支。比如假如當前test分支不存在,我們可以通過git checkout -b test來建立,並且還會自動切換到新建出的test分支上。
我們在新的分支上做了提交之後,可以通過git log --oneline --decorate命令來檢視每一個分支所指向的位置。這裡的oneline是將log壓縮到一行展示,decorate用來檢視分支所指向的位置。
我們可以發現test和master分支指向的提交不同,並且當前我們的HEAD在test上,說明我們當前在test分支。我們前面說了git checkout命令可以改變HEAD指標指向的位置,假如我們在當前目錄下執行git checkout 18a417,這個18a417對應的是add article 6這個提交。這個提交是在master分支的,是test分支的上游,我們使用命令會自動將HEAD跳轉到master分支。
使用之後我們發現的確到了master分支,這裡由於我配置了zsh工具,它會提示我當前所處的位置是比master分支指向的最新位置落後3個提交。
這也驗證了我們說的,HEAD指標可以隨意跳轉。現在想必你們應該能理解上一篇文章當中介紹的,撤銷當前分支的命令git reset HEAD^的含義了,HEAD指的就是HEAD指標,^表示的上一個提交。如果是前多個提交,我們可以用~加數字的形式來表示。比如上圖當中劃了紅線標註的master~3,就表示master節點上3個提交。
分支合併
最後來簡單說說分支合併,我們在使用git進行協同開發的過程當中,雖然大家都在各自的分支。但是最後程式碼還是要合併到一起的,這樣才可以投入使用。git當中程式碼的合併是通過分支合併來體現的。
比如當前的這一篇文章被我加在了test分支當中,這顯然是不行的,因為使用方不可能一一去理解每一個分支做了什麼,當中的程式碼邏輯。所以大多數的分支只是暫時的,用來暫時完成一項功能的,等功能完成之後,一般都會再合併回master分支,將所有的改動合併進去。
合併的方式非常簡單,我們只需要先checkout我們想要合併的目標分支。比如我們要合併到master,就checkout到master。然後使用git merge test命令,表示和test這個分支合併。
合併之後,如果沒有報錯就算是合併成功了。它會展示出來合併進來的程式碼改動,我們注意到日誌裡有一個fast-forward這個單詞,它表示快速合併。快速合併的意思也很簡單,因為我們test分支是從master分支當中切出去的。後來master分支就再也沒有進行過改動,那麼當我們合併的時候,其實只需要移動一下master指標,將它移動到test分支上即可。
我們用圖來展示,合併前:
合併後:
那如果我們在master分支上也有改動,不再是待合併分支的直接上游,會發生什麼呢?
上圖當中我們做了一系列操作,首先我們建立了一個叫做test_merge的分支,在其中建立了一個檔案叫做a.txt,接著我們切回master分支建立了b.txt。最後我們把兩個分支合併。
合併當然也沒有問題,但是我們來用git log來檢視一下日誌:
會發現日誌裡多了一個commit,這個commit並不是我提交的,而是它自動產生的。我們一樣用圖來展示一下,這是合併前:
合併之後:
由於不再擁有直接上下游關係了,所以git建立了一個新的commit用來合併兩個分支的程式碼。當我們合併完成之後,我們就可以把沒用的分支都刪除了。刪除的命令是git branch -d test。
當然git merge的時候並不是永遠都一帆風順的,難免會遇到衝突。所謂的衝突也就是兩個人修改了同一份程式碼,git會不知道應該保留哪一個,於是提示衝突,讓程式設計師自己搞定。關於git merge時遇到衝突怎麼辦的問題,我們放到下一篇文章當中和大家分享。
今天的文章就到這裡,衷心祝願大家每天都有所收穫。如果還喜歡今天的內容的話,請來一個三連支援吧~(點贊、關注、轉發)
原文連結,求個關注
本文使用 mdnice 排版
- END -