git_chapter3_分支

Chapter3 分支

參考自 https://git-scm.com/book/zh/v1/Git-%E5%88%86%E6%94%AF

3.1 什么是分支

為了理解 Git 分支的實現方式,我們需要回顧一下 Git 是如何存儲數據的。Git 保存的不是文件差異或者變化量,而是一系列文件快照。

下面用一個實際例子來說明分支的實現方式:

  1. 首先,我們創建一個新的本地倉庫,并在工作目錄新增三個文件:

    $ mkdir branchtest
    $ cd branchtest
    $ git init
    $ git add file_001 file_002 file_003
    

    注意,這個時候倉庫中還沒有任何分支,不信你用 git branch --list 試一下,沒有輸出任何東西。

  2. 接下來我們將暫存區的文件提交到倉庫:

    git commit -m 'initial commit of my project'
    [master (root-commit) 2618748] initial commit of my project
     3 files changed, 3 insertions(+)
     create mode 100644 file_001
     create mode 100644 file_002
     create mode 100644 file_003
    
    Dongyu@Dongyu-PC MINGW64 /e/workspace/others/git/branchtest (master)
    $ git branch --list
    * master
    

    可以看到這個時候有了一個新的分支 master, 在這個過程中發生了什么呢?

    • 首先,在暫存操作(也就是 git add)中,Git 會計算每個文件的校驗和,然后把文件快照和校驗和打包到 blob 結構體中,再存儲到 倉庫里。暫存區并不存儲文件快照,只是存儲了文件校驗和
    • 然后,在提交操作(也就是 git commit)執行前,Git 會先計算每個子目錄的校驗和,然后把目錄和文件的校驗和一起打包到 tree 結構體中。
    • 最后,執行提交操作,Git 會將之前的 tree 結構體和作者信息等內容打包到 commit 結構體(提交對象)中,存儲到倉庫里。

    上述過程可以用下圖表示:


    單個提交對象在倉庫中的數據結構

    每次 git commit 操作都會將一個 commit 提交對象存儲到倉庫里。新存儲的 commit 會跟在之前 commit 對象的后面:

    多個提交對象之間的鏈接關系

  3. 提交對象鏈 與 分支:
    多次提交會讓 commit 對象連成一條提交鏈。而 Git 中的分支,本質上只是一個指向某個 commit 對象的可變指針。master 是分支的默認名字,每次你提交一個 commit 對象上去,master 指針就會向后移動,指向最新提交的 commit 對象:

    分支其實就是從某個提交對象往回看的歷史
  4. 新建一個分支:
    新建一個分支,實際上就是創建一個新的分支指針,這可以用 git branch 命令實現,比如 git branch testing 就會在當前 commit 對象上新建一個分支指針:

    多個分支指向提交數據的歷史
  5. 切換到新建分支上:
    Git 是如何知道你當前在那個分支上工作呢?很簡單,它保存著一個名為 HEAD 的特別指針。剛剛的 git branch 只是創建了一個新分支,并沒有切換到那個分支上,所以 HEAD 指針還是指向 master 分支:

    HEAD 指向 master 分支

    要切換到新建的 testing 分支,就得把 HEAD 指針移動到 testing 分支上,這可以用 git checkout testing 命令來實現:

    HEAD 指向 testing 分支

    注意 使用 git checkout 切換分支的同時,工作目錄中的文件也會被修改為 目標分支 的文件。如果工作目錄中有未提交的修改的話, git 是不允許你切換分支的。

  6. 在新建分支上提交:
    現在 master 和 testing 分支上的內容是完全一樣的,我們修改一下 工作目錄 中的文件,然后提交。下圖顯示了提交后的結果:

    每次提交后 HEAD 隨著分支一起向前移動
  7. 在原分支上提交:
    我們用 git checkout master 命令切換回原 master 分支。此時工作目錄會還原回 master 分支的狀態。做些修改后再次提交。下圖顯示了提交后的結果:

    不同流向的分支歷史

3.2 分支的新建與合并

例子

現在我們來看一個簡單的分支與合并的實際例子:

已經有了一個項目倉庫(master),接到了一個新需求,創建一個分支用來實現這個需求 (iss53)。

這個時候,你突然接到任務要在原項目中做一個緊急修復。那么可以按照如下步驟處理:

  1. 返回原有項目分支(master);
  2. 為這個緊急任務創建一個新分支,并在其中修復這個問題(hotfix);
  3. Bug 解完后,回到原有分支(master),把修補分支合并進來;
  4. 切換到 新需求分支(iss53),繼續工作。

好,我們來看一下每個步驟中分支的狀態:

  1. 原項目分支(master):


    原項目分支
  2. 接到新需求,創建一個分支(iss53):
    創建分支使用 git branch iss53 命令:

    創建新需求分支
  3. 在新需求分支上提交了一些代碼:


    經過提交的分支
  4. 接到解 Bug 任務,再創建一個解Bug分支(hotfix),并在其中了提交一些代碼:

    # 先回到 master
    git checkout master
    
    # 切換到 hotfix 工作
    git branch hotfix
    git checkout hotfix
    
    # 進行代碼提交
    git add ...
    git commit -m 'fix bug'
    
    經過提交的分支

    注意,創建 hotfix 之前要先回到 master 分支,不然的話創建的分支是基于 iss53 分支的。

  5. Bug 解完,回到原有分支(master), 把 hotfix 合并進來:

    # 先回到 master
    git checkout master
    
    # 進行分支合并
    git merge hotfix
    
    合并后的分支
  6. hotfix 用完了,刪除掉它

    git branch -d hotfix
    
    刪除 hotfix
  7. 回到 新需求分支 上繼續開發:

    git checkout iss53
    
    新分支
  8. 新需求開發完了,合并到 master 上:

    # 先切換回 master
    git checkout master
    
    # 合并分支
    git merge iss53
    

    這次的分支合并跟之前不大一樣,合并 hotfix 的時候,master 還是原來的 master, 但合并 iss53 分支的時候,master 已經不是原來的 master 了(此時的 master 由于合并了 hotfix, 指針向右移動了一格)。

    此時,master 指針不是直接移動到 iss53 指針所在的位置,而是在 master 分支上創建了一個新的節點用于存儲合并的結果:

    iss53 分支合并

