Git應用詳解第七講:Git refspec與遠程分支的重要操做

前言

前情提要:Git應用詳解第六講:Git協做與Git pull常見問題html

這一節來介紹本地倉庫與遠程倉庫的分支映射關係:git refspec。完全弄清楚本地倉庫究竟是如何與遠程倉庫進行聯繫的。git

1、Git refspec

refspecReference Specification的縮寫,字面意思就是具體的引用。它實際上是一種格式git經過這種格式來表示本地分支遠程分支的映射關係;github

在本地倉庫建立master分支外的其餘兩個分支developtest:vim

image-20200329114836186

develop分支上執行git push命令,出現以下錯誤:less

image-20200329114910354

這是因爲本地分支develop沒有與任何的遠程分支創建聯繫致使的。經過git branch -vv查看本地與遠程分支的關聯狀況,可見並無創建任何聯繫:編輯器

image-20200416172209683

2、本地遠程分支

在講解如何創建與本地分支關聯的遠程分支以前,首先咱們來介紹期待已久的本地遠程分支:學習

  • git中其實有三種分支:本地分支、本地遠程分支、遠程分支;
  • 能夠這樣理解:本地遠程分支是遠程分支的一個鏡像,而且在本地倉庫與遠程倉庫之間起到一個橋樑的做用;
  • 在沒有辦法直接查看遠程倉庫的時候,能夠經過本地遠程分支觀察遠程分支的變化狀況。好比本地遠程分支origin/develop就對應着遠程分支develop

1.三分支關係

當本地master分支創建了與之關聯的遠程分支master後,查看當前分支狀態:fetch

image-20200416172925457

圖中的origin/master爲本地遠程分支,表明的是遠程倉庫的master分支,而這個分支是在本地的;也就是說加上遠程倉庫的master分支,一共有三個master分支:url

image-20200407133027775

而且,當本地倉庫中的每個分支都有與之關聯的遠程分支以後,本地倉庫都會建立對應的本地遠程分支,它們所處的位置和關係以下圖所示:指針

image-20200407134533388

能夠這樣理解:本地遠程分支origin/master爲遠程分支master的本地化形式;

假設遠程倉庫和本地倉庫文件內容是同樣的,都只有兩次提交,此時三個分支的狀態以下圖所示:

image-20200407133442447

而後,在本地的master分支中新增了提交3rd,本地倉庫的分支狀況變爲:

image-20200416174501257

上圖中的git dog爲指令:git log --all --decorate --oneline --graph的別名,有關內容將在下一節講解。

分支的示意圖以下:

image-20200407133629409

可見本地master分支比本地遠程分支origin/master多了一次提交。這是由於本地遠程分支是爲了追蹤遠程分支而存在的,只有在執行pullpush操做時它的指向纔會更新。好比在執行推送(push)指令時:

  • 首先,本地master分支對應的本地遠程分支(origin/master)會指向本地master分支最新的提交(向前走了幾步);

  • 而後,本地master分支再將文件推送到遠程master分支中。完成推送後,三分支的狀態爲:

    image-20200416175059690

回到終端,咱們將剛纔新增的提交3rd推送到遠程分支,成功後查看本地分支以及本地遠程分支的提交歷史:

image-20200416180507680

可見,本地遠程分支的指向獲得了更新,指向了最新的提交3rd,由此驗證了上述說法。

查看分支關聯

能夠經過如下指令查看本地分支與本地遠程分支的關聯狀況:

git branch -vv

image-20200410120936906

能夠看到:本地的master分支有本地遠程分支origin/master關聯,說明本地master分支已經和遠程master分支創建了關聯;

其他兩個本地分支popdevelop並無與之關聯的本地遠程分支,因此它們並無與遠程分支創建聯繫。

簡單點說:只要本地分支有與之對應的本地遠程分支,就有與之對應的遠程分支

總結:origin/master做用:追蹤遠程分支。當執行git push/pull操做時,該分支的指向都會相應地發生變化,用於與遠程倉庫保持同步;好比:本地倉庫在執行git push操做的時候,不只會把本地的修改推送到遠程;還會同時修改origin/master分支的指向;

2.實戰演示

可經過該指令查看本地的全部分支及其最新的提交信息

git branch -av

image-20200328164526656

首先,在master分支上進行三次提交,並將它推送到與之關聯的遠程master分支,此時各分支的提交歷史爲:

image-20200416181958738

三個分支的狀態爲:

image-20200416182211614

在此基礎上,在master分支上進行一次提交4th,而後查看狀態git status

image-20200416182358491

