章节 ▾ 第二版

3.5 Git 分支 - 远程分支

远程分支

远程引用是指远程仓库中的引用(指针),包括分支、标签等等。 你可以使用git ls-remote <remote>显式地获取完整的远程引用列表,或者使用git remote show <remote>获取远程分支以及更多信息。 不过,更常用的方法是利用远程跟踪分支。

远程跟踪分支是远程分支状态的引用。 它们是你无法移动的本地引用; 每当你进行任何网络通信时,Git 都会为你移动它们,以确保它们准确地代表远程仓库的状态。 可以将它们视为书签,提醒你上次连接到远程仓库时,远程仓库中的分支在哪里。

远程跟踪分支名称采用<remote>/<branch>的形式。 例如,如果你想查看上次与 `origin` 远程仓库通信时,`master` 分支是什么样的,你可以查看 `origin/master` 分支。 如果你与合作伙伴合作处理一个问题并推送了一个 `iss53` 分支,你可能拥有自己的本地 `iss53` 分支,但服务器上的分支将由远程跟踪分支 `origin/iss53` 表示。

这可能有点令人困惑,所以让我们看一个例子。 假设你的网络中有一台 Git 服务器,地址为 `git.ourcompany.com`。 如果你从中克隆,Git 的 `clone` 命令会自动将其命名为 `origin`,拉取其所有数据,创建一个指向其 `master` 分支位置的指针,并在本地将其命名为 `origin/master`。 Git 还为你提供你自己的本地 `master` 分支,它从与 origin 的 `master` 分支相同的位置开始,因此你可以从那里开始工作。

注意
“origin” 并非特殊

就像分支名称“master”在 Git 中没有任何特殊含义一样,“origin”也没有。 当你运行 `git init` 时,“master”是起始分支的默认名称,这是它被广泛使用的唯一原因,而当你运行 `git clone` 时,“origin”是远程仓库的默认名称。 如果你运行 `git clone -o booyah`,那么你将拥有 `booyah/master` 作为你的默认远程分支。

Server and local repositories after cloning
图 30. 克隆后的服务器和本地仓库

如果你在本地 `master` 分支上做了一些工作,与此同时,其他人推送到 `git.ourcompany.com` 并更新了它的 `master` 分支,那么你的历史记录就会以不同的方式前进。 此外,只要你与 `origin` 服务器保持联系,你的 `origin/master` 指针就不会移动。

Local and remote work can diverge
图 31. 本地和远程工作可能会发散

要将你的工作与给定的远程仓库同步,你需要运行一个 `git fetch <remote>` 命令(在我们的例子中,是 `git fetch origin`)。 这个命令会查找 “origin” 对应的服务器(在本例中,是 `git.ourcompany.com`),从中获取你还没有的任何数据,并更新你的本地数据库,将你的 `origin/master` 指针移动到其新的、更及时的位置。

`git fetch` updates your remote-tracking branches
图 32. git fetch 更新你的远程跟踪分支

为了演示拥有多个远程服务器,以及这些远程项目的远程分支是什么样的,假设你还有另一个内部 Git 服务器,仅供一个 sprint 团队用于开发。此服务器位于 git.team1.ourcompany.com。你可以通过运行 git remote add 命令,将其作为新的远程引用添加到你当前正在处理的项目中,正如我们在 Git 基础 中所介绍的那样。 将此远程服务器命名为 teamone,这将是该完整 URL 的简称。

Adding another server as a remote
图 33. 添加另一个服务器作为远程服务器

现在,你可以运行 git fetch teamone 来抓取远程 teamone 服务器上你尚未拥有的所有内容。 由于该服务器拥有你当前 origin 服务器上数据的子集,因此 Git 不会抓取任何数据,但会将名为 teamone/master 的远程跟踪分支设置为指向 teamonemaster 分支所在的提交。

Remote-tracking branch for `teamone/master`
图 34. 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 分支。”我们将在 Git 内部原理 中详细介绍 refs/heads/ 部分,但通常你可以省略它。你也可以执行 git push origin serverfix:serverfix,这会执行相同的操作 — 它说:“获取我的 serverfix 并将其设置为远程的 serverfix。”你可以使用此格式将本地分支推送到名称不同的远程分支中。 如果你不想在远程仓库上将其命名为 serverfix,你可以改为运行 git push origin serverfix:awesomebranch,将你的本地 serverfix 分支推送到远程项目的 awesomebranch 分支。

