前情提要:Git應用詳解第六講:Git協做與Git pull常見問題html
這一節來介紹本地倉庫與遠程倉庫的分支映射關係:git refspec
。完全弄清楚本地倉庫究竟是如何與遠程倉庫進行聯繫的。git
Git refspec
refspec
是Reference Specification
的縮寫,字面意思就是具體的引用。它實際上是一種格式,git
經過這種格式來表示本地分支與遠程分支的映射關係;github
在本地倉庫建立master
分支外的其餘兩個分支develop
和test
:vim
在develop
分支上執行git push
命令,出現以下錯誤:less
這是因爲本地分支develop
沒有與任何的遠程分支創建聯繫致使的。經過git branch -vv
查看本地與遠程分支的關聯狀況,可見並無創建任何聯繫:編輯器
在講解如何創建與本地分支關聯的遠程分支以前,首先咱們來介紹期待已久的本地遠程分支:學習
git
中其實有三種分支:本地分支、本地遠程分支、遠程分支;origin/develop
就對應着遠程分支develop
。當本地master
分支創建了與之關聯的遠程分支master
後,查看當前分支狀態:fetch
圖中的origin/master
爲本地遠程分支,表明的是遠程倉庫的master
分支,而這個分支是在本地的;也就是說加上遠程倉庫的master
分支,一共有三個master
分支:url
而且,當本地倉庫中的每個分支都有與之關聯的遠程分支以後,本地倉庫都會建立對應的本地遠程分支,它們所處的位置和關係以下圖所示:指針
能夠這樣理解:本地遠程分支
origin/master
爲遠程分支master
的本地化形式;
假設遠程倉庫和本地倉庫文件內容是同樣的,都只有兩次提交,此時三個分支的狀態以下圖所示:
而後,在本地的master
分支中新增了提交3rd
,本地倉庫的分支狀況變爲:
上圖中的
git dog
爲指令:git log --all --decorate --oneline --graph
的別名,有關內容將在下一節講解。
分支的示意圖以下:
可見本地master
分支比本地遠程分支origin/master
多了一次提交。這是由於本地遠程分支是爲了追蹤遠程分支而存在的,只有在執行pull
或push
操做時它的指向纔會更新。好比在執行推送(push
)指令時:
首先,本地master
分支對應的本地遠程分支(origin/master
)會指向本地master
分支最新的提交(向前走了幾步);
而後,本地master
分支再將文件推送到遠程master
分支中。完成推送後,三分支的狀態爲:
回到終端,咱們將剛纔新增的提交3rd
推送到遠程分支,成功後查看本地分支以及本地遠程分支的提交歷史:
可見,本地遠程分支的指向獲得了更新,指向了最新的提交3rd
,由此驗證了上述說法。
查看分支關聯
能夠經過如下指令查看本地分支與本地遠程分支的關聯狀況:
git branch -vv
能夠看到:本地的master
分支有本地遠程分支origin/master
關聯,說明本地master
分支已經和遠程master
分支創建了關聯;
其他兩個本地分支pop
和develop
並無與之關聯的本地遠程分支,因此它們並無與遠程分支創建聯繫。
簡單點說:只要本地分支有與之對應的本地遠程分支,就有與之對應的遠程分支。
總結:origin/master
做用:追蹤遠程分支。當執行git push/pull
操做時,該分支的指向都會相應地發生變化,用於與遠程倉庫保持同步;好比:本地倉庫在執行git push
操做的時候,不只會把本地的修改推送到遠程;還會同時修改origin/master
分支的指向;
可經過該指令查看本地的全部分支及其最新的提交信息:
git branch -av
首先,在master
分支上進行三次提交,並將它推送到與之關聯的遠程master
分支,此時各分支的提交歷史爲:
三個分支的狀態爲:
在此基礎上,在master
分支上進行一次提交4th
,而後查看狀態git status
:
圖中提示信息代表,當前分支(master
)已經領先於origin/master
分支一次提交。爲了看得更清楚,咱們查看本地各分支的提交歷史:
從圖中可看出,origin/master
分支確實落後了一次提交,表示遠程master
分支落後了一次提交。此時可使用git push
將新增的提交推送到遠程master
分支,在這個過程當中會將本地遠程分支origin/master
指向最新的提交4th
。成功推送以後,再次查看本地各分支的提交歷史:
可見,經過git push
操做本地遠程分支確實發生了更新,指向了最新提交4th
。這就驗證了執行git push
時進行了兩步操做:
master
分支的新提交推送到與之關聯的遠程master
分支;origin/master
指向本地master
分支的最新提交;git pull
操做同理,也會更新本地遠程分支的指向;
也就是說:每次執行push
或pull
操做後,本地分支、本地遠程分支、遠程分支三個分支的指向都會達到同步。
當切換到origin/master
分支上時,以下圖所示:
git
並不會直接將分支切換到origin/master
上,而是切換到最新的一次提交上,即一個遊離的提交。這從側面說明了:git
是禁止咱們直接修改origin/master
分支的,只容許咱們切換到最新的提交上;
也就是說本地遠程分支(如origin/master
)是隻讀的,只能由git
來改變,這就解釋了爲什麼使用git branch
沒法查看本地遠程分支。
弄清楚了什麼是本地遠程分支,就能更好地理解接下來所要介紹的,如何創建本地分支與遠程分支的聯繫了。
上圖提示信息中的:upstream branch
表示上游分支,即遠程倉庫的分支。當前的本地分支develop
並無一個遠程倉庫的develop
分支與之對應;要想推送develop
分支到遠程倉庫的同名分支,首先要建立對應的遠程分支,有如下兩種類型四種方法:
類型一:創建本地與遠程分支追蹤關係的。
git push --set-upstream origin <branch> git push -u origin <branch>
使用該類型方法,只需設置一次,以後就可使用簡寫形式git push
進行推送。
類型二:不創建本地與遠程分支追蹤關係的。
git push origin HEAD git push origin <branch_name>
使用該類型方法,每次推送都須要採用上述的完整寫法。
下面就來詳細介紹這四種方法:
git push --set-upstream origin <branch>
方法一:採用下述指令爲本地倉庫mygit
的develop
分支建立遠程分支:
git push --set-upstream origin develop
該命令的做用爲:在遠程倉庫創鍵一個與本地分支develop
關聯的同名分支develop
,並將本地分支develop
的文件推送到該遠程分支上。
也就是將本地分支develop
的上游分支設置爲遠程倉庫的develop
分支,並進行文件同步。
執行完上述命令後會有這樣的提示:
表示本地的develop
分支已與遠程的develop
分支創建聯繫;此時查看本地分支,會發現多了一個本地遠程分支origin/develop
,而且已與本地develop
分支創建了聯繫:
隨後再次執行git push
就不會出現問題了:
此時在github
上查看對應的遠程倉庫,就能查看到新增的遠程分支develop
了:
上圖中的
master
分支是遠程倉庫建立時默認建立的,並無與本地master
分支創建聯繫。
隨後點開branch
能夠看到:
當前一共有兩個分支,master
分支是default
(默認)分支,是不可以被刪除的;活躍的分支爲deavelop
;
git push -u origin <branch>
方法二:先切換到test
分支,再執行如下命令,爲本地倉庫mygit
的test
分支建立對應的遠程分支:
git push -u origin test
-u
與--set-upstream
做用是相似的,都是在遠程倉庫新建一個新的分支,並與本地分支創建聯繫。
執行完上述指令後,再次查看本地分支的詳細狀況,以及分支對應關係,能夠發現test
分支已與遠程test
分支創建聯繫:
git push origin HEAD
方法三:
以下圖所示,經過該指令成功設置了本地develop
分支對應的遠程develop
分支。但沒有顯示追蹤信息,以後不能使用git push
推送。
git push origin <branch>
方法四:
以下圖所示,該方法實質上與方法三相同,由於HEAD
指向的就是當前分支。一樣沒有顯示追蹤信息,以後也不能使用git push
推送。
總結:當本地分支與遠程分支同名時,一旦手動創建了它們之間的聯繫。以後推送本地分支的文件到對應的遠程分支時能夠採用簡寫形式:
git push
。這是由於在已經創建三個分支的對應關係並後,再執行
git push
,git
會自動地將同名的本地分支與遠程分支進行匹配;而其餘狀況則要採用完整寫法進行推送。關於這些結論,將在第三大點
-u
參數的做用中詳細介紹。
主要有如下四種方法,注意:使用每種方法前都須要先切換到對應分支上。
git push --set-upstream origin <branch1>:<branch2>
方法一: 好比當前位於develop
分支,若是採用的是如下簡寫命令:
git push --set-upstream origin develop
則會建立一個同名的遠程分支develop
。而若是採用該命令的完整寫法,就能夠自定義遠程分支的名字了,好比設爲develop2
:
git push --set-upstream origin develop:develop2
執行上述指令後,成功建立了對應的,不一樣名的本地遠程分支origin/develop2
。表示本地develop
分支已與遠程develop2
分支創建聯繫(由於遠程分支與本地遠程分支是一一對應的關係):
在github
上查看本地倉庫關聯的遠程倉庫MY
,能夠看到順利建立了develop2
分支:
能夠發現這麼一個規律:在建立遠程分支的同時會建立同名的本地遠程分支。
git push -u origin <branch1>:<branch2>
方法二:
以下圖所示,使用-u
參數也能將本地develop
分支的遠程分支自定義爲develop2
。
git push origin HEAD:<branch>
方法三:
經過該方法也能成功設置與本地分支關聯的,不一樣名的遠程分支develop2
:
git push origin <branch1>:<branch2>
方法四:該方法與方法二實質上是同樣的,由於方法二中的HEAD
指針指向的就是當前所在的分支,也就是develop
分支。過程與方法二相似:
上面這四種設置不一樣名遠程分支的方法,都有一個共同特色:不能使用
git push
進行推送。
若使用git push
都會出現找不到對應遠程分支的錯誤:
緣由在下面第三點的-u
參數做用中會詳細講解。
既然是-u
參數追蹤問題,那我加上-u
參數不就好了麼?其實這樣也行不通:
解決方案:每次推送的時候,指明本地分支與遠程分支的對應關係,即採用上述命令的完整寫法,好比:
git push --set-upstream origin develop:develop2 git push -u origin develop:develop2 git push origin develop:develop2 git push origin HEAD:develop2
採用了完整寫法後,成功地進行了推送,以下圖所示:
注意:雖然能夠自定義遠程分支與本地遠程分支的名字,可是十分不推薦,由於容易出錯。因此,建議本地遠程分支和遠程分支都使用默認的,與本地分支相同的名字。
以本地分支develop
爲例,不難發現:
使用下列簡寫命令時,遠程分支和本地遠程分支都會採用默認的,與本地分支相同的名字:
git push --set-upstream origin develop git push -u origin develop
而使用下列命令的完整寫法時,就能夠自定義遠程分支與本地遠程分支的名字:
git push --set-upstream origin develop:develop2 git push -u origin develop:develop2 git push origin develop:develop2 git push origin HEAD:develop2
git push origin master
與git push -u origin master
的區別第一次將本地倉庫的master
分支推送到遠程倉庫的master
分支上時,使用前者和後者均可以順利推送,區別在因而否使用了-u
參數:
推送時不使用-u
參數:
推送時使用-u
參數:
注意到推送時使用-u
參數會打印下列提示信息:
Branch 'master' set up to track remote branch 'master' from 'origin'.
表示本地的master
分支被設置去追蹤遠程的master
分支,在第2~n
次推送中,只須要使用git push
這樣的簡寫命令(固然,完整寫法效果等同)。git
就會自動將本地的master
分支與遠程的master
分支進行匹配,完成推送:
而不使用-u
參數時,沒有上述的分支追蹤信息。此時使用簡寫git push
進行推送會出現錯誤:
錯誤信息顯示:當前分支沒有與之對應的遠程分支。這個時候想要成功推送,必須採用指明對應關係的完整寫法,好比:
git push origin master
這就是推送時使不使用-u
參數的區別。而且,根據上面的介紹,使用以下指令進行推送也能達到-u
參數的效果:
git push --set-upstream origin develop
以後也可使用簡寫的git pull
指令進行推送:
細心的你必定發現了,以上都只是本地分支與遠程分支同名的狀況。不一樣名的狀況下,上面的兩個方法還好使嗎?
首先驗證方法一:-u
參數:
設置不一樣名的遠程分支時要注意寫成完整形式:
pop:pop2
能夠看到,即便建立不一樣名的遠程分支,-u
參數也同樣可以設置追蹤關係;可是,奇怪的是git push
卻很差使了:
仍是和沒使用-u
參數時同樣,找不到對應的遠程分支,須要採用指明對應關係的完整寫法,好比:
git push origin pop:pop2
其次驗證方法二:--set-upstream
:
一樣設置分支對應關係時要使用完整寫法。能夠看到,該方法也設置了追蹤關係。奇怪的是git push
一樣無論用:
一樣找不到對應的遠程分支,須要採用指明對應關係的完整寫法,好比:
git push origin bob:bob2
因此能夠得出結論:
-u
參數的做用是設置本地分支與遠程分支的追蹤關係,設置了追蹤關係後,以後的推送可以使用簡寫git push
,git
內部會自動進行匹配;--set-upstream
參數與-u
參數效果等同;--set-upstream
參數與-u
參數依然能夠設置分支的追蹤關係,可是,以後的推送不能使用簡寫git push
,只能使用指定分支對應關係的完整寫法;總結:十分建議將全部的本地分支與對應的遠程分支設爲同名,而且第一次推送使用--set-upstream
或-u
參數創建分支追蹤關係,以後就可使用簡寫git push
進行推送了!
git push -f
該命令的完整寫法爲:
git push -f origin master
意思爲強制推送:直接跳過與遠程倉庫的master
分支合併的環節,強制覆蓋遠程倉庫上master
分支的內容,即以本地的master
分支內容爲準。應慎用該命令,不然將覆蓋遠程倉庫中master
分支上其餘人推送的文件(一星期的成果沒了)。
-f
強制推送,直接覆蓋遠程分支上的內容;分兩種寫法:
第一種:已經經過-u
參數等方式,設置了本地分支與遠程分支的追蹤關係時,採用:
git push -f
第二種:還未設置追蹤關係,採用:
git push -u origin master -f
Github
提供了相應的分支保護機制,能夠在Settings
選項中進行設置:
能夠看到Github
默認是保護分支的:
讓有進度的人,再次對被強制覆蓋的遠程分支執行一次 git push -f
指令,把正確的內容強制推送上去,覆蓋前一次 git push -f
所形成的災難。
假如遠程倉庫M3Y
中有master
和develop
兩個分支,此時新建一個空的本地倉庫mygit
,經過如下指令將它的遠程倉庫地址origin
設置爲M3Y
的地址:
git remote add origin git@github.com:AhuntSun/M3Y.git
此時兩倉庫的狀態爲:
因爲mygit
是空倉庫與遠程倉庫M3Y
沒有任何公共提交歷史,因此在執行git pull
時會出現下圖所示的不一樣源衝突(上一節中詳細介紹過該衝突):
雖然git pull
操做失敗了,可是也成功地將遠程倉庫M3Y
的分支拉取了下來。可是,經過git branch -vv
查看分支追蹤關係,發現並無本地分支與這兩個遠程分支創建了聯繫:
如何創建這兩個本地遠程分支對應的本地分支?能夠經過如下兩種方法:
git checkout -b <branch> origin/<branch>
好比能夠經過如下命令,設置本地遠程分支origin/master
與本地master
分支的追蹤關係:
git checkout -b master origin/master
以上爲本地master
分支已存在的狀況,若是本地分支develop
未建立,能夠採用下述命令建立並切換到develop
分支,而且設置origin/develop
與develop
的追蹤關係:
git checkout -b develop origin/develop
設置了本地分支與遠程分支的追蹤關係,接下來就能夠在本地倉庫執行git push
進行推送了:
git checkout --track origin/<branch>
重置條件,新建立一個空的本地倉庫mygit2
,一樣將其遠程地址origin
設置爲遠程倉庫M3Y
的地址。隨後在本地倉庫mygit2
中執行git pull
操做,將遠程倉庫M3Y
中的兩個分支拉取到本地:
與上次同樣,拉取到本地的兩個本地遠程分支沒有與任何本地分支創建追蹤關係。此次能夠採用另一種方法:
git checkout --track origin/test
建立並切換到develop
分支,而且設置該分支與origin/develop
分支的追蹤關係:
能夠說該方法是方法一的特殊狀況,由於該方法沒有指明建立的本地分支的名字,因此默認採用與遠程分支同樣的名字develop
來命名;
若是想在本地創建一個develop2
(不一樣名)的分支與本地遠程分支origin/test
創建追蹤關係,則應採用第一種方法。
能夠進入.git
目錄,查看儲存遠程分支信息的文件:
config
文件使用vim
編輯器打開該文件,能夠查看到關於遠程分支的信息:
能夠看到remote
這一欄中有兩個信息,第一個是遠程倉庫的url
,第二個是fetch
信息,這兩個信息尤其重要:
refs/heads/*
表示遠程倉庫的refs/heads
目錄下的全部引用都會寫入到本地的refs/remotes/origin
目錄中;+
號是可選的,加了表示不管是否可以自動合併,便是否爲Fast Forward
方式,都將遠程倉庫全部文件拉取到本地。+
則表示若是不是Fast Forward
方式就不拉取。通常狀況下都是加上+
號的,先把文件拉取到本地,不是Fast Forward
方式就手動合併;refs
文件refs
文件夾存儲着refspec
的文件,裏面維護着三個目錄:
第一個目錄heads
:存儲的是本地倉庫的分支信息:
能夠查看其中一個分支:
是一個SHA1
值,表示分支就是一個指針,指向當前提交。
第二個目錄remotes
:裏面存放着遠程分支信息,遠程倉庫中也存在這樣的目錄與文件;
從上圖能夠看到,遠程分支只有master
,沒有develop
(由於以前被刪除了)。而且它們本質上也是一個表明提交的SHA1
值:
創建refspec
映射(即本地分支、本地遠程分支、遠程分支三者間對應關係)後,git
會獲取遠端上refs/heads
下的全部引用,並將它們寫入本地的refs/remotes/origin
目錄下。因此,能夠經過查看本地遠程分支(如origin/master
)的方式查看本地倉庫最後一次訪問遠程倉庫時,遠程倉庫master
分支上的歷史提交記錄:
//完整寫法 git log refs/remotes/origin/master //進一步簡寫 git log remotes/origin/master //繼續簡寫 git log origin/master
上述兩種省略的寫法最終都會轉換爲完整的寫法:
第三個目錄tags
:存放標籤信息,也是一個SHA1
值:
詳細內容將在下一節介紹。
以下圖所示,遠程倉庫有三個分支master
、develop
和test
:
經過前面的學習,咱們知道經過下述指令能夠刪除本地develop
分支:
git branch -d develop
那麼如何刪除遠程分支呢?
首先咱們來看看git push
的完整寫法:
git push origin srcBranch:destBranch
srcBranch
表示本地的分支,destBranch
表示對應的遠程分支;
表示將本地的分支推送到遠程分支,這兩個分支能夠不一樣;
之因此能夠直接使用git push
是由於咱們設置的本地分支和遠程分支的名字是相同的,而且手動創建了聯繫,因此git
可以自動識別;
明白了這點後,就不難理解下列刪除遠程分支的兩種作法了:
git push origin :destBranch
將空的分支推送到遠程分支,這樣就能將該遠程分支刪除;好比刪除本地分支develop
的遠程分支:
git push origin :develop
能夠看到成功刪除了遠程分支develop
以及它所對應的本地遠程分支origin/develop
。
注意:並不須要切換到須要刪除遠程分支的本地分支
develop
上,再執行上述指令。也就是說,能夠在任意本地分支上刪除任意本地分支對應的遠程分支。
git push origin --delete destBranch
還能夠採用更加直觀的--delete
參數,好比刪除遠程分支develop
:
git push origin --delete develop
這兩種方式是等價的,可根據需求選擇。
git remote prune origin
該方法用於刪除無效的遠程分支對應的本地遠程分支,具體場合以下:
如圖所示mygit
與mygit2
共享一個有三個分支的遠程倉庫:
首先在mygit2
中刪除遠程倉庫的develop
分支,能夠看到mygit2
中遠程分支develop
對應的本地遠程分支origin/develop
被刪除了:
而後在mygit
中查看遠程分支詳細信息:
能夠看到提示信息中顯示遠程分支develop
對應的本地遠程分支origin/develop
處於stale
(腐爛,遊離)狀態,即該分支對於mygit
來講已經失效,可使用:
git remote prune origin
(prune
:裁剪)刪除mygit
上這個無效的本地遠程分支:
再次查看分支信息,可發現mygit
中的本地遠程分支origin/develop
已經被刪除了:
注意:通常本地遠程分支設置了保護措施,不能隨意刪除;
能夠經過如下命令,將本地分支dev
重命名爲develop
:
git branch -m dev develop
沒法直接重命名遠程分支,只能經過先刪除原來的遠程分支,再建立重命名後develop
分支對應的遠程分支,過程爲:
//刪除遠程分支dev git push origin :dev //建立重命名後develop分支對應的遠程分支 git push -u origin develop
由此間接地完成了遠程分支的重命名。
以上就是本節的所有內容,相信看到這裏的你已經十分熟悉
git refspec
了。下一節將介紹git
標籤與別名。