Git
章节 ▾ 第二版

3.5 Git 分支 - 远程分支

远程分支

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

远程跟踪分支是对远程分支状态的引用。它们是本地引用,您无法移动它们;当您执行任何网络通信时,Git 会为您移动它们,以确保它们准确地表示远程仓库的状态。可以将它们视为书签,以提醒您上次连接到远程仓库时分支所在的位置。

远程跟踪分支名称采用 <remote>/<branch> 的形式。例如,如果您想查看 origin 远程仓库的 master 分支在上次与之通信时的状态,则可以检查 origin/master 分支。如果您正在与合作伙伴处理某个问题,并且他们推送了一个 iss53 分支,您可能拥有自己的本地 iss53 分支,但服务器上的分支将由远程跟踪分支 origin/iss53 表示。

这可能有点令人困惑,所以让我们看一个例子。假设您在网络上有一个位于 git.ourcompany.com 的 Git 服务器。如果从这里克隆,Git 的 clone 命令会自动将其命名为 origin,下载所有数据,创建指向其 master 分支的指针,并在本地将其命名为 origin/master。Git 还会为您提供自己的本地 master 分支,该分支从 originmaster 分支的相同位置开始,以便您从中进行工作。

注意
“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 <remote> 命令(在我们的例子中,是 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 <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 会自动知道要从哪个服务器获取数据以及要合并哪个分支。

当你克隆存储库时,它通常会自动创建一个跟踪 origin/mastermaster 分支。但是,如果你愿意,可以设置其他跟踪分支——那些跟踪其他远程服务器上的分支或不跟踪 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 获取数据。

如果你已经有一个本地分支,并且想要将其设置为刚刚拉取的远程分支,或者想要更改正在跟踪的上游分支,可以在任何时候使用 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。如果你设置了跟踪分支(如上一节中所示),无论是通过显式设置还是通过 clonecheckout 命令为你创建,git pull 将查找你的当前分支正在跟踪哪个服务器和分支,从该服务器获取数据,然后尝试合并该远程分支。

通常,最好简单地显式使用 fetchmerge 命令,因为 git pull 的“魔力”通常会令人困惑。

删除远程分支

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

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

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

scroll-to-top