圖中提示信息代表,當前分支(master)已經領先於origin/master分支一次提交。爲了看得更清楚,咱們查看本地各分支的提交歷史:

image-20200416182650885

從圖中可看出,origin/master分支確實落後了一次提交,表示遠程master分支落後了一次提交。此時可使用git push將新增的提交推送到遠程master分支,在這個過程當中會將本地遠程分支origin/master指向最新的提交4th。成功推送以後,再次查看本地各分支的提交歷史:

image-20200416182921683

可見,經過git push操做本地遠程分支確實發生了更新,指向了最新提交4th。這就驗證了執行git push時進行了兩步操做:

  • 將本地master分支的新提交推送到與之關聯的遠程master分支;
  • 將本地遠程分支origin/master指向本地master分支的最新提交;

git pull操做同理,也會更新本地遠程分支的指向;

也就是說:每次執行pushpull操做後,本地分支、本地遠程分支、遠程分支三個分支的指向都會達到同步。

當切換到origin/master分支上時,以下圖所示:

image-20200416183352637

git並不會直接將分支切換到origin/master上,而是切換到最新的一次提交上,即一個遊離的提交。這從側面說明了:git禁止咱們直接修改origin/master分支的,只容許咱們切換到最新的提交上;

也就是說本地遠程分支(如origin/master)是隻讀的,只能由git來改變,這就解釋了爲什麼使用git branch沒法查看本地遠程分支。

image-20200416183719001

3、設置遠程分支

弄清楚了什麼是本地遠程分支,就能更好地理解接下來所要介紹的,如何創建本地分支與遠程分支的聯繫了。

1.設置同名遠程分支

上圖提示信息中的: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>

方法一:採用下述指令爲本地倉庫mygitdevelop分支建立遠程分支:

git push --set-upstream origin develop

該命令的做用爲:在遠程倉庫創鍵一個與本地分支develop關聯的同名分支develop,並將本地分支develop的文件推送到該遠程分支上。

也就是將本地分支develop的上游分支設置爲遠程倉庫的develop分支,並進行文件同步。

執行完上述命令後會有這樣的提示:

image-20200416172401646

表示本地的develop分支已與遠程的develop分支創建聯繫;此時查看本地分支,會發現多了一個本地遠程分支origin/develop,而且已與本地develop分支創建了聯繫:

image-20200416235458838

隨後再次執行git push就不會出現問題了:

image-20200329120642796

此時在github上查看對應的遠程倉庫,就能查看到新增的遠程分支develop了:

image-20200329122141124

上圖中的master分支是遠程倉庫建立時默認建立的,並無與本地master分支創建聯繫。

隨後點開branch能夠看到:

image-20200329122249532

當前一共有兩個分支,master分支是default(默認)分支,是不可以被刪除的;活躍的分支爲deavelop

git push -u origin <branch>

方法二:先切換到test分支,再執行如下命令,爲本地倉庫mygittest分支建立對應的遠程分支:

git push -u origin test

image-20200417000257150

-u--set-upstream做用是相似的,都是在遠程倉庫新建一個新的分支,並與本地分支創建聯繫。

執行完上述指令後,再次查看本地分支的詳細狀況,以及分支對應關係,能夠發現test分支已與遠程test分支創建聯繫:

image-20200417000515217

git push origin HEAD

方法三:

以下圖所示,經過該指令成功設置了本地develop分支對應的遠程develop分支。但沒有顯示追蹤信息,以後不能使用git push推送。

image-20200417121129943

git push origin <branch>

方法四:

以下圖所示,該方法實質上與方法三相同,由於HEAD指向的就是當前分支。一樣沒有顯示追蹤信息,以後也不能使用git push推送。

image-20200417121419529

總結:當本地分支與遠程分支同名時,一旦手動創建了它們之間的聯繫。以後推送本地分支的文件到對應的遠程分支時能夠採用簡寫形式:git push

這是由於在已經創建三個分支的對應關係並後,再執行git pushgit會自動地將同名的本地分支與遠程分支進行匹配;

而其餘狀況則要採用完整寫法進行推送。關於這些結論,將在第三大點-u參數的做用中詳細介紹。

2.設置不一樣名遠程分支

主要有如下四種方法,注意:使用每種方法前都須要先切換到對應分支上。

git push --set-upstream origin <branch1>:<branch2>

方法一: 好比當前位於develop分支,若是採用的是如下簡寫命令:

git push --set-upstream origin develop

則會建立一個同名的遠程分支develop。而若是採用該命令的完整寫法,就能夠自定義遠程分支的名字了,好比設爲develop2

git push --set-upstream origin develop:develop2