注意
不要每次都输入密码

如果你使用 HTTPS URL 进行推送,Git 服务器会要求你提供用户名和密码进行身份验证。默认情况下,它会在终端上提示你输入此信息,以便服务器可以判断你是否被允许推送。

如果你不想每次推送都输入密码,你可以设置“凭据缓存”。最简单的方法就是将其保存在内存中几分钟,你可以通过运行 git config --global credential.helper cache 轻松设置它。

有关各种可用凭据缓存选项的更多信息,请参阅 凭据存储

下次你的协作者从服务器抓取时,他们将获得对服务器版本的 serverfix 的引用,该引用位于远程分支 origin/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 会自动知道从哪个服务器抓取以及合并哪个分支。

当你克隆一个存储库时,它通常会自动创建一个 master 分支,该分支跟踪 origin/master。 但是,如果你愿意,你可以设置其他跟踪分支 — 跟踪其他远程仓库上的分支,或者不跟踪 master 分支。 简单的例子是你刚刚看到的,运行 git checkout -b <branch> <remote>/<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'

事实上,这是如此常见,以至于甚至有一个用于该简写的快捷方式。 如果你尝试检出的分支名称 (a) 不存在,并且 (b) 仅与一个远程仓库上的名称完全匹配,Git 将为你创建一个跟踪分支

$ git checkout 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(如果你愿意)。

如果你想查看你设置了哪些跟踪分支,你可以使用 -vv 选项来 git branch。 这将列出你的本地分支,并提供更多信息,包括每个分支正在跟踪的内容,以及你的本地分支是提前、落后还是两者兼而有之。

$ git branch -vv
  iss53     7e424c3 [origin/iss53: ahead 2] Add forgotten brackets
  master    1ae2a45 [origin/master] Deploy index fix
* serverfix f8674d9 [teamone/server-fix-good: ahead 3, behind 1] This should do it
  testing   5ea463a Try something new

因此,在这里我们可以看到我们的 iss53 分支正在跟踪 origin/iss53,并且“提前”了两个,这意味着我们本地有两个尚未推送到服务器的提交。 我们还可以看到我们的 master 分支正在跟踪 origin/master 并且是最新的。 接下来,我们可以看到我们的 serverfix 分支正在跟踪我们 teamone 服务器上的 server-fix-good 分支,并且提前了三个并且落后了一个,这意味着服务器上有一个我们尚未合并的提交,并且本地有三个我们尚未推送的提交。 最后,我们可以看到我们的 testing 分支未跟踪任何远程分支。

重要的是要注意,这些数字仅自你上次从每个服务器抓取以来的时间。 此命令不会访问服务器,它会告诉你它从这些服务器在本地缓存的内容。 如果你想要完全最新的提前和落后数字,你需要先从所有远程仓库抓取,然后再运行此命令。 你可以像这样做到这一点

$ git fetch --all; git branch -vv

拉取

虽然 git fetch 命令会抓取你尚未拥有的服务器上的所有更改,但它根本不会修改你的工作目录。 它只会为你获取数据并让你自己合并它。 但是,有一个名为 git pull 的命令,它本质上是 git fetch,然后在大多数情况下立即执行 git merge。 如果你按照上一节中演示的方式设置了跟踪分支,无论是通过显式设置它还是通过 clonecheckout 命令为你创建它,git pull 都会查找你的当前分支正在跟踪哪个服务器和分支,从该服务器抓取,然后尝试合并该远程分支。

删除远程分支

假设你已完成远程分支 — 比如说你和你的协作者已经完成了一个功能,并将其合并到远程仓库的 master 分支中(或你的稳定代码库所在的任何分支)。 你可以使用 git push--delete 选项删除远程分支。 如果你想从服务器中删除你的 serverfix 分支,你可以运行以下命令

$ git push origin --delete serverfix
To https://github.com/schacon/simplegit
 - [deleted]         serverfix

基本上,这所做的只是从服务器中删除指针。 Git 服务器通常会将数据保留在那里一段时间,直到垃圾回收运行,因此如果意外删除,通常很容易恢复。

scroll-to-top