git系列---分支

注: 該系列文章整理自《Pro Git》,非原創。html

git系列:

git系列--基礎使用

git系列--分支

一 Git分支簡介

幾乎全部的版本控制系統都以某種形式支持分支。使用分支意味着你能夠把你的工做從開發主線上分離開來,以避免影響開發主線。git

與許多其它版本控制系統不一樣,Git處理分支的方式可謂是難以置信的輕量,且鼓勵在工做流程中頻繁地使用分支與合併,哪怕一天以內進行許屢次。github

首先回顧下Git保存數據的方式:Git保存的不是文件的變化或差別,而是一系列不一樣時刻的文件快照。Git進行提交操做的時候,會保存一個提交對象,該對象會包含一個指向內容快照的指針,還包含了做者的姓名和郵箱、提交時輸入的信息以及指向它的父對象的指針(首次提交產生的提交對象沒有父對象,普通提交操做產生的提交對象有一個父對象,而由多個分支合併產生的提交對象有多個父對象),相似於鏈數據結構。bash

Git的分支,在本質上僅僅只是指向提交對象的可變指針!它會在每次的提交操做中自動向前移動。服務器

注:Git的默認分支是「master」,並無什麼特殊意義,只是git init命名默認建立的,通常懶得去更名字數據結構

1.1 分支建立

Git分支的建立,能夠理解爲就是簡單的建立了一個基於當期提交對象的能夠移動的新指針。post

好比,新建一個testing分支,用git branch測試

$ git branch testing
複製代碼

這會在當前所在的提交對象上建立一個新的指針fetch

那麼問題來了,git怎麼知道當前本身在哪個分支指針上呢?它用一個名爲「HEAD」的特殊指針來區分——它指向當前所在的本地分支(可將其想象成當前分支的一個別名)。切換到其餘分支,能夠當作就是HEAD指向另外一個分支指針ui

1.2 切換分支

新建分支,並不會自動切換到新分支,須要使用git checkout切換。

$ git checkout testing
複製代碼

這樣,就切到咱們剛纔新建的testing分支上,即HEAD指向了testing分支

Git的分支功能十分強大有用。好比,你須要在testing上開發一個測試功能,但不想影響到主線master,或master先進行額外的更新發布。此時,你就能夠分別在兩個分支上操做,待時機成熟,再將你的工做合併起來:

首先切換到testing分支,並進行了一次提交,此時,testing分支就向前移動領先master(此時master是不移動的)

而後咱們切換回到主線master上

$ git checkout master
複製代碼

這條命令作了兩件事。

  • 一是使 HEAD 指回 master 分支,
  • 二是將工做目錄恢復成 master 分支所指向的快照內容。 也就是說,你如今作修改的話,項目將始於一個較舊的版本。 本質上來說,這就是忽略 testing 分支所作的修改,以便於向另外一個方向進行開發。

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

如今,若在master上,又進行了一個更新提交,則項目上就會產生分叉。你能夠在不一樣分支間不斷地來回切換和工做,並在時機成熟時將它們合併起來。

因爲 Git 的分支實質上僅是包含所指對象校驗和(長度爲 40 的 SHA-1 值字符串)的文件,因此它的建立和銷燬都異常高效。任何規模的項目都能在瞬間建立新分支。同時,因爲每次提交都會記錄父對象,因此尋找恰當的合併基礎(即共同祖先)也是一樣的簡單和高效。這些高效的特性使得 Git 鼓勵開發人員頻繁地建立和使用分支。

二 分支的新建和合並

在實際開發過程當中,經常會忽然遇到一個緊急問題須要修復。這時,能夠先切換到主分支,再爲這個緊急的任務新建一個分支來解決問題。待問題解決後,再將其合併到主分支中。

2.1 新建分支和分支合併

假設項目已有一些提交

如今你正在解決公司的問題#53,所以,你新建了一個issue53分支。(想新建一個分支,並同時切換道這個分支上,可使用帶-b參數的git checkout命令:

$ git checkout -b iss53
Switched to a new branch "iss53"
複製代碼

