首先,让我们定义术语远程跟踪分支。远程跟踪分支只是一个完整拼写以refs/remotes/ 开头的引用,例如您的示例中的refs/remotes/origin/master。在与 Git 的大多数正常交互中,我们删除了 refs/remotes/ 部分,而只写了 origin/master,但有时——如较低级别的、面向脚本的 Git 命令,或者在做像你的示例这样奇怪的事情时——有必要把它拼出来完全。
现在,远程跟踪分支的目的是跟踪存储在您的一个分支中的ID strong>remotes(因此得名)。因此,如果我们在这里仔细研究您的问题:
...通过本地分支更新远程分支是一种好习惯吗?
我们需要注意一些事情:这不是更新“远程分支”(我们还没有真正定义),而是更新 remote-tracking 分支,即其中之一名称以refs/remotes/ 开头的本地实体。我们还要在这里注意,您专门删除一个远程跟踪分支(这是一种相当严重的更新形式:-))。
不过,假设您刚刚删除了一个词并且确实是指“远程跟踪分支”:以这种方式更新它是一种好习惯吗?一般不会,不会。
不过,让我添加更多背景。我还需要在这里定义术语 refspec。 refspec 只是一对引用(如master 或origin/master,或它们的完全拼写版本,分别为refs/heads/master 和refs/remotes/origin/master),由冒号: 字符分隔。左侧的引用称为源或src,右侧的引用称为目标或dst。或者,您可以在该对前面加上加号 +,并最多省略 src 和/或 dst 之一。如果您省略 dst,您可以也可能应该省略冒号:例如,git fetch origin master 是一个没有目的地的源。省略 src 时需要冒号:git push origin :foo 不能拼写为 git push origin foo。
(当你省略一半的参考规范时,它意味着变化,在git fetch 和git push 之间。我不打算完全描述这一点,只是足以涵盖这里感兴趣的案例.)
通过git fetch 和git push 更新
从 Git 版本 1.8.4 开始,git push 和 git fetch 都会适时更新远程跟踪分支。也就是说,如果您有 Git 联系人远程 R 并获取或设置其分支 B,并且您的配置说要在 @ 下跟踪 B 987654347@(与正常设置一样),您的 Git 将(从 1.8.4 开始)更新该远程跟踪分支。 (在 1.8.4 之前,Git 对push 进行了机会性更新,但对fetch 没有。)
删除的分支和--prune
只需运行git fetch origin(无需额外的参考规范)将更新您的所有refs/remotes/origin/ 远程跟踪分支。但是假设您之前这样做了,并且您的 Git 已将一个 xyz 分支复制到 refs/remotes/origin/xyz,并且从那时起,有人删除了在 origin 上的分支 xyz?
今天发生的事情是您的 Git 保留了您的 origin/xyz(假设您仍在使用它)。要告诉你的 Git 它应该自动修剪你拥有的任何远程跟踪分支,远程不再有,只需将--prune(或-p)添加到你的命令中:
$ git fetch --prune origin
您的 Git 现在将删除您在 origin 下列出的任何远程跟踪分支,这些分支尚未跟踪名为 origin 的远程上的实际分支。
你也可以这样拼写:
git remote update --prune origin
并且在某些版本的 Git 中存在一些错误,其中两个命令之一有效而另一个无效(我不确定哪个版本,也不确定哪个版本成功)。
手动更新
作为一般规则,您应该让 Git 自动更新远程跟踪分支。但是,在某些特定情况下,您可能需要手动更新它们,例如在手动修改配置以更改远程名称或更改应用于远程的映射(remote.<em>remote</em>.fetch 行)之后,或遇到像我刚才提到的那样的错误。或者,例如,如果您刚刚使用无源 git push 或 git push --delete 删除了一个分支 —git push origin :foo — 并且您的网络连接速度极慢,您可能会迫不及待地无法运行 @ 987654368@然后等待。
在这种情况下,虽然git push . :refs/remotes/origin/foo 确实可以从您的存储库中删除origin/foo,但您不妨使用更直接的命令:
$ git branch -r -d origin/foo
Deleted remote-tracking branch origin/foo (was 5ace313).
事实上,任何远程跟踪分支的更新都可以通过git branch -r(或底层的“管道”命令git update-ref)完成,就像本地分支的更新可以通过git branch完成一样(没有@ 987654375@)。我认为有充分理由使用git push . 更新分支的唯一地方是在不实际检查分支的情况下实现git merge --ff-only 的结果。1
1具体来说,git push . <em>x</em>:<em>y</em> 尝试对本地 ID 进行快进 x——这可以是任何可以解析为有效对象 ID 的东西——使用参考 y。当 y 是本地分支时,它相当于:
git checkout y && git merge --ff-only x && git checkout -
除了它根本不接触工作树。除了原子性和类型检查问题以及可能的符号引用问题之外,它也相当于这个 shell 命令序列:
x="$1" y="$2"
# resolve x to an object ID
xid=$(git rev-parse $x) || exit
# resolve y to a reference
yref=$(git rev-parse --symbolic-full-name $y) || exit
[ -z "$yref" ] && { echo "fatal: $y: not a valid reference"; exit 1; }
# make sure the current value of y is an ancestor of (resolved) x
git merge-base --is-ancestor $yref $xid || { echo 'not a fast forward'; exit 1; }
# ok, it is a fast forward: update
git update-ref -m "fast forward" $yref $xid
请注意,--symbolic-full-ref 将像 origin/HEAD 这样的符号引用解析为它所引用的名称,例如 refs/remotes/origin/master。这在这里可能是不可取的,但是git rev-parse 没有明显的标志来验证引用名称是否有效并将其转换为完全限定的名称,但不会将符号名称扩展到其目标。