天天看點

git入門四(分支建立合并)

熟悉git分支的原理是掌握了git的精髓,因為git和我們常用的源碼管理系統有很大的差別和優點在分支上可以展現出來,一般我們常用的源碼管理系統分支都是需要建立新目錄,有全新的源碼copy,一般都需要建立一個源代碼目錄完整的副本。對應大項目來說非常的耗費時間和空間。git正式因為其優秀的分支模式可以從源碼管理系統中脫穎而出。因為git的分支非常的輕量級,他的操作機會瞬間完成,在不同的分支切換也非常快速。與其他版本相比,git更加推崇使用分支管理。分支是一個git非常強大和高效的工具。熟悉使用可以大大的提高工作效率和開發效率。

1、分支

git分支是如果存儲資料,我們通過git資料存儲的方式可以知道,存儲一些列檔案快照的方式。而一般常用的源碼管理軟體儲存的基于檔案版本差異的變化儲存資料。

在git送出中,會儲存一個送出(commit)對象,該對象包含一個指向暫存内容快照的指針,包括本次送出的作業的相關附屬資訊,包含零個或多個指向該送出對象的父對象指針,首次送出時沒有直接的祖先,普通送出有一個祖先,由兩個或多個分支合并産生的送出有多個祖先。

假如我們的工作目錄有三個檔案,準備将他們暫存後送出,暫存操作會對每一個檔案計算校驗和(Sha1哈希字元串)。然後把目前版本檔案快照儲存到git倉庫中。git使用blob類型對象存儲這些快照。并将校驗和加入暫存區。

當使用git commit 建立一個送出對象前,git會計算每一個子目錄或者項目的根目錄的校驗和,然後在git倉庫中将這些目錄儲存為(tree)樹對象。之後git建立的送出對象,除了包含相關送出資訊之外,還包含指向這個樹對象(項目根目錄)的指針。如此他就可以在将來需要的時候,重制此次快照的内容。

$ git add test1.txt test2.txt test3.txt

$ git commit -m " this is the firt commit"

[master (root-commit) 680e70a]  this is the firt commit

 3 files changed, 3 insertions(+), 0 deletions(-)

 create mode 100644 test1.txt

 create mode 100644 test2.txt

 create mode 100644 test3.txt

$ find .git/objects/ -type f

.git/objects/15/f2ed7f3d000ee95bf0e343f40eb892f622174d

.git/objects/30/758feb48e8bdba6ace1ecadf7bf2f89b94c115

.git/objects/5b/3c0c1c42386a00a8f81854b84628305f7082c7

.git/objects/68/0e70ac4e4376992c7d90905b8351badd233850

.git/objects/97/22bcfd0dcf84bbc82ce6095055010c011f91e6

以上是我剛剛建立的一個空的倉庫,添加test1.txt test2.txt test3.txt 三個檔案。添加到暫存區後,在。git/objects/ 對象中會新增加三個檔案快照blog 對象。暫存.git/index 會指向對應的對象。首次送出到倉庫後,倉庫中在.git/objecs/目錄中有5個對象。比在暫存區中增加了兩個對象,是因為一個是根目錄樹(tree)對象。和一個送出commit 的對象。根目錄tree對象指向三個檔案blob的索引對象和儲存檔案内容的名稱。而commit 對象會指向樹對象,同時儲存對象說明及相關的附屬資訊。倉庫對象之間的關系圖可以如下圖。

git入門四(分支建立合并)

當連續多次提及後,每次建立一個送出commit 對象會指向上一個送出的父對象。已經送出了三個對象,可以參照如下圖:

$ git log --oneline

4d38d40 the third commit

66a1a2e the secomd commit

680e70a  this is the firt commit

git入門四(分支建立合并)

2、 建立分支

git bran branchname  這個指令會在目前活動分支的基礎上建立一個新的分支。其實是在目前活動分支對象commit 新添加一個指向該對象的分支指針,如我們在現master 分支的基礎上建立一個testing 分支。git branch testing 建立分支如下關系圖:

git入門四(分支建立合并)

git 目前活動分支,即工作分支是是哪個呢,可以通過指令git branch 檢視,*号标示的就是目前活動分支。有一個head 特别指針儲存着目前活動分支。具體實體位址是儲存在.git/head 檔案中,他指向的是一個分支名稱,分支明确refs/heads/master  指向的是目前分支指向的commit 對象。他是指向目前工作本地活動分支的指針。看如下指令,我麼可以通過git checkout branchname 轉換分支。如git checkout testing 後就指向了testing 分支。看如下圖轉變過程。

$ git branch

* master

  testing

$ ls .git/refs/heads

master  testing

$ cat .git/refs/heads/master

4d38d402b3873d95c03cb3e1d2c65bf9aa3b5894

$ cat .git/refs/heads/testing

$ cat .git/head

ref: refs/heads/master

$ git checkout testing

Switched to branch 'testing'

ref: refs/heads/testing

git入門四(分支建立合并)

目前活動分支是在testing 分支,我們在testing 分支做修改後送出。這是testing 分支指向最近一個送出。master 分支是指向目前送出的父送出commit 對象。

現在我們切換到master 分支,并把工作目錄檔案換成master分支指向的快照内容,現在所做的修改時在老的版本上做修改,在master 分支上做修改。修改不同餘testing 分支另一條分支送出。如下圖結果。

$ git commit -a -m "testing branch change"

[testing 0c8f2de] testing branch change

 1 files changed, 1 insertions(+), 0 deletions(-)