image-20200417001750391

執行上述指令後,成功建立了對應的,不一樣名的本地遠程分支origin/develop2。表示本地develop分支已與遠程develop2分支創建聯繫(由於遠程分支與本地遠程分支是一一對應的關係):

image-20200417001824384

github上查看本地倉庫關聯的遠程倉庫MY,能夠看到順利建立了develop2分支:

image-20200417002057662

能夠發現這麼一個規律:在建立遠程分支的同時會建立同名的本地遠程分支。

git push -u origin <branch1>:<branch2>

方法二:

以下圖所示,使用-u參數也能將本地develop分支的遠程分支自定義爲develop2

image-20200417103419756

git push origin HEAD:<branch>

方法三:

經過該方法也能成功設置與本地分支關聯的,不一樣名的遠程分支develop2

image-20200417003727161

git push origin <branch1>:<branch2>

方法四:該方法與方法二實質上是同樣的,由於方法二中的HEAD指針指向的就是當前所在的分支,也就是develop分支。過程與方法二相似:

image-20200417004155159

上面這四種設置不一樣名遠程分支的方法,都有一個共同特色:不能使用git push進行推送

若使用git push都會出現找不到對應遠程分支的錯誤:

image-20200417004455754

緣由在下面第三點的-u參數做用中會詳細講解。

既然是-u參數追蹤問題,那我加上-u參數不就好了麼?其實這樣也行不通:

image-20200417005033407

解決方案:每次推送的時候,指明本地分支與遠程分支的對應關係,即採用上述命令的完整寫法,好比:

git push --set-upstream origin develop:develop2
git push -u origin develop:develop2
git push origin develop:develop2
git push origin HEAD:develop2

採用了完整寫法後,成功地進行了推送,以下圖所示:

image-20200407215703682

注意:雖然能夠自定義遠程分支與本地遠程分支的名字,可是十分不推薦,由於容易出錯。因此,建議本地遠程分支和遠程分支都使用默認的,與本地分支相同的名字。

3.總結

以本地分支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

4、git push origin mastergit push -u origin master的區別

第一次將本地倉庫的master分支推送到遠程倉庫的master分支上時,使用前者和後者均可以順利推送,區別在因而否使用了-u參數:

  • 推送時不使用-u參數:

    image-20200407204915066

  • 推送時使用-u參數:

    image-20200407205035250

注意到推送時使用-u參數會打印下列提示信息:

Branch 'master' set up to track remote branch 'master' from 'origin'.

表示本地的master分支被設置去追蹤遠程的master分支,在第2~n次推送中,只須要使用git push這樣的簡寫命令(固然,完整寫法效果等同)。git就會自動將本地的master分支與遠程的master分支進行匹配,完成推送:

image-20200407205848977

而不使用-u參數時,沒有上述的分支追蹤信息。此時使用簡寫git push進行推送會出現錯誤:

image-20200407210028495

錯誤信息顯示:當前分支沒有與之對應的遠程分支。這個時候想要成功推送,必須採用指明對應關係的完整寫法,好比:

git push origin master

image-20200407210350445

這就是推送時使不使用-u參數的區別。而且,根據上面的介紹,使用以下指令進行推送也能達到-u參數的效果:

git push --set-upstream origin develop

image-20200407210704627

以後也可使用簡寫的git pull指令進行推送:

image-20200407210730668

細心的你必定發現了,以上都只是本地分支與遠程分支同名的狀況。不一樣名的狀況下,上面的兩個方法還好使嗎?

首先驗證方法一:-u參數:

image-20200407211150483

設置不一樣名的遠程分支時要注意寫成完整形式:pop:pop2

能夠看到,即便建立不一樣名的遠程分支,-u參數也同樣可以設置追蹤關係;可是,奇怪的是git push卻很差使了:

image-20200407211424929

仍是和沒使用-u參數時同樣,找不到對應的遠程分支,須要採用指明對應關係的完整寫法,好比:

git push origin pop:pop2

image-20200407211604378

其次驗證方法二:--set-upstream

image-20200407212552925

一樣設置分支對應關係時要使用完整寫法。能夠看到,該方法也設置了追蹤關係。奇怪的是git push一樣無論用:

image-20200407212657862

一樣找不到對應的遠程分支,須要採用指明對應關係的完整寫法,好比:

git push origin bob:bob2

image-20200407212901263

