1. 程式人生 > >Git學習——Git分支篇(未完)

Git學習——Git分支篇(未完)

記得 決定 方法 操作 其它 並不會 基礎 git使用 可能

Git學習——Git分支篇(未完)


前言

完成了Git學習的基礎篇,繼續學習Git的分支特性,這是Git出眾之處。

目錄

  • 分支簡介
  • 分支創建
  • 分支切換
  • 分支新建與合並
    • 分支新建
    • 分支合並
    • 遇到沖突時的分支合並

分支簡介

首先,Git保存數據的方式比較特殊,保存的是文件的快照,而不是文件的變化。

因此,在執行提交( commit )操作時,Git會保存一個提交對象( commit object)。該提交對象包含一個指針指向暫存的內容快照,同時包含作者的姓名、郵箱、提交時輸入的信息和指向它父對象的指針。

首次提交產生的提交對象沒有父對象,普通提交產生的提交對象又一個父對象,多個分支合並產生的提交又多個父親對象。

暫存操作會為每一個文件計算校驗和(使用SHA-1哈希算法),然後保存當前版本的文件快照( Git使用blob對象來保存)。

提交操作會計算每個子目錄的校驗和,然後保存樹對象在倉庫中。然後Git會創建一個提交對象,它包含除了以上信息外,還有指向這個樹對象(項目根目錄)的指針。

當做出修改後再提交,所產生的提交對象會包含一個指向上次提交對象(父對象)的指針。

Git的分支,其本質是指向提交對象的可變指針。Git默認分支名為master,多次提交後,已經有一個指向最後提交對象的master分支,會在多次提交的操作中自動向前移動。

Git 的 master 分支並不特殊,它和其它分支一樣,只是大多數人不會改變它。


分支創建

Git創建新分支實際上是創建了一個可以移動的指針,執行 git branch branch-name 完成。

    $ git branch testing

這會在當前所在的提交對象上創建一個指針。此時擁有兩個分支 master 和 testing 。

Git 是如何知道當前所在分支,這是由於它有一個名為 HEAD 的特殊指針,指向當前所在的本地分支。此時我們還是 master 分支上,因為 git branch 命令只創建了一個新分支,並不會自動切換到新分支中去。

簡單的使用 git log 命令可以查看各個分支當前所指向的對象。只要加上 --decorate選項。

$ git log --oneline --decorate
21eeb6c (HEAD -> master, testing) init the repo

可以看見當前的 master 和 testing 分支都指向校驗和以 21eeb6c 開頭的提交對象。


分支切換

執行 git checkout branch-name 可以切換到相應分支。

$ git checkout testing
Switched to branch ‘testing‘

此時的 HEAD 指向新分支 testing 。那麽這麽實現的作用是什麽。假如此時在Git中修改並執行提交。

$ vim Hello.c
$ git add Hello.c
$ git commit -m "add Hello.c"

這裏添加一個新文件舉例,再使用 git log 查看。

$ git log --oneline --decorate
b6e0678 (HEAD -> testing) add Hello.c
21eeb6c (master) init the repo

可以看見的是,HEAD 指向了 testing並伴隨其向前移動到了新的提交對象上,對象的校驗和為b6e0678。值得註意的是,master 分支並沒有向前移動,還是停留在上一次提交的狀態。再次切換到 master 分支查看。

$ git checkout master
Switched to branch ‘master‘

這裏,Git 做了兩件事,以是將 HEAD 指向 master 分支,二是將工作目錄恢復成 master 分支所指向的快照內容。也就是說,如果現在做修改,將始於一個較舊的版本。本質上就是忽略 testing 分支所做的修改,以便於向另一個方向進行開發。

分支切換會改變你工作目錄中的文件在切換分支時,一定要註意你工作目錄裏的文件會被改變。 如果是切換到一個較舊的分支,你的工作目錄會恢復到該分支最後一次提交時的樣子。 如果 Git 不能幹凈利落地完成這個任務,它將禁止切換分支。

此時我們在 master 分支中添加 Hello.java 並提交。執行以下命令,輸出提交歷史、各個分支的指向以及項目的分支交叉情況。

$ git log --oneline --decorate --graph --all
* ee4b05d (HEAD -> master) another version of Hello
| * b6e0678 (testing) add Hello.c
|/
* 21eeb6c init the repo

註意這裏的master 和 testing 指向不同的對象。


分支的新建與合並

讓我們來看一個簡單的分支新建與分支合並的例子,實際工作中你可能會用到類似的工作流。 你將經歷如下步驟:

  1. 開發某個網站。
  2. 為實現某個新的需求,創建一個分支。
  3. 在這個分支上開展工作。

    正在此時,你突然接到一個電話說有個很嚴重的問題需要緊急修補。 你將按照如下方式來處理:

  4. 切換到你的線上分支(production branch)。
  5. 為這個緊急任務新建一個分支,並在其中修復它。
  6. 在測試通過之後,切換回線上分支,然後合並這個修補分支,最後將改動推送到線上分
    支。
  7. 切換回你最初工作的分支上,繼續工作。