它是下面兩條命令的簡寫

$ git branch iss53
$ git checkout iss53
複製代碼

繼續在issue53上工做並提交了一些新提交。

如今忽然接到緊急任務,須要快速修復主分支的問題。有了Git的幫助,你沒必要把這個緊急問題和iss53的修改混在一塊兒,你也不須要花大力氣來還原關於 53# 問題的修改,而後再添加關於這個緊急問題的修改,最後將這個修改提交到線上分支。 你所要作的僅僅是切換回 master 分支,並新建hotfix分支,再修復合併。

$ git checkout master
$ git checkout -b hotfix
複製代碼

你能夠在hotfix分支上修復問題,並運行你的測試,確保你的修改是正確的,而後將其合併回你的 master 分支來部署到線上。 你可使用 git merge 命令來達到上述目的:

$ git checkout master
$ git merge hotfix
Updating f42c576..3a0874c
Fast-forward
 index.html | 2 ++
 1 file changed, 2 insertions(+)
複製代碼

如今這個緊急問題已修復併合併到主分支中了,所以能夠刪除該分支。 你可使用帶 -d 選項的 git branch 命令來刪除分支:

$ git branch -d hotfix
Deleted branch hotfix (3a0874c).
複製代碼

如今切回以前工做的分支,即issue53,並繼續工做

$ git checkout iss53
複製代碼

在hotfix分支上作的修復工做並無包含到issue53分支中。若是須要拉取所作的修復,可使用git merge master將master合併入issue53分支中(由於hotfix已刪除,因此合併master分支)。 或者,能夠等到issue53分支完成任務後再合併回master分支中。

假設你已經修正了 #53 問題,而且打算將你的工做合併入 master 分支。 爲此,你須要合併 iss53 分支到 master 分支,這和以前你合併 hotfix 分支所作的工做差很少。 你只須要檢出到你想合併入的分支,而後運行 git merge 命令:

$ git checkout master
$ git merge iss53

複製代碼

這和你以前合併 hotfix 分支的時候看起來有一點不同。 在這種狀況下,你的開發歷史從一個更早的地方開始分叉開來(diverged)。 由於,master 分支所在提交併非 iss53 分支所在提交的直接祖先,Git 不得不作一些額外的工做。 出現這種狀況的時候,Git 會使用兩個分支的末端所指的快照(C4 和 C5)以及這兩個分支的工做祖先(C2),作一個簡單的三方合併。

和hotfix單純的推動分支指針不一樣,Git 將這次三方合併的結果作了一個新的快照而且自動建立一個新的提交指向它。 這個被稱做一次合併提交,它的特別之處在於他有不止一個父提交。

最後刪除分支

$ git branch -d iss53
複製代碼

三 分支管理

3.1 查看分支列表

git branch命令,不加任何參數的狀況下,會當到當前全部分支的列表(本地分支):

$ git branch
  iss53
* master
  testing
複製代碼

notemaster分支前的*字符,表明當前檢出的那個分支(即,當前HEAD指針所指向的分支)

--merged--no-merged這兩個有用的選項能夠過濾這個列表中已經合併或還沒有合併到當前分支的分支。 若是要查看哪些分支已經合併到當前分支,能夠運行 git branch --merged:

$ git branch --merged
  issue53
* master
複製代碼

由於以前已經合併了issue53分支,因此如今它在列表中。一般能夠用git branch -d刪除這些已經合併了的分支

若要查看未合併的分支,能夠運行git branch --no-merged

$ git branch --no-merged
  testing
複製代碼

注:對於這些還未合併的分支,若是它包含了還未合併的工做,嘗試使用git branch -d來刪除時會提示失敗。(可使用-D選項強制刪除,不推薦)

四 遠程分支

能夠經過 git remote show (remote) 查看遠程倉庫的具體遠程分支狀況

4.1 遠程分支介紹

上一篇講過遠程倉庫,也就是你服務器的git倉庫,如GitHub上的項目。遠程分支能夠理解爲,是將遠程倉庫上的分支的拷貝到本地目錄中,但對於這些拷貝下來的遠程分支,你只能去查看,而不能主動去提交修改,只有當你與服務器同步時,拷貝下來的分支纔會有新的提交,即分支指針纔會有移動。

