章节 ▾ 第二版

3.5 Git 分支 - 远程分支

远程分支

远程引用是你远程仓库中的引用(指针),包括分支、标签等等。你可以通过 `git ls-remote ` 或 `git remote show ` 显式地获取完整的远程引用列表,后者还能提供更多关于远程分支的信息。然而,更常用的方式是利用远程跟踪分支。

远程跟踪分支是远程分支状态的引用。它们是不能手动移动的本地引用;每当你进行网络通信时,Git 会自动移动它们,以确保它们准确地表示远程仓库的状态。你可以把它们看作书签,提醒你上次连接到远程仓库时分支的位置。

远程跟踪分支的命名形式为 `/`。例如,如果你想查看 `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”也没有。虽然“master”是运行 `git init` 时起始分支的默认名称,这也是它被广泛使用的唯一原因,“origin”是运行 `git clone` 时远程仓库的默认名称。如果你改为运行 `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 ` 命令(在我们的例子中是 `git fetch origin`)。此命令会查找“origin”是哪个服务器(在本例中是 `git.ourcompany.com`),从中获取你尚未拥有的任何数据,并更新你的本地数据库,将你的 `origin/master` 指针移动到它新的、更最新的位置。

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

为了演示拥有多个远程服务器以及这些远程项目的远程分支是什么样子,假设你还有另一个内部 Git 服务器,仅供你的一个冲刺团队开发使用。该服务器位于 `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` 的远程跟踪分支,指向 `teamone` 的 `master` 分支所在的提交。

Remote-tracking branch for `teamone/master`
图 34. `teamone/master` 的远程跟踪分支

推送

当你想与他人共享一个分支时,你需要将其推送到一个你拥有写入权限的远程仓库。你的本地分支不会自动同步到你写入的远程仓库——你必须显式地推送你想要共享的分支。这样,你可以使用私有分支进行不希望共享的工作,并只推送你希望协作的主题分支。

如果你有一个名为 `serverfix` 的分支,想和他人一起工作,你可以用和推送第一个分支相同的方式推送它。运行 `git push `

$ 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` 轻松设置。

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

下次你的某个协作者从服务器拉取时,他们会在远程分支 `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

需要注意的是,当你执行 `fetch` 操作拉取新的远程跟踪分支时,你不会自动拥有它们的本地、可编辑副本。换句话说,在这种情况下,你没有新的 `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 /`。这是一个足够常见的操作,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` 拉取。

如果你已经有一个本地分支,并想将其设置为刚刚拉取的远程分支,或者想更改你正在跟踪的上游分支,你可以随时使用 `git branch` 命令的 `-u` 或 `--set-upstream-to` 选项来明确设置它。

$ 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] 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` 的组合。如果你按照上一节的演示设置了跟踪分支,无论是显式设置还是由 `clone` 或 `checkout` 命令为你创建,`git pull` 都会查找当前分支正在跟踪哪个服务器和分支,从该服务器获取数据,然后尝试合并该远程分支。

删除远程分支

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

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

基本上,所有这些操作都是从服务器上移除指针。Git 服务器通常会将数据保留一段时间,直到垃圾回收运行,所以如果意外删除了,通常很容易恢复。

scroll-to-top