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

18

这是我的.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
这些部分的含义和目的是什么,特别是“fetch”和“merge”子部分?Git 如何利用这些信息指导其操作?

4
您可以在文档中阅读所有这些内容...https://git-scm.com/docs/git-config - René Höhle
1
@Stony:我不理解这份文档。我希望有更清晰的解释。 - Evan Aad
@Rene:在我看来,关于git-config的文档解释了你可以使用git-config列出和操作git配置文件的所有方式,但没有说明各种内容对git行为的影响。 - qxotk
2个回答

28

TL;DR summary

Git fetches from one remote when you run "git fetch" with no additional arguments. When running "git merge" or "git rebase" with no additional arguments, Git uses a name based on the "merge" setting under the "branch" section. Running "git pull" with no additional arguments runs "git fetch" followed by either "git merge" or "git rebase".

Long

The "merge" setting under each "branch" section is the least obvious of the three settings mentioned. The Git documentation is not very clear about it, so let's start with the other two.

Settings under a "[remote "..."]" section

有很多可能的设置。通常情况下,你不需要直接用git config来设置它们——几乎所有这些设置都有包装命令以更“用户友好”的方式进行设置。这包括您在此处看到的两个设置。很少需要更改这些设置。

每个命名远程主机(例如origin)的remote部分列出了git fetch的URL(和可选的单独推送URL),以及其他remote.*配置项,如《git config》文档中所述。它还有一个或多个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=”行,仅从远程检索引用a和c,并将它们写入存储库中的引用b和d。由于这既没有“+”也没有“--force”,因此这些引用都不会被强制更新 - 虽然在大多数情况下这没有任何区别。参考是一个泛指词,涵盖分支和标签(以及其他更多的内容)。分支名称如master只是以refs/heads/开头的引用的简称。Remote-tracking分支名称(origin/master)只是以refs/remotes/开头的引用的简称。请注意,origin/部分来自fetch =行,但为了使所有这些正常工作,该行必须匹配方括号中的remote名称。在[branch "..." ]部分下的设置有很多可能的设置。通常,您不需要使用git config直接设置它们 - 几乎所有这些设置都有包装器命令以更加“用户友好”的方式设置它们。其中包括您在此处看到的两个设置。远程部分本身非常清晰:它意味着如果您在分支master上运行git fetch而根本没有给出远程名称,则Git应该从名为origin的远程获取。合并部分比较棘手。它列出了一个分支的名称,正如在远程上看到的那样。它可能应该是refs/remotes/origin/master,但是这个设置比远程本身还要早。因此,它没有列出引用的完整名称,而是列出了在远程上显示的引用的全名。

这个设置会在你运行 git merge 或者 git rebase 时,没有提供分支名字的情况下使用。Git 通过 fetch = 行提供的映射来确定它应该合并哪个分支,例如它会合并到 origin/master

这个设置也被 git pull 命令使用,这个命令实际上等同于运行 git fetch 然后运行 git merge

你可能需要更改这些设置中的一个或两个。例如,如果你创建了一个新的本地分支 feature/tall,它可能根本就没有设置branch.feature/tall.remotebranch.feature/tall.merge

因为你刚刚创建了这个分支,所以没有origin/feature/tall。位于 origin 的 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 -ugit pushgit branch --set-upstream-to步骤组合在一起,这样更好。但重点仍然是:您使用一个包装器同时设置两个值,因为仅设置一个值不是那么有用。4

特殊的远程名称.表示此存储库(而不是某个远程存储库)。如果[branch "xyzzy"]部分说remote = .branch = refs/heads/whatever,则分支xyzzy具有本地分支whatever作为其上游,而不是以origin/whatever为其上游。


3 这故意忽略了很多琐碎的细节。

4 仅设置remote部分确认会影响到未来的git push,但是git mergegit rebase无法进行远程跟踪分支映射而没有这两个条目。


branch 部分下的设置对 fetch 没有影响,只对 mergepull 有效,是吗? - Evan Aad
1
没错。这些只会影响到 git pull,因为它运行的是 git merge,或者如果你进行了配置,那么就是 git rebase,尽管将 git pull 配置为运行 git rebase 的东西又是另一个 branch-section 条目! - torek
@torek 但是你还是写道:“remote部分本身就很清楚:[...] Git应该从名为origin的远程获取”……如果你能澄清一下那就太好了。 - philipp2100
@philipp2100:为了确保我理解正确:这里有什么困惑吗?如果我在这里包含实际文本 remote = origin,是否有帮助? - torek
@philipp2100:啊,这里的问题在于 remote = <name> 告诉 git fetch 要从哪个远程仓库获取。它对获取操作本身没有影响,只是决定了使用哪个 URL 等信息。如果你想要控制从特定远程仓库获取操作,包括 URL 本身,你需要查看 [remote] 部分。我会尽力让这更清晰明了。 - torek
显示剩余2条评论

13

它被称为refspec。 这是git用来与远程服务器“交流”并将本地分支映射到远程分支的机制。

Refspecs

Refspec将本地库中的分支映射到远程库中的分支。
这使得可以使用本地Git命令管理远程分支,并配置一些高级git push和git fetch行为。

Refspec指定为[+]<src>:<dst><src>参数是本地库中的源分支,<dst>参数是远程库中的目标分支。
可选的+符号是用于强制远程库执行非快速向前更新

Refspec可与git push命令一起使用,以为远程分支指定不同的名称。例如,以下命令将主分支推送到origin远程库,就像普通的git push一样,但它将qa-master用作在origin repo中的分支名称。这对需要将自己的分支推送到远程库的QA团队非常有用。

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

通过在Git配置文件中添加几行代码,您可以使用refspec来改变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命令以匹配以下内容:

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

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

您也可以类似地配置git push。例如,如果您想始终将主分支推送到源远程的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仅使用所需的分支。


假设 fetch 引用规范为 fetch = +refs/heads/*:refs/remotes/origin/*。如果远程仓库有一个名为 mybranch 的分支,而我的本地仓库没有同名的远程跟踪分支,那么会创建一个同名的远程跟踪分支,还是会报错? - Evan Aad
1
如果分支不存在,它将创建新的分支,但是如果您使用的是git v2.X并且没有更改默认的fetch refspec,则只能推送到相同的分支。 - CodeWizard
相反,如果我的本地仓库有一个名为 origin/mybranch 的远程跟踪分支,它是由之前的fetch创建的,但是与此同时,分支 mybranch 已在远程上被删除。在进行fetch时,根据我之前评论中提到的refspec,会发生什么?本地的 origin/mybranch 是否会被删除? - Evan Aad
1
如果一个分支被删除了,你可以使用 git fetch --all --prune 命令更新本地仓库,它会删除本地未删除的分支。如果你尝试将已删除的分支推送到远程仓库,它将会在远程仓库上重新创建。 - CodeWizard
@CodeWizard,您可以请格式化这行代码吗 - fetch = +refs/heads/master:refs/remotes/origin/master?我不能这样做,因为它没有足够的字符数。 - hIpPy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接