好文推薦:Git之遠端分支

Git是一個分散式版本管理系統。但是到現在為止,所有的演練都是在本地Git倉庫。如果想與他人合作,還需要一個遠端的 Git 倉庫。儘管技術上可以從個人的倉庫裡推送和拉取修改內容,但不鼓勵這樣做,因為一不留心就很容易弄混其他人的進度。另外,你也一定希望合作者們即使在自己不開機的時候也能從倉庫獲取資料——擁有一個更穩定的公共倉庫十分有用。因此,更好的合作方式是建立一個大家都可以訪問的共享倉庫,從那裡推送和拉取資料。我們將把這個倉庫稱為“Git 伺服器”。

1、傳輸協議

理論上來說,Git 可以使用四種協議來傳輸資料:本地傳輸協議,SSH 協議,Git 協議和HTTP 協議。

1。1 本地傳輸協議

本地傳輸,其實就是在使用本地的某個Git倉庫當做遠端倉庫。這常見於團隊每一個成員都對一個共享的檔案系統(例如NFS)擁有訪問權。基於檔案倉庫的優點在於它的簡單,同時保留了現存檔案的許可權和網路訪問許可權。如果團隊已經有一個全體共享的檔案系統,建立倉庫就十分容易。

這種方法的缺點是,與基本的網路連線訪問相比,難以控制從不同位置來的訪問許可權。

2。2 Git 協議

Git 協議是一個包含在 Git 軟體包中的特殊守護程序; 它會監聽一個提供類似於 SSH 服務的特定埠(9418),而無需任何授權。打算支援 Git 協議的倉庫,需要先建立

git-export-daemon-ok

檔案——它是協議程序提供倉庫服務的必要條件——但除此之外該服務沒有什麼安全措施。要麼所有人都能克隆 Git 倉庫,要麼誰也不能。這也意味著該協議通常不能用來進行推送。你可以允許推送操作;然而由於沒有授權機制,一旦允許該操作,網路上任何一個知道專案 URL 的人將都有推送許可權。

Git 協議是現存最快的傳輸協議。如果在提供一個有很大訪問量的公共專案,或者一個不需要對讀操作進行授權的龐大專案,架設一個 Git 守護程序來供應倉庫是個不錯的選擇。它使用與 SSH 協議相同的資料傳輸機制,但省去了加密和授權的開銷。

Git 協議消極的一面是

缺少授權機制

。用 Git 協議作為訪問專案的唯一方法通常是不可取的。一般的做法是,同時提供 SSH 介面,讓幾個開發者擁有推送(寫)許可權,其他人透過git:// 擁有隻讀許可權。Git 協議可能也是最難架設的協議。它要求有單獨的守護程序,需要定製。該協議還要求防火牆開放 9418 埠,而企業級防火牆一般不允許對這個非標準埠的訪問。大型企業級防火牆通常會封鎖這個少見的埠。

2。3 SSH 協議

SSH 是建立在應用層和傳輸層基礎上的安全協議,可以有效防止遠端管理過程中的資訊洩露問題。透過SSH,可以把所有傳輸的資料進行加密,這樣“中間人”這種攻擊方式就不可能實現了,而且也能夠防止DNS欺騙和IP欺騙。使用SSH,還有一個額外的好處就是傳輸的資料是經過壓縮的,所以可以加快傳輸的速度。

Git 使用的傳輸協議中最常見就是 SSH 。因為大多數環境已經支援透過 SSH 對伺服器的訪問——即便還沒有,架設起來也很容易。

使用 SSH 的好處有很多。首先,如果想擁有對網路倉庫的寫許可權,基本上不可能不使用 SSH。其次,SSH 架設相對比較簡單,而且很多作業系統都自帶了它或者相關的管理工具。再次,透過 SSH 進行訪問是安全的——所有資料傳輸都是加密和授權的。最後,和 Git 及本地協議一樣,SSH 也很高效,會在傳輸之前儘可能壓縮資料。

SSH 的限制在於不能透過它實現倉庫的匿名訪問。即使僅為讀取資料,人們也必須在能透過 SSH 訪問主機的前提下才能訪問倉庫,這使得 SSH 不利於開源的專案。但是如果是在公司網路裡使用,SSH 絕對是首選協議。

1。4 HTTP/S 協議

HTTP 或 HTTPS 協議的優美之處在於架設的簡便性。基本上,只需要把 Git 的裸倉庫檔案放在 HTTP 的根目錄下,配置一個特定的post-update 掛鉤(hook)就可以搞定。

HTTP 協議不會佔用過多伺服器資源。因為它一般只用到靜態的 HTTP 服務提供所有資料,普通的 Apache 伺服器平均每秒都能支撐數千個檔案的併發訪問。

