# 遠程分支
遠程引用是對遠程倉庫的引用(指針),包括分支、標簽等等。你可以通過 `git ls-remote (remote)` 來顯式地獲得遠程引用的完整列表,或者通過 `git remote show (remote)` 獲得遠程分支的更多信息。然而,一個更常見的做法是利用遠程跟蹤分支。
遠程跟蹤分支是遠程分支狀態的引用。它們是你不能移動的本地引用,當你做任何網絡通信操作時,它們會自動移動。遠程跟蹤分支像是你上次連接到遠程倉庫時,那些分支所處狀態的書簽。
它們以 `(remote)/(branch)` 形式命名。例如,如果你想要看你最后一次與遠程倉庫 `origin` 通信時 `master` 分支的狀態,你可以查看 `origin/master` 分支。你與同事合作解決一個問題并且他們推送了一個 `iss53` 分支,你可能有自己的本地 `iss53` 分支;但是在服務器上的分支會指向 `origin/iss53` 的提交。
這可能有一點兒難以理解,讓我們來看一個例子。假設你的網絡里有一個在 `git.ourcompany.com` 的 Git 服務器。如果你從這里克隆,Git 的 `clone` 命令會為你自動將其命名為 `origin`,拉取它的所有數據,創建一個指向它的 `master` 分支的指針,并且在本地將其命名為 `origin/master`。Git 也會給你一個與 origin 的 `master` 分支在指向同一個地方的本地 `master` 分支,這樣你就有工作的基礎。
> ### “origin” 并無特殊含義
> 遠程倉庫名字 “origin” 與分支名字 “master” 一樣,在 Git 中并沒有任何特別的含義一樣。同時 “master” 是當你運行 `git init` 時默認的起始分支名字,原因僅僅是它的廣泛使用,“origin” 是當你運行 `git clone` 時默認的遠程倉庫名字。如果你運行 `git clone -o booyah`,那么你默認的遠程分支名字將會是 `booyah/master`。

Figure 3-22. 克隆之后的服務器與本地倉庫
如果你在本地的 `master` 分支做了一些工作,然而在同一時間,其他人推送提交到 `git.ourcompany.com` 并更新了它的 `master` 分支,那么你的提交歷史將向不同的方向前進。也許,只要你不與 origin 服務器連接,你的 `origin/master` 指針就不會移動。

Figure 3-23. 本地與遠程的工作可以分叉
如果要同步你的工作,運行 `git fetch origin` 命令。這個命令查找 “origin” 是哪一個服務器(在本例中,它是 `git.ourcompany.com`),從中抓取本地沒有的數據,并且更新本地數據庫,移動 `origin/master` 指針指向新的、更新后的位置。

