了解 .git/config 的 'remote' 和 'branch' 部分

Understanding .git/config's 'remote' and 'branch' sections

这是我的 .git/config 文件的 remotebranch 部分的内容。

[remote "origin"]  
    url = https://EvanAad@bitbucket.org/EvanAad/bitbucketstationlocations.git  
    fetch = +refs/heads/*:refs/remotes/origin/*  
[branch "master"]  
    remote = origin  
    merge = refs/heads/master

这些部分的内容的含义和目的是什么,特别是 fetchmerge 小节? Git 如何使用此信息来指导其操作?

它叫做 refspec。 它是 git 用来与远程服务器“对话”并将本地分支映射到远程分支的机制。

参考规格

refspec 将本地存储库中的分支映射到远程存储库中的分支。
这使得使用本地 Git 命令管理远程分支以及配置一些高级 git 推送和 git 获取行为成为可能。

refspec 指定为 [+]<src>:<dst><src>参数为本地仓库中的源分支,<dst>参数为远程仓库中的目标分支。
可选的+标志用于强制远程存储库执行非快进更新

Refspecs 可以与git push 命令一起使用,为远程分支指定一个不同的名称。例如,以下命令像普通 git 推送一样将 master 分支推送到原始远程仓库,但它使用 qa-master 作为原始仓库中分支的名称。这对于需要将自己的分支推送到远程仓库的 QA 团队很有用。

git push origin master:refs/heads/qa-master

通过在 Git 配置文件中添加几行,您可以使用 refspecs 来改变 git fetch 的行为。

默认情况下,git fetch 获取远程存储库中的所有分支。原因是 .git/config 文件的以下部分:

[remote "origin"]
    url = https://git@github.com:mary/example-repo.git
    fetch = +refs/heads/*:refs/remotes/origin/*

fetch 行告诉 git fetch 从原始仓库下载所有分支
但是,某些工作流程并不需要所有这些。例如,许多持续集成工作流只关心 master 分支。要仅获取 master 分支,请更改获取行以匹配以下内容:

[remote "origin"]
    url = https://git@github.com:mary/example-repo.git
    fetch = +refs/heads/master:refs/remotes/origin/master

您也可以以类似的方式配置git 推送。例如,如果你想始终将 master 分支推送到 origin 远程的 qa-master(就像我们上面所做的那样),你可以将配置文件更改为:

[remote "origin"]
    url = https://git@github.com:mary/example-repo.git
    fetch = +refs/heads/master:refs/remotes/origin/master
    push = refs/heads/master:refs/heads/qa-master

Refspecs 让您可以完全控制各种 Git 命令如何在存储库之间传输分支

它们允许您从本地存储库重命名删除 分支,fetch/push 到具有不同名称的分支,并配置git push 和 git fetch 只使用你想要的分支。

TL;DR 总结

总的来说,我们对两三件事感兴趣:

  1. 如果你 运行 git fetch 没有额外的参数,会发生什么?
  2. 如果你 运行 git mergegit rebase 没有额外的参数,会发生什么?
  3. 如果你 运行 git pull 没有额外的参数,会发生什么?

问题 #1 的简短回答是:Git 选择您的一个 遥控器 从中获取,然后从该遥控器获取。远程 Git 选择来自 remote = <em>name</em> 部分下的 [branch] 设置。如果没有任何这样的设置,Git 使用 origin.

问题 2 的答案是:Git 选择一些名称来使用,就好像你有 运行 git 合并 <em>name </em>git 变基 <em>name</em>name 基于 merge = <em>ref</em> 设置在 [branch] 部分下——但它的工作方式有点模糊:如果同一部分说,例如,remote = originbranch = refs/heads/master,名称 Git选择使用的不是 master,而是 origin/master。如果显示 remote = originbranch = develop,则 Git 选择的名称是 origin/develop,依此类推。

(虽然这看起来很简单,但 Git 中的实际映射相当棘手:如果该部分显示 remote = .branch = master,则名称为 master,不是 ./master,例如。如果您设置了不寻常的获取 refspecs,可能会发生更多奇怪的事情。这个答案根本不涵盖最后一种情况。)

问题 #3 的答案在某些方面是最简单的:git pull 首先简单地 运行s git fetch,然后——假设成功——其他两个命令之一, git mergegit rebase,所以你真的只需要看问题1和2。

每个 branch 部分下的 merge 条目,我认为是最不明显的。 Git 文档让它有点模糊。先说其他的吧。

[remote "..."] 部分下的设置

有很多可能的设置。通常,您不必直接使用 git config 设置它们中的任何一个——几乎所有它们都有包装命令以更“用户友好”的方式设置它们。这包括您在此处看到的两个设置。也很少有人想改变这些。

每个命名遥控器的 remote 部分,例如 origin,列出了 git fetch 的 URL(以及可选的单独推送 URL git push,其他 remote.* 配置项为 described in the git config documentation)。它还有一个或多个 fetch 行,为来自该遥控器的 git fetch 提供默认的 refspec 参数。

也就是说,如果你 运行:

git fetch origin

Git 将查找 remote.origin.url 以查看连接位置,然后连接到那里,然后根据所有 remote.origin.fetch 条目检索引用。您在此处看到的默认值:

+refs/heads/*:refs/remotes/origin/*

告诉Git从远程复制所有个分支1,将它们重命名为origin/-在您自己的存储库中添加前缀远程跟踪分支2,因此:

git fetch origin

基本上什么都拿来了。 (领先的 + 说 Git 应该这样做,无论远程跟踪分支更新是否是快进操作。也就是说,它就像使用 --force,但不必指定 --force.)

另一方面,如果您 运行:

git fetch origin a:b c:d

Git 将 完全忽略 所有 fetch = 行,仅从远程检索引用 ac,将它们写入存储库中的引用 bd。 (并且由于这既没有 + 也没有 --force,其中的 none 将被强制更新——尽管在大多数情况下这没有任何区别。)


1, 2 一个 reference 是一个涵盖两个分支 标签的通用术语(还有更多的东西)。像 master 这样的分支名称只是以 refs/heads/ 开头的引用的简写。像 origin/master 这样的远程跟踪分支名称只是以 refs/remotes/ 开头的引用的简写。请注意,origin/ 部分来自 fetch = 行——但要使其按预期方式工作,该行 必须 与方括号中的远程。


[branch "..."] 部分下的设置

有很多可能的设置。通常,您不必直接使用 git config 设置它们中的任何一个——几乎所有它们都有包装命令以更“用户友好”的方式设置它们。这包括您在此处看到的两个设置。想要更改其中一个或两个的情况并不少见,使用我们稍后会看到的命令。

remote 部分本身很清楚,不过:这意味着如果你在分支 master 和 运行 git fetch 上而没有给出远程名称Git 应该从名为 origin.

的远程获取

merge 部分是棘手的部分。它列出了在远程 上看到的分支名称。注意当我们运行git fetch origin时,我们告诉我们的Git调用另一个Git,找到另一个Git 的 master,并将其复制到我们的存储库中,但将其命名为 origin/master。然而……merge 行显示 merge = refs/heads/master。它不应该说:merge = refs/remotes/origin/master?

它可能应该——但是这个设置首先早于遥控器的发明。所以它没有;相反,它列出了参考文献的全名 ,因为它出现在遥控器上

如果您 运行 git mergegit rebase 没有提供要合并或变基的分支名称,则会使用此设置。 Git 运行 通过远程 fetch = 行提供的映射来确定它应该与 origin/master 合并的名称,例如。

此设置也被git pull便利命令使用,实际上3与运行ning git fetch后跟运行宁git merge.

您可能想要更改其中之一或两者。例如,如果您创建一个新的本地分支 feature/tall,它可能根本没有 branch.feature/tall.remotebranch.feature/tall.merge 设置。

由于您刚刚创建了这个分支,所以没有origin/feature/tallorigin 上的 Git 还没有 feature/tall,所以你没有它的副本。

然后你 git push origin feature/tall:feature/tall 让你的 Git 调用 origin 的 Git 并让他们的 Git 创建 那个分支,所以你现在 origin/feature/tall。您可能希望 Git 记住这一点。

可以 运行 两个 git config 命令,但是你可以 运行 一个更高级别的包装命令:

git branch --set-upstream-to=origin/feature/tall feature/tall

这会告诉您的 Git 将 branch.feature/tall.remote 设置为 origin,并将 branch.feature/tall.merge 设置为 refs/heads/feature/tall(即 origin 上的名称).

您可以使用 git push -u 组合 git pushgit branch --set-upstream-to 步骤,这更好,但这里的要点仍然存在:您使用包装器获得 both 值一次设置,因为只设置一个值不是很有用。4

特殊远程名称. 表示此存储库(与某些远程存储库相对)。如果 [branch "xyzzy"] 部分说 remote = .branch = refs/heads/whatever,那么分支 xyzzylocal 分支 whatever 作为它的上游,而不是将 origin/whatever 作为其上游。


3这故意掩盖了很多繁琐的细节。

4仅设置 remote 部分 会影响未来 git push,但 git mergegit rebase 将无法在没有这两个条目的情况下进行远程跟踪分支映射。