新建分支

首先,假設正在項目上工作,並且有了兩次提交,記為C0(第一次提交),C1(第二次提交),此時的HEAD和和master在第二次提交處。

$ git log --oneline
d093652 (HEAD -> master) add homepage
64d793d add README

此時你決定要解決公司使用的問題追蹤系統中的#53問題,於是想要新建一個分支並切換過去。可以執行如下命令。

$ git checkout -b iss53
Switched to a new branch ‘iss53‘

這條命令實際上和以兩條下命令等價。

$ git branch iss53
$ git checkout iss53

此時的 master 和 iss53 都在第二次提交處,即C1。


假設你在iss53分支上工作並且做出了修改,經行了一次提交,這裏記為C2。iss53的分支向前推進。

$ vim index.html
$ git add issue53.html
$ git commit -m "added a new footer [issue 53]"
$ git log --oneline
b8deb27 (HEAD -> iss53) added a new footer [issue 53]
d093652 (master) add homepage
64d793d add README

現在,你接到電話,有個緊急問題需要解決,在Git中,不需要將這個問題和iss53放到一起,也不需要還原關於#53問題的修改,然後再添加這個緊急問題的修改,所需要做的是切換回 master 分支。

在這麽做之前,要留意工作目錄和暫存區裏那些還沒有被提交的修改,它可能會和即將檢出的分支產生沖突從而阻止 Git 切換到該分支。 最好的方法是在切換分支之前,保持好一個幹凈的狀態。 有一些方法可以繞過這個問題,這先不深究。切換回 master 。

$ git checkout master
Switched to branch ‘master‘

此時的工作目錄將和開始#53問題前一模一樣,可以開始修復緊急問題,建立一個針對該緊急問題的分支( hotfix ),在該分支上工作直到問題解決。

假設問題很簡單,只進行了一次提交完成了問題的解決。這次提交記為C3。

$ vim homepage.html
$ git commit -a -m "fixed the problem"

分支合並

在確保問題解決後想要合並回 master 分支 來部署到線上。執行 git merge branch-name 命令,記得要切換到 master 分支上。

$ git checkout master
Switched to branch ‘master‘
$ git merge hotfix
Updating d093652..4a8a77e
Fast-forward
 homepage.html | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 homepage.html

值得註意的是"Fast-forward"這個詞。由於當前的 master 分支所指向的提交是有關hotfix的提交的直接上遊,所以Git只是簡單的將指針向前移動,因為這種情況下的合並操作沒有需要解決的分歧,這叫做"快進Fast-forward"。

完成了緊急任務,你又要回到之前的工作,不過記得刪除hotfix這個分支,因為你以及不需要了,master 已經指向了同一位置,刪除操作如下。

$ git branch -d hotfix
Deleted branch hotfix (was 4a8a77e).

此時再切換到 iss53 分支繼續之前的工作。假設你工作了一段時間經行了一次提交,記為C4。這裏先梳理一下,master指向C3提交,iss53指向C4,C4的上遊為C2,C2和C3的上遊為C1。

$ git checkout iss53
vim index.html
...
...
$ git commit -a -m "fix some issues"

你在 hotfix 分支上所做的工作並沒有包含到 iss53 分支中。 如果你需要拉取hotfix 所做的修改,你可以使用 git merge master 命令將 master 分支合並入 iss53 分支,或者你也可以等到 iss53 分支完成其使命,再將其合並回 master 分支。

假設這裏#53已經解決,打算合並到master分支上,執行和之前合並 hotfix 差不多的命令。

$ git checkout master
Switched to branch ‘master‘
$ git merge iss53
Merge made by the ‘recursive‘ strategy.
 index.html   | 7 +++++++
 issue53.html | 0
 2 files changed, 7 insertions(+)
 create mode 100644 issue53.html

這裏合並和 hotfix 的合並看起來不一樣。因為 iss53 是從更早的時候分叉出來的(即 master 為C1時)。此時 master 為C3,並不是C4的直接祖先,Git需要做額外的工作,即將兩個分支的末端快照(C3和C4)以及這兩個分支的共同祖先(c1),做一個簡單的三方合並。

和之前不同的是,這裏將三方合並的結果做了一個新的快照並且自動創建一個新的提交指向了它。這個被稱作一次合並提交,它的特別之處在於它有不止一個父提交。不妨記作C5。

$ git log --oneline --graph --all
*   f9d8ac1 (HEAD -> master) Merge branch ‘iss53‘
|| * 3c230d6 (iss53) fix some issues
| * b8deb27 added a new footer [issue 53]
* | 4a8a77e fixed the problem
|/
* d093652 add homepage
* 64d793d add README

這裏由下向上顯示了合並的過程,由於hotfix已經刪除,所以不再顯示。 64d793d代表C0,d093652代表C1,4a8a77e代表C3,b8deb27代表C2,3c230d6代表C4,f9d8ac1代表C5。此時可以刪除iss53分支。

$ git branch -d iss53

Git學習——Git分支篇(未完)