HTTP 協議的消極面在於,相對來說客戶端效率更低。克隆或者下載倉庫內容可能會花費更多時間,而且 HTTP 傳輸的體積和網路開銷比其他任何一個協議都大。

以上四個協議中,

本地傳輸協議與Git協議只會在實驗中使用,企業級應用中普遍以SSH與HTTP為主

在與遠端倉庫進行資訊交換之前,我們先要獲得訪問許可權,對Git伺服器上的某個專案,自己是否可讀、是否可寫。這就需要確立一個Git伺服器有相互識別的手段,最常用、最可靠的就是公鑰私鑰認證,github上使用的就是這種手段,如何設定官網有詳細的教程,不再贅述。

2、add操作

命令:

git remote add [shortname] [url]

可以將一個遠端倉庫與本地Git倉庫關聯起來。

比如,我在github上有一個名為“MyProject”的專案,在本地的Git倉庫目錄下執行:

git remote add gitHubProject git@github。com:Winner2015/MyProject。git

然後使用

git remote

檢視現有的遠端倉庫列表:

gitHubProject

git@github。com:Winner2015/MyProject。git

是我的專案在github上的地址,“gitHubProject”是我給遠端倉庫起的別名,以後就可以用gitHubProject來代指這個遠端倉庫。

注意

:該命令只是建立了本地Git倉庫與一個URL地址(其實本質上就是一個字串)的關聯關係,但是這個URL存不存在(比如,你有可能輸錯了一個字母)、你有沒有許可權訪問,Git暫時並不知道。

3、fetch操作

命令:

git fetch [remote-name]

可以從遠端倉庫抓取資料到本地。

例如,將剛新增的遠端倉庫拉取到本地:

git fetch gitHubProject

Git提示:

warning: no common commits

remote: Counting objects: 13, done。

remote: Total 13 (delta 0), reused 0 (delta 0), pack-reused 13

Unpacking objects: 100% (13/13), done。

From github。com:Winner2015/MyProject

-* [new branch] master -> gitHubProject/master

現在github上的專案MyProject已經抓取到本地了,

gitHubProject/master

代表該專案的master分支。

使用命令

git checkout gitHubProject/master

命令切換到該專案的master分支,Git會提示:

Note: checking out ‘gitHubProject/master’。

You are in ‘detached HEAD’ state。 You can look around, make experimental

changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another checkout。

If you want to create a new branch to retain commits you create, you may

do so (now or later) by using -b with the checkout command again。 Example:

git checkout -b

遠端倉庫雖然已經抓取到本地,但是並沒有與本地的任何分支關聯,所以Git警告,遠端分支處於“detached HEAD”狀態,遊離於所有已知分支之外。

fetch 命令只是將遠端的資料拉到本地倉庫,並不自動合併到當前工作分支。

實際上,如果我們想將自己的修改提交到遠端倉庫,首先必須提交到本地,然後再push到遠端倉庫。所以,應該將抓取下來的資料手工merge到某個現有分支或者新建的一個分支當中。

該命令在實際應用中很少使用,常用的是更高階的pull命令。

4、pull操作

如果設定了本地某個分支用於跟蹤某個遠端倉庫的分支,可以使用

git pull

命令自動抓取資料下來,並將遠端分支自動合併到本地倉庫中當前分支(前提是該遠端分支已經與本地的某個分支建立了關聯)。在日常工作中我們經常這麼用,既快且好。

命令格式如下:

git pull [remote-name]

pull之後,我們的本地分支很可能與遠端分支出現衝突,同樣需要解決之後才能順利合併、提交。

5、push操作

專案進行到一個階段,要同別人分享目前的成果,可以將本地倉庫中的資料推送到遠端倉庫。實現這個任務的命令很簡單:

git push [remote-name] [branch-name]

某種情況下,初次執行git pull或者git push的時候,Git會提示說“no tracking information”,無法完成操作,則說明本地分支和遠端分支之間的關聯沒有建立。用命令:

git branch ——set-upstream [branch-name] [origin/branch-name]

可以將某個遠端分支設定為本地分支的“上游”。

在版本較新的Git中,該命令已經不推薦使用,而是使用

——track

引數或

——set-upstream-to

引數。

建立本地分支並追蹤遠端某個分支,可以用一個命令搞定:

git branch ——track local_branchname origin/remote_branchname

手動設定本地分支的上游時,推薦使用命令:

git branch ——set-upstream-to=origin/ remote_branchname

取消對某個分支的跟蹤,使用命令:

git branch ——unset-upstream local_branchname

6、clone操作

使用clone命令可以將Git伺服器上的資料克隆到本地,例如:

git clone git@github。com:Winner2015/MyProject。git

預設情況下