因此能夠得出結論:

  • 本地/遠程分支同名時:
    • -u參數的做用是設置本地分支與遠程分支的追蹤關係,設置了追蹤關係後,以後的推送可以使用簡寫git pushgit內部會自動進行匹配;
    • --set-upstream參數與-u參數效果等同;
  • 本地/遠程分支不一樣名時:
    • --set-upstream參數與-u參數依然能夠設置分支的追蹤關係,可是,以後的推送不能使用簡寫git push,只能使用指定分支對應關係的完整寫法;

總結:十分建議將全部的本地分支與對應的遠程分支設爲同名,而且第一次推送使用--set-upstream-u參數創建分支追蹤關係,以後就可使用簡寫git push進行推送了!

5、git push -f

該命令的完整寫法爲:

git push -f origin master

意思爲強制推送:直接跳過與遠程倉庫的master分支合併的環節,強制覆蓋遠程倉庫上master分支的內容,即以本地的master分支內容爲準。應慎用該命令,不然將覆蓋遠程倉庫中master分支上其餘人推送的文件(一星期的成果沒了)。

1.應用場合

  • 當遠程倉庫的歷史提交記錄太亂了,想要從新整理時。注意:必定要與其餘人協商好再用本地分支強制覆蓋遠程分支。
  • 只有一我的開發時,代碼以本地爲準。爲了不推送時繁瑣的合併,可使用-f強制推送,直接覆蓋遠程分支上的內容;

分兩種寫法:

  • 第一種:已經經過-u參數等方式,設置了本地分支與遠程分支的追蹤關係時,採用:

    git push -f

    image-20200417113501672

  • 第二種:還未設置追蹤關係,採用:

    git push -u origin master -f

    image-20200417114605971

2.預防措施

Github提供了相應的分支保護機制,能夠在Settings選項中進行設置:

image-20200409092021721

能夠看到Github默認是保護分支的:

image-20200409092311908

3.補救措施

讓有進度的人,再次對被強制覆蓋的遠程分支執行一次 git push -f 指令,把正確的內容強制推送上去,覆蓋前一次 git push -f 所形成的災難。

6、設置遠程分支對應的本地分支

假如遠程倉庫M3Y中有masterdevelop兩個分支,此時新建一個空的本地倉庫mygit,經過如下指令將它的遠程倉庫地址origin設置爲M3Y的地址:

git remote add origin git@github.com:AhuntSun/M3Y.git

此時兩倉庫的狀態爲:

image-20200417140610948

因爲mygit是空倉庫與遠程倉庫M3Y沒有任何公共提交歷史,因此在執行git pull時會出現下圖所示的不一樣源衝突(上一節中詳細介紹過該衝突):

image-20200417141801581

雖然git pull操做失敗了,可是也成功地將遠程倉庫M3Y的分支拉取了下來。可是,經過git branch -vv查看分支追蹤關係,發現並無本地分支與這兩個遠程分支創建了聯繫:

image-20200417142011442

如何創建這兩個本地遠程分支對應的本地分支?能夠經過如下兩種方法:

1.git checkout -b <branch> origin/<branch>

好比能夠經過如下命令,設置本地遠程分支origin/master與本地master分支的追蹤關係:

git checkout -b master origin/master

image-20200417142353425

以上爲本地master分支已存在的狀況,若是本地分支develop未建立,能夠採用下述命令建立並切換到develop分支,而且設置origin/developdevelop的追蹤關係:

git checkout -b develop origin/develop

image-20200417142901803

設置了本地分支與遠程分支的追蹤關係,接下來就能夠在本地倉庫執行git push進行推送了:

image-20200417143211765

2.git checkout --track origin/<branch>

重置條件,新建立一個空的本地倉庫mygit2,一樣將其遠程地址origin設置爲遠程倉庫M3Y的地址。隨後在本地倉庫mygit2中執行git pull操做,將遠程倉庫M3Y中的兩個分支拉取到本地:

image-20200417144521310

與上次同樣,拉取到本地的兩個本地遠程分支沒有與任何本地分支創建追蹤關係。此次能夠採用另一種方法:

git checkout --track origin/test

建立並切換到develop分支,而且設置該分支與origin/develop分支的追蹤關係:

image-20200417144827348

能夠說該方法是方法一的特殊狀況,由於該方法沒有指明建立的本地分支的名字,因此默認採用與遠程分支同樣的名字develop來命名;

若是想在本地創建一個develop2(不一樣名)的分支與本地遠程分支origin/test創建追蹤關係,則應採用第一種方法。

7、遠程分支信息

能夠進入.git目錄,查看儲存遠程分支信息的文件:

image-20200417123200144

1.查看config文件

使用vim編輯器打開該文件,能夠查看到關於遠程分支的信息:

image-20200417123001405