Figure 3-24. `git fetch` 更新你的遠程倉庫引用
為了演示有多個遠程倉庫與遠程分支的情況,我們假定你有另一個內部 Git 服務器,僅用于你的 sprint 小組的開發工作。這個服務器位于 `git.team1.ourcompany.com`。你可以運行 `git remote add` 命令添加一個新的遠程倉庫引用到當前的項目,這個命令我們會在 [Chapter?2](#) 中詳細說明。將這個遠程倉庫命名為 `teamone`,將其作為整個 URL 的縮寫。

Figure 3-25. 添加另一個遠程倉庫
現在,可以運行 `git fetch teamone` 來抓取遠程倉庫 `teamone` 有而本地沒有的數據。因為那臺服務器上現有的數據是 `origin` 服務器上的一個子集,所以 Git 并不會抓取數據而是會設置遠程跟蹤分支 `teamone/master` 指向 `teamone` 的 `master` 分支。

Figure 3-26. 遠程跟蹤分支 `teamone/master`
## 推送
當你想要公開分享一個分支時,需要將其推送到有寫入權限的遠程倉庫上。本地的分支并不會自動與遠程倉庫同步 - 你必須顯式地推送想要分享的分支。這樣,你就可以把不愿意分享的內容放到私人分支上,而將需要和別人協作的內容推送到公開分支。
如果希望和別人一起在名為 `serverfix` 的分支上工作,你可以像推送第一個分支那樣推送它。運行 `git push (remote) (branch)`:
~~~
$ git push origin serverfix
Counting objects: 24, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (15/15), done.
Writing objects: 100% (24/24), 1.91 KiB | 0 bytes/s, done.
Total 24 (delta 2), reused 0 (delta 0)
To https://github.com/schacon/simplegit
* [new branch] serverfix -> serverfix
~~~
這里有些工作被簡化了。Git 自動將 `serverfix` 分支名字展開為 `refs/heads/serverfix:refs/heads/serverfix`,那意味著,“推送本地的 serverfix 分支來更新遠程倉庫上的 serverfix 分支。”我們將會詳細學習 [Chapter?10](#) 的 `refs/heads/` 部分,但是現在可以先把它放在兒。你也可以運行 `git push origin serverfix:serverfix`,它會做同樣的事 - 相當于它說,“推送本地的 serverfix 分支,將其作為遠程倉庫的 serverfix 分支”可以通過這種格式來推送本地分支到一個命名不相同的遠程分支。如果并不想讓遠程倉庫上的分支叫做 `serverfix`,可以運行 `git push origin serverfix:awesomebranch` 來將本地的 `serverfix` 分支推送到遠程倉庫上的 `awesomebranch` 分支。
> ### 如何避免每次輸入密碼
> 如果你正在使用 HTTPS URL 來推送,Git 服務器會詢問用戶名與密碼。默認情況下它會在終端中提示服務器是否允許你進行推送。
如果不想在每一次推送時都輸入用戶名與密碼,你可以設置一個 “credential cache”。最簡單的方式就是將其保存在內存中幾分鐘,可以簡單地運行 `git config --global credential.helper cache` 來設置它。
想要了解更多關于不同驗證緩存的可用選項,查看 [“憑證存儲”](#)。
下一次其他協作者從服務器上抓取數據時,他們會在本地生成一個遠程分支 `origin/serverfix`,指向服務器的 `serverfix` 分支的引用:
~~~
$ git fetch origin
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0)
Unpacking objects: 100% (3/3), done.
From https://github.com/schacon/simplegit
* [new branch] serverfix -> origin/serverfix
~~~
要特別注意的一點是當抓取到新的遠程跟蹤分支時,本地不會自動生成一份可編輯的副本(拷貝)。換一句話說,這種情況下,不會有一個新的 `serverfix` 分支 - 只有一個不可以修改的 `origin/serverfix` 指針。
可以運行 `git merge origin/serverfix` 將這些工作合并到當前所在的分支。如果想要在自己的 `serverfix` 分支上工作,可以將其建立在遠程跟蹤分支之上:
~~~
$ git checkout -b serverfix origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'
~~~
這會給你一個用于工作的本地分支,并且起點位于 `origin/serverfix`。
## 跟蹤分支
從一個遠程跟蹤分支檢出一個本地分支會自動創建一個叫做 “跟蹤分支”(有時候也叫做 “上游分支”)。跟蹤分支是與遠程分支有直接關系的本地分支。如果在一個跟蹤分支上輸入 `git pull`,Git 能自動地識別去哪個服務器上抓取、合并到哪個分支。
當克隆一個倉庫時,它通常會自動地創建一個跟蹤 `origin/master` 的 `master` 分支。然而,如果你愿意的話可以設置其他的跟蹤分支 - 其他遠程倉庫上的跟蹤分支,或者不跟蹤 `master` 分支。最簡單的就是之前看到的例子,運行 `git checkout -b [branch] [remotename]/[branch]`。這是一個十分常用的操作所以 Git 提供了 `--track` 快捷方式:
~~~
$ git checkout --track origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'
~~~
如果想要將本地分支與遠程分支設置為不同名字,你可以輕松地增加一個不同名字的本地分支的上一個命令:
~~~
$ git checkout -b sf origin/serverfix
Branch sf set up to track remote branch serverfix from origin.
Switched to a new branch 'sf'
~~~
現在,本地分支 `sf` 會自動從 `origin/serverfix` 拉取。
設置已有的本地分支跟蹤一個剛剛拉取下來的遠程分支,或者想要修改正在跟蹤的上游分支,你可以在任意時間使用 `-u` 或 `--set-upstream-to` 選項運行 `git branch` 來顯式地設置。
~~~
$ git branch -u origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
~~~
> ### 上游快捷方式
> 當設置好跟蹤分支后,可以通過 `@{upstream}` 或 `@{u}` 快捷方式來引用它。所以在 `master` 分支時并且它正在跟蹤 `origin/master` 時,如果愿意的話可以使用 `git merge @{u}` 來取代 `git merge origin/master`。
如果想要查看設置的所有跟蹤分支,可以使用 `git branch` 的 `-vv` 選項。這會將所有的本地分支列出來并且包含更多的信息,如每一個分支正在跟蹤哪個遠程分支與本地分支是否是領先、落后或是都有。
~~~
$ git branch -vv
iss53 7e424c3 [origin/iss53: ahead 2] forgot the brackets
master 1ae2a45 [origin/master] deploying index fix
* serverfix f8674d9 [teamone/server-fix-good: ahead 3, behind 1] this should do it
testing 5ea463a trying something new
~~~
這里可以看到 `iss53` 分支正在跟蹤 `origin/iss53` 并且 “ahead” 是 2,意味著本地有兩個提交還沒有推送到服務器上。也能看到 `master` 分支正在跟蹤 `origin/master` 分支并且是最新的。接下來可以看到 `serverfix` 分支正在跟蹤 `teamone` 服務器上的 `server-fix-good` 分支并且領先 2 落后 1,意味著服務器上有一次提交還沒有合并入同時本地有三次提交還沒有推送。最后看到 `testing` 分支并沒有跟蹤任何遠程分支。
需要重點注意的一點是這些數字的值來自于你從每個服務器上最后一次抓取的數據。這個命令并沒有連接服務器,它只會告訴你關于本地緩存的服務器數據。如果想要統計最新的領先與落后數字,需要在運行此命令前抓取所有的遠程倉庫。可以像這樣做:`$ git fetch --all; git branch -vv`
## 拉取
當 `git fetch` 命令從服務器上抓取本地沒有的數據時,它并不會修改工作目錄中的內容。它只會獲取數據然后讓你自己合并。然而,有一個命令叫作 `git pull` 在大多數情況下它的含義是一個 `git fetch` 緊接著一個 `git merge` 命令。如果有一個像之前章節中演示的設置好的跟蹤分支,不管它是顯式地設置還是通過 `clone` 或 `checkout` 命令為你創建的,`git pull` 都會查找當前分支所跟蹤的服務器與分支,從服務器上抓取數據然后嘗試合并入那個遠程分支。
由于 `git pull` 的魔法經常令人困惑所以通常單獨顯式地使用 `fetch` 與 `merge` 命令會更好一些。
## 刪除遠程分支
假設你已經通過遠程分支做完所有的工作了 - 也就是說你和你的協作者已經完成了一個特性并且將其合并到了遠程倉庫的 `master` 分支(或任何其他穩定代碼分支)。可以運行帶有 `--delete` 選項的 `git push` 命令來刪除一個遠程分支。如果想要從服務器上刪除 `serverfix` 分支,運行下面的命令:
~~~
$ git push origin --delete serverfix
To https://github.com/schacon/simplegit
- [deleted] serverfix
~~~
基本上這個命令做的只是從服務器上移除這個指針。Git 服務器通常會保留數據一段時間直到垃圾回收運行,所以如果不小心刪除掉了,通常是很容易恢復的。
- 前言
- Scott Chacon 序
- Ben Straub 序
- 獻辭
- 貢獻者
- 引言
- 1. 起步
- 1.1 關于版本控制
- 1.2 Git 簡史
- 1.3 Git 基礎
- 1.4 命令行
- 1.5 安裝 Git
- 1.6 初次運行 Git 前的配置
- 1.7 獲取幫助
- 1.8 總結
- 2. Git 基礎
- 2.1 獲取 Git 倉庫
- 2.2 記錄每次更新到倉庫
- 2.3 查看提交歷史
- 2.4 撤消操作
- 2.5 遠程倉庫的使用
- 2.6 打標簽
- 2.7 Git 別名
- 2.8 總結
- 3. Git 分支
- 3.1 分支簡介
- 3.2 分支的新建與合并
- 3.3 分支管理
- 3.4 分支開發工作流
- 3.5 遠程分支
- 3.6 變基
- 3.7 總結
- 4. 服務器上的 Git
- 4.1 協議
- 4.2 在服務器上搭建 Git
- 4.3 生成 SSH 公鑰
- 4.4 配置服務器
- 4.5 Git 守護進程
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 第三方托管的選擇
- 4.10 總結
- 5. 分布式 Git
- 5.1 分布式工作流程
- 5.2 向一個項目貢獻
- 5.3 維護項目
- 5.4 總結
- 6. GitHub
- 6.1 賬戶的創建和配置
- 6.2 對項目做出貢獻
- 6.3 維護項目
- 6.4 管理組織
- 6.5 腳本 GitHub
- 6.6 總結
- 7. Git 工具
- 7.1 選擇修訂版本
- 7.2 交互式暫存
- 7.3 儲藏與清理
- 7.4 簽署工作
- 7.5 搜索
- 7.6 重寫歷史
- 7.7 重置揭密
- 7.8 高級合并
- 7.9 Rerere
- 7.10 使用 Git 調試
- 7.11 子模塊
- 7.12 打包
- 7.13 替換
- 7.14 憑證存儲
- 7.15 總結
- 8. 自定義 Git
- 8.1 配置 Git
- 8.2 Git 屬性
- 8.3 Git 鉤子
- 8.4 使用強制策略的一個例子
- 8.5 總結
- 9. Git 與其他系統
- 9.1 作為客戶端的 Git
- 9.2 遷移到 Git
- 9.3 總結
- 10. Git 內部原理
- 10.1 底層命令和高層命令
- 10.2 Git 對象
- 10.3 Git 引用
- 10.4 包文件
- 10.5 引用規格
- 10.6 傳輸協議
- 10.7 維護與數據恢復
- 10.8 環境變量
- 10.9 總結
- A. 其它環境中的 Git
- A1.1 圖形界面
- A1.2 Visual Studio 中的 Git
- A1.3 Eclipse 中的 Git
- A1.4 Bash 中的 Git
- A1.5 Zsh 中的 Git
- A1.6 Powershell 中的 Git
- A1.7 總結
- B. 將 Git 嵌入你的應用
- A2.1 命令行 Git 方式
- A2.2 Libgit2
- A2.3 JGit
- C. Git 命令
- A3.1 設置與配置
- A3.2 獲取與創建項目
- A3.3 快照基礎
- A3.4 分支與合并
- A3.5 項目分享與更新
- A3.6 檢查與比較
- A3.7 調試
- A3.8 補丁
- A3.9 郵件
- A3.10 外部系統
- A3.11 管理
- A3.12 底層命令