遠端倉庫與 fetch 命令——Git 學習筆記 20

ARM的程式設計師敲著詩歌的夢發表於2018-11-04

遠端倉庫是指託管在因特網或其他網路(比如區域網)中的版本庫。 你可以有好幾個遠端倉庫,通常有些倉庫對你只讀,有些則對你可讀可寫。 與他人協作涉及管理遠端倉庫以及根據需要推送或拉取資料。

檢視遠端倉庫

如果想檢視你已經配置的遠端倉庫伺服器,可以執行 git remote 命令。它會列出你指定的每一個遠端伺服器的簡寫。 如果你已經克隆了倉庫,那麼至少應該能看到 origin -—— 這是 Git 給你起的克隆的倉庫伺服器的預設名字:

$ git remote
origin

你也可以指定選項 -v,會顯示遠端倉庫對應的簡寫和它的 URL。

$ git remote -v
origin  https://github.com/schacon/ticgit (fetch)
origin  https://github.com/schacon/ticgit (push)

上面的命令表示,當前只有一臺遠端主機,叫做 origin,它的網址是https://github.com/schacon/ticgit

fetch 和 push 對應的都是這個網址,當然,也可以對應不同的網址。

如果你的遠端倉庫不止一個,該命令會將它們全部列出。 例如:

$ git remote -v
bakkdoor  https://github.com/bakkdoor/grit (fetch)
bakkdoor  https://github.com/bakkdoor/grit (push)
cho45     https://github.com/cho45/grit (fetch)
cho45     https://github.com/cho45/grit (push)
defunkt   https://github.com/defunkt/grit (fetch)
defunkt   https://github.com/defunkt/grit (push)
koke      git://github.com/koke/grit.git (fetch)
koke      git://github.com/koke/grit.git (push)
origin    git@github.com:mojombo/grit.git (fetch)
origin    git@github.com:mojombo/grit.git (push)

可以看到,一共有5個遠端倉庫。注意到這些遠端倉庫使用了不同的協議(從地址的格式就可以看出來)。

新增遠端倉庫

執行 git remote add <shortname> <url> 新增一個新的遠端倉庫,同時指定一個簡寫:

$ git remote
origin
$
$ git remote add pb https://github.com/paulboone/ticgit
$
$ git remote -v
origin  https://github.com/schacon/ticgit (fetch)
origin  https://github.com/schacon/ticgit (push)
pb  https://github.com/paulboone/ticgit (fetch)
pb  https://github.com/paulboone/ticgit (push)

