之前已經學會了分支的創建,刪除,轉換和合並,那麼如何對分支進行管理呢
分支的管理
查看分支:git branch
之前用到過 git branch 創建分支,刪除分支,直接使用 git branch 就是查看有哪些分支。
其中,有 * 代表是當前工作分支。
查看各個分支最後一次提交信息: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
從清單中篩選出你已經(或尚未)與當前分支合併的分支,加上 --merge
和 --no-merged
選項
$ git branch --merged
iss53
* master
之前我們已經合併了 iss53
,所以在這裏會看到它。一般來說,列表中沒有 *
的分支通常都可以用 git branch -d
來刪掉。因爲既然已經把它們所包含的工作整合到了其他分支,刪掉也不會損失什麼。
而對於 --no-merged 的分支則不能 -d 直接刪除,會報錯(提示會丟失數據)。如果想要強制刪除,使用 -D 選項。
遠程分支
牢記這些分支全部都是本地分支,這一點很重要。當你在使用分支及合併的時候,一切都是在你自己的 Git 倉庫中進行的 — 完全不涉及與服務器的交互。
遠程分支(remote branch)是對遠程倉庫中的分支的索引。使用 (遠程倉庫名)/(分支名)
這樣的形式表示遠程分支,遠程分支不能直接修改,只有與網絡交互才能對他更新。
如果從遠程倉庫中克隆一個項目,Git 會自動將此遠程倉庫命名爲 origin
,並下載其中所有的數據,建立一個指向它的 master
分支的指針,在本地命名爲 origin/master
,但你無法在本地更改其數據。接着,Git 建立一個屬於你自己的本地 master
分支,始於 origin
上 master
分支相同的位置,然後你可以就此開始工作。
如果團隊成員在遠程倉庫推送了他們的更新, 服務器和本地 master 指針會向前移動,而本地的 origin/master 仍然不變
這時可以使用 git fetch origin 拉取服務器的最新數據並同步到本地倉庫,origin/master 會移動到最新位置。
推送本地分支
要想和其他人分享某個本地分支,就需要把它推送到一個你擁有寫權限的遠程倉庫。
將本地分支推送到遠端服務器:git push (遠程倉庫名) (分支名)
$ git push origin serverfix
Counting objects: 20, done.
Compressing objects: 100% (14/14), done.
Writing objects: 100% (15/15), 1.74 KiB, done.
Total 15 (delta 5), reused 0 (delta 0)
To [email protected]:schacon/simplegit.git
* [new branch] serverfix -> serverfix
這會將本地 serverfix 分支推送到遠程服務器 serverfix 分支。若想把遠程分支叫作 awesomebranch
,可以用 git push origin serverfix:awesomebranch
來推送數據。
接下來,當協作者再次從服務器上獲取數據時,他們將得到一個新的遠程分支 origin/serverfix
,並指向服務器上 serverfix
所指向的版本:
$ git fetch origin
remote: Counting objects: 20, done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 15 (delta 5), reused 0 (delta 0)
Unpacking objects: 100% (15/15), done.
From [email protected]:schacon/simplegit
* [new branch] serverfix -> origin/serverfix
值得注意的是,在 fetch
操作下載好新的遠程分支之後仍然無法在本地編輯該遠程倉庫中的分支。換句話說,在本例中,你不會有一個新的 serverfix
分支,有的只是一個你無法移動的 origin/serverfix
指針。
如果要把該遠程分支的內容合併到當前分支,可以運行 git merge origin/serverfix
。如果想要一份自己的 serverfix
來開發,可以在遠程分支的基礎上分化出一個新的分支來:
$ git checkout -b serverfix origin/serverfix
Branch serverfix set up to track remote branch refs/remotes/origin/serverfix.
Switched to a new branch "serverfix"
這會切換到新建的 serverfix
本地分支,其內容同遠程分支 origin/serverfix
一致,這樣你就可以在裏面繼續開發了。
跟蹤遠程分支
從遠程分支 checkout
出來的本地分支,稱爲 跟蹤分支 (tracking branch)。跟蹤分支是一種和某個遠程分支有直接聯繫的本地分支。在跟蹤分支裏輸入 git push
,Git 會自行推斷應該向哪個服務器的哪個分支推送數據。同樣,在這些分支裏運行 git pull
會獲取所有遠程索引,併合併到本地分支。
在克隆倉庫時,Git 通常會自動創建一個名爲 master
的分支來跟蹤 origin/master
。這正是 git push
和 git pull
一開始就能正常工作的原因。當然,你可以隨心所欲地設定爲其它跟蹤分支,比如 origin
上除了 master
之外的其它分支。剛纔我們已經使用:git checkout -b [分支名] [遠程名]/[分支名]
。如果是 1.6.2 以上版本的 Git,還可以用 --track
選項簡化:
$ git checkout --track origin/serverfix
Branch serverfix set up to track remote branch refs/remotes/origin/serverfix.
Switched to a new branch "serverfix"
要爲本地分支設定不同於遠程分支的名字,只需在第一個版本的命令裏換個名字:
$ git checkout -b sf origin/serverfix
Branch sf set up to track remote branch refs/remotes/origin/serverfix.
Switched to a new branch "sf"
現在你的本地分支 sf
會自動將推送和抓取數據的位置定位到 origin/serverfix
了。
刪除遠程分支
如果不再需要某個遠程分支了,比如搞定了某個特性並把它合併進了遠程的 master
分支(或任何其他存放穩定代碼的分支),可以這樣來刪除它:git push [遠程名] :[分支名]
。如果想在服務器上刪除 serverfix
分支,運行下面的命令:
$ git push origin :serverfix
To [email protected]:schacon/simplegit.git
- [deleted] serverfix
有種方便記憶這條命令的方法:不久前見過 git push [遠程名] [本地分支]:[遠程分支]
語法,如果省略 [本地分支]
,那就等於是在說“在這裏提取空白然後把它變成 [遠程分支]
”。
分支的衍合
把一個分支中的修改整合到另一個分支的辦法有兩種:merge
和 rebase(衍合)
衍合的基本操作
假如兩個分支進行到如下階段
使用 merge 操作整合分支,就會把兩個分支的最新快照(C3和C4)以及他們的共同祖先(C2)進行三方合併,產生新的提交對象C5。
還有另外一個選擇:就是把在 C3 裏產生的變化補丁在 C4 的基礎上重新打一遍。在 Git 裏,這種操作叫做衍合(rebase)。有了 rebase
命令,就可以把在一個分支裏提交的改變移到另一個分支裏重放一遍。
$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command
用當前分支給分支名標識的分支打補丁:git rebase 分支名
它的原理是回到兩個分支最近的共同祖先,根據當前分支(也就是要進行衍合的分支 experiment
)後續的歷次提交對象(這裏只有一個 C3),生成一系列文件補丁,然後以基底分支(也就是主幹分支 master
)最後一個提交對象(C4)爲新的出發點,逐個應用之前準備好的補丁文件,最後生成一個新的合併提交對象(C3'),從而改寫 experiment
的提交歷史,使它成爲 master
分支的直接下游。
然後回到 master 分支,進行一次快速合併
接下來看一個複雜一點的衍合,但是看過之後你會對衍合有更深的理解。
假如 master 操作後又添加了新的分支 server ,提交到 C4,這時又添加 client 分支修改客戶端代碼,提交到 C9 又回到 server 並提交到 C10。
接下來在一次發佈中要把 client 合併到 master 中。這個時候,我們就可以把 client 分支的改變即 C8 和 C9,跳過 server
直接放到 master
分支中重演一遍,但這需要用 git rebase
的 --onto
選項指定新的基底分支 master
:
$ git rebase --onto master server client
這個命令的意思是:“取出 client
分支,找出 client
分支和 server
分支的共同祖先之後的變化,然後把它們在 master
上重演一遍”。結果如圖。
現在可以快進 master 了
$ git checkout master
$ git merge client
現在決定把 server
分支的變化也包含進來。我們可以直接把 server
分支衍合到 master
,而不用手工切換到 server
分支後再執行衍合操作 — git rebase [主分支] [特性分支]
命令(注意:特性分支也就是要使用的改變所在的分支是在命令的最後面,就可以理解 --onto 中最後是 client 了)會先取出特性分支 server
,然後在主分支 master
上重演:
$ git rebase master server
然後就可以快進主幹分支 master
了:
$ git checkout master
$ git merge server
現在 client
和 server
分支的變化都已經集成到主幹分支來了,可以刪掉它們了。
$ git branch -d client
$ git branch -d server
衍合的風險
使用衍合也是有風險的,但是請記住:
一旦分支中的提交對象發佈到公共倉庫,就千萬不要對該分支進行衍合操作。
也就是說,如果把衍合當成一種在推送之前清理提交歷史的手段,而且僅僅衍合那些尚未公開的提交對象,就沒問題。如果衍合那些已經公開的提交對象,並且已經有人基於這些提交對象開展了後續開發工作的話,就會出現叫人沮喪的麻煩。 因此,最好在分享之前進行衍合。