git clone

命令自動建立本地的 master 分支用於跟蹤遠端倉庫中的 master 分支,並且將遠端倉庫命名為“origin”。

使用命令

git remote show origin

可以檢視名為“origin”的遠端倉庫的資訊:

-* remote origin

Fetch URL:

git@github。com

:Winner2015/MyProject。git

Push URL:

HEAD branch: master

Remote branches:

master tracked

Local branch configured for ‘git pull’:

master merges with remote master

Local ref configured for ‘git push’:

master pushes to master (up to date)

Git友好地告訴你,pull操作會將遠端倉庫的master分支自動與本地的master分支合併;類似地,push操作會自動將本地的master合併到遠端的master分支。

6、刪除和重新命名

重新命名命令:

git remote rename [newName]

刪除命令:

git remote rm [remoteName]

注意:該命令只是將本地分支與遠端分支之間的關聯刪除,而不是將遠端倉庫對應的分支刪除。

7、實現原理

遠端分支是對遠端倉庫中的分支的索引。它們是一些無法移動的本地分支;只有在 Git 進行網路互動時才會更新。遠端分支就像是書籤,提醒著上次連線遠端倉庫時上面各分支的位置。

我們用“遠端倉庫名/分支名”這樣的形式表示遠端分支。比如我們想看看上次同 origin 倉庫通訊時master 的樣子,就應該檢視 origin/master 分支。

假設Git伺服器地址為 git。ourcompany。com,如果從這裡克隆,Git 會自動將此遠端倉庫命名為origin,並下載其中所有的資料,同時建立一個指向它的 master 分支的指標,在本地命名為origin/master,但你無法在本地更改其資料。接著,Git 建立一個屬於你自己的本地master 分支,始於 origin 上 master分支相同的位置,你可以就此開始工作:

好文推薦:Git之遠端分支

一次 Git 克隆會建立本地分支 master 和遠端分支 origin/master,它們都指向 origin/master 分支的最後一次提交。

如果在本地 master 分支做了些改動,與此同時,其他人向 git。ourcompany。com 推送了他們的更新,那麼伺服器上的master 分支就會向前推進,而於此同時,在本地的提交歷史正朝向不同方向發展。不過只要不和伺服器通訊,你的 origin/master 指標仍然保持原位不會移動:

好文推薦:Git之遠端分支

可以執行

git fetch origin

來同步遠端伺服器上的資料到本地。該命令首先找到 origin 是哪個伺服器,從上面獲取尚未擁有的資料,更新本地的資料庫,然後把 origin/master 的指標移到它最新的位置上:

好文推薦:Git之遠端分支

要想和其他人分享某個本地分支,需要把它push到一個擁有寫許可權的遠端倉庫。本地分支不會被自動同步到引入的遠端伺服器上,除非明確執行推送操作。換句話說,對於無意分享的分支,儘管保留為私人分支好了,而只推送那些協同工作要用到的特性分支。

如果有個叫 serverfix 的分支需要和他人一起開發,可以執行

git push origin serverfix

。也可以執行 ·git push origin serverfix:serferfix· 來實現相同的效果,它的意思是“上傳我本地的 serverfix 分支到遠端倉庫中去,仍舊稱它為 serverfix 分支”。透過此命令,可以把本地分支推送到某個命名不同的遠端分支:若想把遠端分支叫作awesomebranch,可以用

git push origin serverfix:awesomebranch

來推送資料。

接下來,當你的協作者再次從伺服器上獲取資料時,他們將得到一個新的遠端分支 origin/serverfix。

值得注意的是,在 你的協作者fetch 好新的遠端分支之後,仍然無法在本地編輯該遠端倉庫中的分支。換句話說,在本例中,不會有一個新的serverfix 分支,有的只是一個無法移動的 origin/serverfix 指標。

如果要把該內容合併到當前分支,可以執行 git merge origin/serverfix。如果想要一份自己的 serverfix 來開發,可以在遠端分支的基礎上分化出一個新的分支來:

git checkout -b serverfix origin/serverfix

這會切換到新建的 serverfix 本地分支,其內容同遠端分支 origin/serverfix 一致,這樣你就可以在裡面繼續開發了。

從遠端分支 checkout 出來的本地分支,稱為

跟蹤分支

(tracking branch)。跟蹤分支是一種和遠端分支有直接聯絡的本地分支。在跟蹤分支裡輸入

git push

,Git 會自行推斷應該向哪個伺服器的哪個分支推送資料。反過來,在這些分支裡執行 git pull 會獲取所有遠端索引,並把它們的資料都合併到本地分支中來。

在克隆倉庫時,Git 通常會自動建立一個名為 master 的分支來跟蹤 origin/master。這正是