現在你可以在命令列中使用字串 pb 來代替整個 URL (即https://github.com/paulboone/ticgit)。

git fetch命令

要從遠端倉庫中獲得資料,可以執行:

$ git fetch [remote-name]

這個命令會訪問遠端倉庫,從中取回所有你還沒有的資料。 執行完成後,你將會擁有那個遠端倉庫中所有分支的引用,可以隨時合併或檢視。

如果你使用 clone 命令克隆了一個倉庫,那麼 Git 會自動將其新增為遠端倉庫並預設以 “origin” 為簡寫。 所以,git fetch origin 會抓取克隆(或上一次抓取)後新推送的所有工作。

必須注意 :git fetch 命令僅僅是將資料拉取到你的本地倉庫,它並不會自動合併或修改你當前的工作。當準備好時你必須手動合併。

如果你覺得上面的文字讓你雲裡霧裡,那麼請看下文。

假設伺服器上有一個版本庫:
在這裡插入圖片描述
最近的 2 次提交標記為 A 和 B。

這時候,你把這個版本庫克隆到了本地:

在這裡插入圖片描述

克隆之後:

  • 原始版本庫中的所有提交都複製到克隆版本庫。
  • 克隆版本庫中有一個名為 origin/master遠端追蹤分支( remote-tracking branch),它指向原始版本庫中 master 指向的提交,也就是 B。
  • 克隆版本庫中建立了一個新的本地追蹤分支( local-tracking development branches),稱為 master 分支。這個 master 分支指向 origin/master指向的提交,也就是 B。

克隆後,Git 會選擇新的 master 分支作為當前分支,並自動檢出它。因此,除非切換分支,否則克隆後所做的任何修改都會影響 master 分支。

在圖中,原始版本庫和派生的克隆版本庫中的開發分支都由深灰色作為背景色,而遠端追蹤分支則用淺灰色作為背景色。在 Git 的實現中,深灰色背景的分支屬於 refs/heads/ 名稱空間,淺灰色背景的分支的屬於refs/remotes/名稱空間。

請注意,本地追蹤分支遠端追蹤分支都是私有的,並只存在於各自的版本庫中。

遠端追蹤分支是遠端分支狀態的引用。它們是你不能移動的本地引用,當你做任何網路通訊操作時,它們會自動移動。遠端追蹤分支像是你上次連線到遠端倉庫時,那些分支所處狀態的書籤。例如,如果你想要看你最後一次與遠端倉庫 origin 通訊時 master 分支的狀態,你可以檢視 origin/master 分支。

在本地 master分支工作的時候,本地的 master 會向前移動,而**origin/master是不可以移動的**。正如下圖所示,你的開發使 master 分支變長了:在提交 B 的基礎上多出 2 個新的提交 —— X 和 Y。

在這裡插入圖片描述

在你開發期間,如果原始版本庫沒有任何變化,那麼你可以很容易地把 X 和 Y 推送到上游:Git 會把你的提交傳輸到原始版本庫,並把它們新增到 B 的後邊,然後 Git 會把你的提交合併到原始版本庫的 master 分支,實際上這是一種特殊的合併操作 —— 快進(fast-forward),快進本質上是一個簡單的線性歷史記錄推進操作。

如下圖所示:
在這裡插入圖片描述

正如前文所述,在推送(push)的過程中,本地倉庫與遠端倉庫 origin 進行了“通訊”, origin/master 分支會和遠端倉庫的 master 保持同步,所以,你的origin/master 分支也指向了 Y。

以上的舉例是理想情況。實際情況是,在你開發期間,任何其他有權訪問原始版本庫的開發人員可能已經做了進一步開發,並把她的修改推送到該版本庫。如下圖:

在這裡插入圖片描述

在這種情況下,我們說版本庫的歷史記錄在提交 B 處分叉(diverged 或 forked)了。當你嘗試推送,Git 會拒絕它並用一條如下所示的訊息告訴你有關的衝突。

 $ git push
    To /tmp/Depot/public_html
     ! [rejected]        master -> master (non-fast forward)
    error: failed to push some refs to '/tmp/Depot/public_html'

那麼,什麼是你真正想要做的?你想覆蓋其他人員的工作,還是想要合併兩組歷史記錄?

提示

如果你想覆蓋所有其他變化,也是可以的。只要在你的 git push 中使用 -f選項即可。不過我建議你不要這麼做,否則你的夥伴會恨你的……

更多的時候,你不想覆蓋別人的提交,你只是想新增你自己的修改。在這種情況下,你必須在推送之前在你的版本庫中合併兩組歷史記錄。這時候,就該 fetch大顯身手了。

要讓 Git 合併兩組歷史記錄,那麼這兩組歷史記錄必須存在於同一個版本庫。現在你的 X 和 Y 提交本身就在你的版本庫裡,為了把 origin 中的 C 和 D 提交納入你的版本庫,你可以用 git fetch命令進行抓取。這個命令會訪問遠端倉庫,從中拉取所有你還沒有的資料,如下圖:

在這裡插入圖片描述

注意,引入 C 和 D 這組歷史記錄並不能改變由 X 和 Y 代表的歷史記錄,所以你不用擔心 fetch 操作會破壞你的勞動成果。fetch 後,這兩組歷史記錄同時存在於你的版本庫中,形成一幅比較複雜的圖,簡單來說就是在 B 處分叉了。你的歷史記錄由 master 分支代表,遠端歷史記錄則由 origin/master 遠端追蹤分支代表。

講到這裡,fetch 命令就講完了,剩下的工作(merge, push)以後再說。

參考資料

【1】《Git 分支:遠端分支》https://git-scm.com/book/zh/v2/
【2】《Git 版本控制管理(第2版)》,人民郵電出版社
【3】《精通 Git(第2版)》,人民郵電出版社

相關文章