簡單而言,就是你僅僅只有隻讀權限,能夠讀取服務器的最新代碼狀態,並將其合併到本身的本地分支中。或者自行推送最新的修改到服務器中(此時,拷貝的遠程分支也會自動更新到最新狀態)

遠程分支以(remote)/(branch) 形式命名

舉個例子,假設你有一個git.ourcompany.com的git遠程服務器,且上面已經有工做基礎了的,而不是個空項目,此時你能夠經過 git clone 克隆下來,拉取它的數據。此時,git爲幫你將這個遠程服務器命名爲origin,同時拉取它的全部數據,並建立一個origin/master分支指針指向它,而且還會建立一個master本地分支,指向相同的地方,使得有工做的基礎

如今你在本地master分支上作了些提交,與此同時,其餘人推送更新了git.ourcompany.com遠程倉庫。(只要你不與服務器同步,拷貝的遠程分支origin/master指針就不會有移動)

要抓取服務器的最新代碼,能夠運行git fetch origin。抓取並更新本地拷貝下來的遠程分支。

fetch命令只會抓取數據並更新到遠程分支,但並不會自動幫你合併到你的工做的本地分支master中,須要你自行合併(pull命令則能夠自動合併,但不推薦使用)。

4.2 推送

本地的分支並不會自動與遠程倉庫同步——你必須顯式地推送想要分享的分支。當你想要分享公開一個分支時,你可使用git push推送上去。

例如,但願和其餘人在serverfix分支上工做,能夠運行git push (remote) (branch)

$ git push origin serverfix
Counting objects: 24, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (15/15), done.
Writing objects: 100% (24/24), 1.91 KiB | 0 bytes/s, done.
Total 24 (delta 2), reused 0 (delta 0)
To https://github.com/schacon/simplegit
 * [new branch]      serverfix -> serverfix
複製代碼

你也能夠運行 git push origin serverfix:serverfix,它會作一樣的事——也就是說「推送本地的 serverfix 分支,將其做爲遠程倉庫的 serverfix 分支」 能夠經過這種格式來推送本地分支到一個命名不相同的遠程分支。 若是並不想讓遠程倉庫上的分支叫作 serverfix,能夠運行 git push origin serverfix:awesomebranch 來將本地的 serverfix 分支推送到遠程倉庫上的 awesomebranch 分支

推送以後,其餘人使用git fetch抓取數據時,會在本地生成一個遠程分支origin/serverfix,指向服務器的serverfix分支的引用

$ git fetch origin
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0)
Unpacking objects: 100% (3/3), done.
From https://github.com/schacon/simplegit
 * [new branch]      serverfix    -> origin/serverfix
複製代碼

特別注意:抓取新的服務器分支後,只會拷貝一份可讀的遠程分支,本地不會自動生成可工做的server分支(clone命令,生成master命令是系統自動生成的) 換一句話說,這種狀況下,不會有一個新的 serverfix 分支——只有一個不能夠修改的 origin/serverfix 指針。

能夠運行git merge origin/serverfix,將這些工做合併到當前所處的本地工做分支。或者,若是想在本身本地也建立一個serverfix分支工做,能夠將其創建在遠程跟蹤分支上($ git checkout -b serverfix origin/serverfix):

$ git checkout -b serverfix origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'
複製代碼

這會給你一個用於工做的本地分支,而且起點位於 origin/serverfix。

4.3 跟蹤分支

從一個遠程分支檢出(checkout)一個本地分支會建立所謂的」跟蹤分支「(它跟蹤的分支叫作「上游分支」)。跟蹤分支是與遠程分支有直接關係的本地分支。 若是在一個跟蹤分支上輸入git pull,Git能自動地識別去哪一個服務器上抓取、合併到哪一個分支。