一開始就能正常工作的原因。當然,你可以隨心所欲地設定為其它跟蹤分支,比如origin 上除了 master 之外的其它分支:

git checkout -b [分支名] [遠端名]/[分支名]

還可以“——track” 選項簡化:

git checkout ——track origin/serverfix

要為本地分支設定不同於遠端分支的名字,只需在前個版本的命令裡換個名字:

git checkout -b sf origin/serverfix

現在你的本地分支 sf 會自動向 origin/serverfix 推送和抓取資料了。

如果不再需要某個遠端分支了,可以用這個非常無厘頭的語法來刪除它:

git push [遠端名] :[分支名]

如果想在伺服器上刪除serverfix 分支,執行下面的命令:

git push origin :serverfix

這個命令比較怪異,其實它與“git push [遠端名] [本地分支]:[遠端分支] ”的語法一樣,只不過省略 [本地分支],等於是在說“在這裡提取空白然後把它變成[遠端分支]”,也就是刪除。

8、標籤管理

釋出一個版本時,我們通常先在版本庫中打一個

標籤

(tag),這樣,就唯一確定了打標籤時刻的版本。將來無論什麼時候,取某個標籤的版本,就是把那個打標籤的時刻的歷史版本取出來。所以,標籤也是版本庫的一個快照。

8。1 新建標籤

Git 使用的標籤有兩種型別: 輕量級的(lightweight)和含附註的(annotated)。輕量級標籤就像是個不會變化的分支,實際上它就是個指向特定提交物件的引用。而含附註標籤,實際上是儲存在倉庫中的一個獨立物件,它有自身的校驗和資訊,包含著標籤的名字,電子郵件地址和日期,以及標籤說明,標籤本身也允許使用 GNU Privacy Guard (GPG) 來簽署或驗證。一般我們都建議使用含附註型的標籤,以便保留相關資訊;當然,如果只是臨時性加註標籤,或者不需要旁註額外資訊,用輕量級標籤也沒問題。

建立一個含附註型別的標籤非常簡單,用 -a (取 annotated 的首字母)指定標籤名字即可:

git tag -a v1。1 -m ‘My version 1。1’

而 -m 選項則指定了對應的標籤說明,Git 會將此說明一同儲存在標籤物件中。如果沒有給出該選項,Git 會啟動文字編輯軟體供你輸入標籤說明。

輕量級標籤實際上就是一個儲存著對應提交物件的校驗和資訊的檔案。要建立這樣的標籤,一個 -a,-s 或 -m 選項都不用,直接給出標籤名字即可:

git tag v1。1

Git還支援對早先的某次提交加註標籤。比如在下面展示的提交歷史中,只要在打標籤的時候跟上對應提交物件的校驗和(或前幾位字元)即可:

git tag -a v1。2 9fceb02

8。2 檢視標籤

列出現有標籤的命令非常簡單,直接執行:

git tag

顯示的標籤只是按字母順序排列,並不表示重要程度的高低。

我們可以用特定的搜尋模式列出符合條件的標籤。在 Git 自身專案倉庫中,有著超過 240 個標籤,如果只對 1。4。2 系列的版本感興趣,可以執行下面的命令:

git tag -l ‘v1。4。2。*’

v1。4。2。1

v1。4。2。2

v1。4。2。3

v1。4。2。4

可以使用 git show 命令檢視相應標籤的版本資訊,並連同顯示打標籤時的提交物件。

git show v1。1

tag v1。1

Tagger: chenlongfei

chenlongfei@163。com

Date: Mon Jul 18 10:07:21 2016 +0800

My version 1。1

commit 91c233e97a5a88218b93bf1b57b9383377153587

Author: chenlongfei

Date: Fri Jul 15 17:14:50 2016 +0800

如果有自己的私鑰,還可以用 GPG 來簽署標籤,只需要把之前的 -a 改為 -s (取signed 的首字母)即可。

8。3 推送標籤

預設情況下,

並不會把標籤傳送到遠端伺服器上,只有透過顯式命令才能分享標籤到遠端倉庫。其命令格式如同推送分支,執行

git push origin [tagname]

即可:

git push origin v1。5

如果要一次推送所有本地新增的標籤上去,可以使用“——tags”選項。

8。4 刪除標籤

在本地刪除標籤很簡單,加上“-d”引數:

git tag -d v0。1

如果標籤已經推送到遠端倉庫,就要複雜一點,首先用上面的命令在本地刪除,然後,將該操作cimmit之後push到遠端倉庫,push命令的格式如下:

git push origin :refs/tags/v1。1

與刪除遠端分支的語法類似。冒號前實際上是一個空物件,代表用一個空物件取代遠端倉庫的v1。1標籤,其實就是刪除操作。

頂部