GIT學習筆記(3):分支管理
何謂分支
GIT是如何存儲數據的
GIT不是存儲文件差異或者變化量,而是一系列文件的快照。在Git提交時,會保存一個提交(commit)對象,該對象包含一個指向暫存內容快照的指針,它大概是這樣子的。
三個表示文件快照內容的 blob 對象;一個記錄着目錄樹內容及其中各個文件對應 blob 對象索引的 tree 對象;以及一個包含指向 tree 對象(根目錄)的索引和其他提交信息元數據的 commit 對象。
多個提交對象之間是鏈接關系,每個提交對象會指向上一個提交對象,如下圖所示:
引入分支
Git 中的分支,其實本質上僅僅是個指向 commit 對象的可變指針。Git 會使用 master 作為分支的默認名字。在若干次提交后,你其實已經有了一個指向最后一次提交對象的 master 分支,它在每次提交的時候都會自動向前移動。
創建一個新分支
創建一個新的分支很簡單,比如新建一個testing分支,可以使用命令git branch testing,此時將會有兩個分支指向當前commit對象。
那么,Git 是如何知道你當前在哪個分支上工作的呢?其實答案也很簡單,它保存着一個名為 HEAD 的特別指針。它是一個指向你正在工作中的本地分支的指針(將 HEAD 想象為當前分支的別名。)。我們創建了新的分支,但是並不會自動切換到這個分支中去,所以我們依然在master分支中進行,我們可以使用git checkout testing命令,來轉換到新建的分支中去。
之后每次提交,HEAD將隨着分支一起向右移動,如下圖所示HEAD跟隨testing進行移動。
當然,我們可以使用checkout切換回matser分支,這樣再次提交就會產生不同的流向
分支的合並
通過上面內容,我們已經理解了分支的基本概念已經分支的新建切換步驟等等,下面我們來探討一下分支的合並。
分化分支的合並
如下圖所示,hotfix分支是master分支的分化分支,所謂分化,既是源於master,matser是其父級。
hotfix做了一些操作后,比如修復BUG,master認為hotfix修復了其關鍵故障,想合並hotfix分支,我們可以回到master,然后指向git merge hotfix命令。
此時發現master和hotfix指向了同一個commit對象。由於 master
分支所在的提交對象是要並入的 hotfix
分支的直接上游,Git 只需把 master
分支指針直接右移。換句話說,如果順着一個分支走下去可以到達另一個分支的話,那么 Git 在合並兩者時,只會簡單地把指針右移,因為這種單線的歷史分支不存在任何需要解決的分歧,所以這種合並過程可以稱為快進(Fast forward)。
這個時候,hotfix已經完成了歷史使命,可以刪掉了,使用git branch -d hotfix可以刪除不用的分支。
反思:看到這里我們可以想一下實際的業務場景,比如我們matser版本出現問題,我們可以先創建一個新的分支進行修改,如果修復成功了,master合並即可,即使沒有成功,也不會對master有任何影響。
基於共同祖先的合並
比如maste分支r想要合並iss53分支,由於當前 master
分支所指向的提交對象(C4)並不是 iss53
分支的直接祖先,Git 不得不進行一些額外處理。就此例而言,Git 會用兩個分支的末端(C4 和 C5)以及它們的共同祖先(C2)進行一次簡單的三方合並計算。
即如下圖所示:
這次,Git 沒有簡單地把分支指針右移,而是對三方合並后的結果重新做一個新的快照,並自動創建一個指向它的提交對象(C6)。
既然之前的工作成果已經合並到 master
了,那么 iss53
也就沒用了。你可以就此刪除它。
遇到沖突時的分支合並
有時候合並操作並不會如此順利。如果在不同的分支中都修改了同一個文件的同一部分,Git 就無法干凈地把兩者合到一起,比如我們合並master與tesing分支。
此時會出現問題,因為他檢測出來兩個分支都對Test.java進行了操作,他對Test.java進行了合並,但沒有提交,它會停下來等你解決沖突。打開Test.java文件,我們發現它自動的把兩者的內容放在一起,讓我們做出選擇
我們自行做出修改,然后運行git add將其表示為已經解決狀態,最后提交即可。
到這里分支管理的基礎內容已經講完了,關於遠程分支等內容我們將在下篇文章中詳細說明,謝謝關注!