當clone克隆一個倉庫時,一般會自動建立一個跟蹤origin/master的master分支。你也能夠設置其餘的跟蹤分支,如前面的$ git checkout -b serverfix origin/serverfix就會建立一個跟蹤origin/serverfix的本地serverfix分支。(該命令的快捷方式:$ git checkout --track origin/serverfix

如今,在本地分支 serverfix運行pull命令 會自動從 origin/serverfix 拉取。

設置已有的本地分支跟蹤一個剛剛拉取下來的遠程分支,或者想要修改正在跟蹤的上游分支,你能夠在任意時間使用 -u--set-upstream-to 選項運行 git branch 來顯式地設置。

$ git branch -u origin/serverfix
複製代碼

當推送一個新分支到服務器時,也可使用帶-upush命令,它不只會推送本地分支到服務器,也會跟蹤它。

$ git push -u origin serverfix

複製代碼

若是你有注意的話,在GitHub上新建一個空項目時,就有相關的提示

添加一個遠程倉庫,並推送本地master分支到服務器上,同時跟蹤這個遠程分支。

4.4 拉取

git fetch 命令從服務器上抓取本地沒有的數據時,它並不會修改工做目錄中的內容。 它只會獲取數據而後讓你本身合併。 然而,有一個命令叫做 git pull 在大多數狀況下它的含義是一個 git fetch 緊接着一個 git merge 命令。 若是有一個像以前章節中演示的設置好的跟蹤分支,無論它是顯式地設置仍是經過 clone 或 checkout 命令爲你建立的,git pull 都會查找當前分支所跟蹤的服務器與分支,從服務器上抓取數據而後嘗試合併入那個遠程分支。

因爲 git pull 常常使人困惑因此一般單獨顯式地使用 fetch 與 merge 命令會更好一些。

4.5 刪除遠程分支

假設你已經經過遠程分支作完全部的工做了——也就是說你和你的協做者已經完成了一個特性而且將其合併到了遠程倉庫的 master 分支(或任何其餘穩定代碼分支)。 能夠運行帶有 --delete選項的 git push 命令來刪除一個遠程分支。

$ git push origin --delete serverfix
To https://github.com/schacon/simplegit
 - [deleted]         serverfix
複製代碼

五 分支的另外一種整合:變基(rebase)

在 Git 中整合來自不一樣分支的修改主要有兩種方法:merge 以及 rebase。二者的主要區別在於提交歷史的不一樣。

5.1 變基(rebase)

變基rebase命令將分支上的全部修改移到另外一個你指定的分支中,就好像「從新播放」同樣,跟以前的合併merge的主要區別就是最終提交歷史記錄不一樣,但最終的代碼結果是同樣的。

假設如今開發任務有兩個分叉

若是使用merge 命令。 它會把兩個分支的最新快照(C3 和 C4)以及兩者最近的共同祖先(C2)進行三方合併,合併的結果是生成一個新的快照(並提交)。

此時若使用變基rebase命令,則提取在 C4 中引入的補丁和修改,而後在 C3 的基礎上應用一次,就好像「從新播放」同樣。

$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command
複製代碼

它的原理是首先找到這兩個分支(即當前分支 experiment、變基操做的目標基底分支 master)的最近共同祖先 C2,而後對比當前分支相對於該祖先的歷次提交,提取相應的修改並存爲臨時文件,而後將當前分支指向目標基底 C3, 最後以此將以前另存爲臨時文件的修改依序應用

如今回到 master 分支,進行一次快進合併。

$ git checkout master
$ git merge experiment
複製代碼

此時,C4' 指向的快照就和上面使用 merge 命令的例子中 C5 指向的快照如出一轍了。再次強調:兩種整合方法的最終結果沒有任何區別,可是變基使得提交歷史更加整潔,通常使用變基的目的是爲了確保在向遠程分支推送時能保持提交歷史的整潔,具體使用哪一種方法,看我的實際使用。

注:不要對在你的倉庫外有副本的分支執行變基。 若是你遵循這條金科玉律,就不會出差錯。不然,人民羣衆會仇恨你,你的朋友和家人也會嘲笑你,唾棄你。

更多可查看Git 分支 - 變基

相關文章
相關標籤/搜索