能夠看到remote這一欄中有兩個信息,第一個是遠程倉庫的url,第二個是fetch信息,這兩個信息尤其重要:

  • refs/heads/*表示遠程倉庫的refs/heads目錄下的全部引用都會寫入到本地的refs/remotes/origin目錄中;
  • 其中的+號是可選的,加了表示不管是否可以自動合併,便是否爲Fast Forward方式,都將遠程倉庫全部文件拉取到本地。
  • 而不加+則表示若是不是Fast Forward方式就不拉取。通常狀況下都是加上+號的,先把文件拉取到本地,不是Fast Forward方式就手動合併;

2.查看refs文件

refs文件夾存儲着refspec的文件,裏面維護着三個目錄:

image-20200417123302707

  • 第一個目錄heads:存儲的是本地倉庫的分支信息:

    image-20200417123344501

    能夠查看其中一個分支:

    image-20200417123406330

    是一個SHA1值,表示分支就是一個指針,指向當前提交。

  • 第二個目錄remotes:裏面存放着遠程分支信息,遠程倉庫中也存在這樣的目錄與文件;

    image-20200417123559607

    從上圖能夠看到,遠程分支只有master,沒有develop(由於以前被刪除了)。而且它們本質上也是一個表明提交的SHA1值:

    image-20200417125705684

    創建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

    上述兩種省略的寫法最終都會轉換爲完整的寫法:

    image-20200417125810727

  • 第三個目錄tags:存放標籤信息,也是一個SHA1值:

    image-20200417130027257

    詳細內容將在下一節介紹。

8、刪除遠程分支

以下圖所示,遠程倉庫有三個分支masterdeveloptest

image-20200329125145378

經過前面的學習,咱們知道經過下述指令能夠刪除本地develop分支:

git branch -d develop

image-20200329125322296

那麼如何刪除遠程分支呢?

首先咱們來看看git push的完整寫法:

git push origin srcBranch:destBranch
  • srcBranch表示本地的分支,destBranch表示對應的遠程分支;

  • 表示將本地的分支推送到遠程分支,這兩個分支能夠不一樣;

  • 之因此能夠直接使用git push是由於咱們設置的本地分支和遠程分支的名字是相同的,而且手動創建了聯繫,因此git可以自動識別;

明白了這點後,就不難理解下列刪除遠程分支的兩種作法了:

1.git push origin :destBranch

將空的分支推送到遠程分支,這樣就能將該遠程分支刪除;好比刪除本地分支develop的遠程分支:

git push origin :develop

image-20200417120547162

能夠看到成功刪除了遠程分支develop以及它所對應的本地遠程分支origin/develop

注意:並不須要切換到須要刪除遠程分支的本地分支develop上,再執行上述指令。也就是說,能夠在任意本地分支上刪除任意本地分支對應的遠程分支。

2.git push origin --delete destBranch

還能夠採用更加直觀的--delete參數,好比刪除遠程分支develop

git push origin --delete develop

image-20200417120853849

這兩種方式是等價的,可根據需求選擇。

3.git remote prune origin

該方法用於刪除無效的遠程分支對應的本地遠程分支,具體場合以下:

如圖所示mygitmygit2共享一個有三個分支的遠程倉庫:

image-20200409230534065

首先在mygit2中刪除遠程倉庫的develop分支,能夠看到mygit2中遠程分支develop對應的本地遠程分支origin/develop被刪除了:

image-20200329161409609

而後在mygit中查看遠程分支詳細信息:

image-20200329161520419

能夠看到提示信息中顯示遠程分支develop對應的本地遠程分支origin/develop處於stale(腐爛,遊離)狀態,即該分支對於mygit來講已經失效,可使用:

git remote prune origin

prune:裁剪)刪除mygit上這個無效的本地遠程分支:

image-20200329161857475

再次查看分支信息,可發現mygit中的本地遠程分支origin/develop已經被刪除了:

image-20200329161939271

注意:通常本地遠程分支設置了保護措施,不能隨意刪除;

9、重命名分支

1.本地分支

能夠經過如下命令,將本地分支dev重命名爲develop

git branch -m dev develop

image-20200407193850382

2.遠程分支

沒法直接重命名遠程分支,只能經過先刪除原來的遠程分支,再建立重命名後develop分支對應的遠程分支,過程爲:

//刪除遠程分支dev
git push origin :dev
//建立重命名後develop分支對應的遠程分支
git push -u origin develop

由此間接地完成了遠程分支的重命名。

以上就是本節的所有內容,相信看到這裏的你已經十分熟悉git refspec了。下一節將介紹git標籤與別名。

相關文章
相關標籤/搜索