沖突時的分支合并

加入在合并 iss53 分支的時候遇到了沖突,git 會顯示如下內容:

$ git merge iss53
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.

上述指令顯示在 index.html 文件中出現了沖突,你可以用 git status 命令查看一下此時的狀態:

$ git status
On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")

Unmerged paths:
  (use "git add <file>..." to mark resolution)

        both modified:      index.html

no changes added to commit (use "git add" and/or "git commit -a")

git 提醒你你要先解決沖突,然后使用 git add <file> 來將沖突標記為已解決狀態,然后調用 git commit 命令來修復沖突。

  1. 打開 index.html 文件,找到沖突所在位置:

    <<<<<<< HEAD
    <div id="footer">contact : email.support@github.com</div>
    =======
    <div id="footer">
      please contact us at support@github.com
    </div>
    >>>>>>> iss53
    

    ======= 以上的部分是 master 分支的內容,以下的部分是 iss53 分支的內容,你可以保留其中某個,或者改成另一種實現。

  2. 解決完沖突后,調用 git add index.html 將該文件放入暫存區;

  3. 然后調用 git commit 命令提交到倉庫。

3.3 分支的管理

  1. 顯示分支列表 git branch

    $ git branch
      iss53
    * master
      testing
    
  2. 查看各個分支的最后提交信息 git branch -v

    $ git branch -v
      iss53   93b412c fix javascript issue
    * master  7a98805 Merge branch 'iss53'
      testing 782fd34 add scott to the author list in the readmes
    
  3. 顯示已合并的分支 git branch --merged

    $ git branch --merged
      iss53
    * master
    

    知道哪些分支已經被合并,就可以刪除它們了

  4. 顯示未合并的分支 git branch --no-merged

    $ git branch --no-merged
      testing
    
  5. 分支已經合并的情況下,可以用 git branch -d branch_name 命令來刪除:

    $ git branch -d iss53
    
  6. 分支未合并的情況下,直接使用上述命令會報錯,因為分支上的修改可能是有用的,刪除的話可能會有危險:

    $ git branch -d testing
    error: The branch 'testing' is not fully merged.
    If you are sure you want to delete it, run 'git branch -D testing'.
    

    正如提示所說,如果你確實不需要這個分支,可以用 git branch -D branch_name 來強制刪除。

利用分支進行開發的工作流程

這一節介紹一些使用分支進行開發的工作流程。你可以結合實際項目的情況選擇一種用用看。

長期分支

由于 git 使用簡單的三方合并,你可以同時擁有多個開放的分支,每個分支用于特定的任務。

許多使用 git 的開發者都喜歡用這種方式來開展工作。比如在 master 分支中保留完全穩定的代碼,與此同時,在 master 的基礎上新建一個 develop 分支用于開發,還可以在 develop 分支的基礎上新建一個 topic 分支用于開發某些新特性。

topic 分支開發完畢后,合并到 develop 分支;develop 分支穩定下來后,合并到 master 分支:

流水線式的分支

注意: 這種方式是基于一種篩選的思想,新代碼通過 topic -> develop -> master 層層篩選,從而達到提交代碼穩定性的目的。跟之前 同時做兩件事的分叉分支 不是一個思路。

特性分支

這種方式就是上一節中同時做好幾件事的思路。在任何項目中都可以使用 特性分支(topic) 。一個特性分支是指一個短期的,用來實現單一特性的分支。可能你在其它版本控制工具里從未這樣做過,因為創建與合并分支的消耗太大。然而,在 git 中,一天之內建立、合并再刪除多個分支是常見的事。

這種技術有一個顯而易見的好處:各個特性分支之間互不干擾。瀏覽代碼之類的事情會因此變得更簡單。你可以把特性分支保存幾天甚至幾個月,在需要的時候合并它們就好了。

這種方式就不舉例子了,之前已經說過。

3.5 遠程分支

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 純手工打造每一篇開源資訊與技術干貨,數十萬程序員和Linuxer已經關注 1 Git 分支 - 分支簡介 有人把 ...
    塵世不擾閱讀 747評論 0 3
  • 因工作需要搭建了一個簡單的rose框架,采用的都是比較新的版本,可以根據需要自己調整。 rose框架確實比較古老了...
    瘋狂的冰塊閱讀 2,281評論 0 0
  • 很遺憾,沒辦法去接考完試的你。 說來至今也覺得認識你這件事蠻神奇。明明是你幫我撿到飯卡,主動相熟的那個人也是你。但...
    卿嶺閱讀 179評論 0 0
  • 你是一個不喜言語的女孩 我卻總是去調戲你 你力氣大,膽子小 我總喜歡去打你,然后讓你來追我 你經常幫助別人 我卻總...
    江瀟然閱讀 241評論 6 2
  • 第四課作業 1 找出你的星系印記所在的波符。并且在課程群里找一下,是否有和你同個波符的伙伴,如果有,把他們的昵稱和...
    逆流的栗Z閱讀 249評論 2 2