$ git checkout master

Switched to branch 'master'

$ git commit -a -m "the master branch commit "

[master 1d37642] the master branch commit

 1 files changed, 2 insertions(+), 0 deletions(-)

如現在我們需要把testing 分支和 master 分支合并。合并操作指令git merge branch name ,檢視分支日志。

$ git merge testing

Merge made by the 'recursive' strategy.

 test2.txt |    1 +

$ git log --oneline --graph

*   ac631f6 Merge branch 'testing'

|\

| * 0c8f2de testing branch change

* | 1d37642 the master branch commit

|/

* 4d38d40 the third commit

* 66a1a2e the secomd commit

* 680e70a  this is the firt commit

git入門四(分支建立合并)
git入門四(分支建立合并)

由于分支實際上僅僅是一個包含所指對象校驗和(40個字元串長度的sha1字元串)檔案。是以建立和銷毀一個分支會非常的容易和廉價。實際上建立一個分支就是向一個檔案寫入41個位元組(一個換行符)。指向一某個送出點。

正式由于這個特性和其它版本形成了特别鮮明的對比。原始的源碼管理系統管理分支一般都采用備份所有項目檔案到特定的目錄,是以根據項目檔案數量和大小不同,消耗的時間随着項目的大小時間也不同。而gti 無論項目大小不同,他的特性與項目的複雜度無關,永遠可以在幾毫秒内建立分支和切換分支。同時每次回記錄和指向父對象。這位将來要合并對象對參照标準基礎。

3、分支合并建立執行個體模型

    分支建立和合并的例子,實際工作中工作流程

    1、開發一個項目

    2、為顯示某個新的需求,建立一個新的分支。

    3、在這個分支的開發

    4、如果此時項目有bug需要立即修複需要在原先釋出到生産環境的版本的分支。

    5、為這次緊急bugfix 建立一個新的分支,并在其中修複問題。

    6、bug修複沒問題後,會到開發伺服器生産分支,和修補分支合并,然後推送到生産伺服器上。

    7、所有一切都完成後,切換到之前新需求分支,繼續工作。

   接着剛剛寫的範例,我們在主分支釋出到生産環境後,已經推送到中心伺服器,目前的狀态如下圖:

git入門四(分支建立合并)

   1、現在有新的需求需要開發,是以在當地master 分支上建立一個新的開發分支。然後再開發分支上做開發。用到的指令式git checkout -b dev ,相當于git branch dev ,git checkout dev 兩條指令。目前工作活動分支是指向dev開發分支,我們在新的開發分支上做修改送出後。如下圖:

$ git checkout -b dev

Switched to a new branch 'dev'

$ git commit -a -m "dev branch revise"

[dev 46324fb] dev branch revise

git入門四(分支建立合并)

現在正式的生産環境發現有bug 需要修複,現在我們不需要在建立和釋出到修複伺服器上花大力氣做修改,現在需要做的隻需要切回到master分支,然後再master 的分支上建立bugfix分支。在分支切回過程有一點需要注意的是。在暫存區和工作目錄是否還沒有送出的修改,如果他和你即将檢出的分支産生沖突進而阻止切回分支。在切換分支前需要保持一個幹淨的工作區域。

$ git checkout -b bugfix

Switched to a new branch 'bugfix'

$ git commit -a -m "bug fix branch commit"

[bugfix 7599941] bug fix branch commit

git入門四(分支建立合并)

在bugfix 分支上測試成功後,發現已經ok了,需要把master分支合并進來,然後推送到中心倉庫伺服器。先切換成到master 分支,然後git merge 合并bugfix 分支做合并,你會發現fast-forfward 合并,git隻需要把master分支指針直接右移,如果順着一個分支走下去可以到達另一個分支,那麼在git 合并的時候,隻會簡單的把指針右移,因為這種單線曆史分支不存在解決沖突和分歧。是以這種合并過程成為快進(fast forward). 當master 分支和bugfix 分支合并後,見下圖,目前的bugfix 分支和master 分支會指向一個相同的送出對象。如果bugfix 分支已經沒有存在的必要了,可以使用git branch -d branch 進行删除。

$ git merge bugfix

Updating ac631f6..7599941

Fast-forward

 test1.txt |    2 ++

$ git log --graph --oneline

* 7599941 bug fix branch commit

$ git branch -d bugfix

Deleted branch bugfix (was 7599941).

git入門四(分支建立合并)

以上bug修複完成後,可以切換到dev 分支,繼續需求開發,可以将master 分支合并到dev 分支,或者等dev 版本開發完成後,再将dev分支更新到并入master.現在分支不同于hotfix 合并的方式,因為這次從曆史ac631 處有分支同父對象,dev 和master 分支在進行合并的時候,會找到相同的祖先分支。ac631 和最新分支末端 46324fb 和 22ef87 進行一個三方合并運算,合并送出三個對象,git沒有簡單的把分支指正右移,而是三方合并結果做一個新的快照建立一個送出commit 對象1421fd5 。這個送出對象有兩個祖先,有一點需要注意的是,git 可以決定哪個祖先是最佳合并的基礎,這和CVS 和 SVN 不同,他們需要開發者手工合并基礎,是以此特性合并操作會讓git操作會比其它系統要簡單許多。

$ git merge dev

*   1421fd5 Merge branch 'dev'

| * 46324fb dev branch revise

* | 22ef787  before dev merege commit

* | 7599941 bug fix branch commit

git入門四(分支建立合并)