如何在Git中获取主分支的默认设置?

10

最近,GitHub建议使用main作为默认分支,而不是master

我们如何获取该名称,以便编写弹性的Git别名? 如何获取Git中“master”分支的默认值?

我在Stack Overflow上搜索过,但在git - how to get default branch?中的解决方案都无法可靠地解决我的问题...

如果我在从develop分支派生出来的feature分支中,它将返回给我develop而不是master(或者develop是从main派生的),那么怎样才能获取“master”分支名称呢?

注意--问题可能是:为什么git remote show origin不显示我master,而是显示develop作为HEAD branch


什么是“默认分支”?如果我在主机上有两个git,一个是/usr/bin/git,另一个是/usr/local/bin/git,前者/usr/bin/git init创建带有“master”的存储库,而后者/usr/local/bin/git init创建带有“main”的存储库 - 那么哪个是“默认”的?在我看来,并没有这样的事情,每个存储库都有自己的默认值,不一定是“master”或“main”;它可以是任何东西,例如“default”或“trunk”。 - phd
1
是的,当然,它可以是存储库所有者决定的任何名称。问题在于,在这里,如何通过程序找到默认名称,以编写具有弹性的Git别名。 - user3341592
执行命令 git remote show origin,然后查找带有 HEAD branch: main 文本的部分(main在克隆时将被替换为默认分支)。这在您链接的页面上有提到,并且应该可以解决问题。如果还是不行,那么您或许应该提出相关问题。我之所以这么说是因为如果您没有说明为什么不适用于您或者您的情境有何不同,那么这个问题很可能会被关闭并作为重复问题,因为您最终可能会得到完全相同的答案。除非您告诉我们在您的情况下有哪些不同之处。 - Lasse V. Karlsen
@LasseV.Karlsen 默认远程也不一定是 origin :-) - phd
如果一个仓库只有一个分支,那么它就是默认的。如果有多个分支但只有一个远程分支,那么从远程解析HEAD并映射回本地分支。但是,如果有多个分支和多个远程分支,那么就糟糕了。所有远程分支和本地分支都是相等的,而默认的概念在用户的头脑中(双关语),没有办法通过编程方式提取知识。我唯一看到的方法是在您的别名/脚本中设置一些合理的默认值(originmaster),但允许用户使用配置或参数进行覆盖。 - phd
@LasseV.Karlsen 和其他人:我对 origin 没有问题(现在不会更改),而且我还没有使用多个远程 - 尽管在我的实际情况中,当查看 git remote show origin 的输出时,我确实看到 develop 作为 HEAD branch,而它应该输出 master(顺便说一下,master 也被列为远程分支之一..)。为什么会这样?我完全不理解... - user3341592
3个回答

12

回答你的问题存在两个问题

你提到了 mainmaster。现在,第一个问题是实际上目前没有任何默认分支。虽然有一些类似的东西,但由于尚未达成共识,所以没有保证的方法来查询它:并非每个人都安装了 Git 版本 2.28 或更高版本。

话虽如此,运行以下命令:

git config --get init.defaultBranch
may会在你自己的Git存储库中生成一些内容,如果生成了并且你的Git版本是2.28或更高,你的 Git将使用其作为运行git init时未命名分支的默认名称。(另请参见提交8747ebb7cde9e90d20794c06e6806f75cd540142。)
请注意,如果有人运行:
git init --initial-branch=xyzzy

创建一个新的空仓库时,在这个新的空仓库中,未出现过的分支的名称将是xyzzy,无论是否存在init.defaultBranch设置以及其中可能包含了什么。同时,--initial-branch选项也首次出现在Git 2.28中。

HEAD

针对您关于git - how to get default branch?的问题:

该问题可以改成:为什么git remote show origin没有显示出master,而是作为HEAD branchdevelop

git remote show命令和git ls-remote命令(试试看)都是通过调用其他Git仓库并从中获取信息完成工作的。在讨论这些信息之前,让我们先来看一下HEAD您自己的仓库中是如何工作的。

Git仓库本质上是由一系列提交组成的集合。每个提交都有一个唯一的哈希ID。所有的Git系统都使用特定算法——目前是SHA-1,但计划切换到SHA-256——根据提交的内容计算哈希ID,以便所有Git系统都针对相同的提交得到相同的哈希ID。

这就是Git在分发提交方面的工作方式。每个仓库都有自己的完整提交集合。当您交叉连接一对Git仓库(例如您自己的仓库和一个名为origin的远程仓库)时,它们会交换哈希ID。由于哈希ID唯一地标识提交,因此发送者可以列出一个哈希ID,接收者可以立即从中判断他是否拥有该提交或需要该提交。加入一些基于提交图形结构的优化,我们就有了大多数需要的have/want协议交换部分。

然而,每个Git都使用名称来查找其自己仓库中的提交:分支名称、标签名称和其他类似的名称。这些名称与哈希ID一起使用,以查找某个特定的提交。那些提交然后也能够查找到任何早期的提交。

为了发送提交,在git fetch匹配中的发送Git会列出他的分支、标签和其他类似的名称,以便接收Git可以确定需要请求哪些提交(如果有的话)。您可以使用git for-each-ref列出自己的名称——全部或某个特定子集。默认情况下,将列出以refs/开头的所有名称。

上面的图片中还缺少一个部分,那就是特殊名称HEAD。这个特殊名称——它不以refs/开头,并且Git在各种特殊方式下都会在内部使用它——通常是Git所谓的一个符号引用(symbolic ref),我们可以使用git symbolic-ref命令来读取它。
$ git symbolic-ref HEAD
refs/heads/master

这告诉我们在这个Git库中,当前分支为名为master的分支。(分支名称都以refs/heads/开头;其他名称以其他基于refs/的前缀开头。)在一个非裸的Git存储库中(通常我们使用的那种),这意味着有人运行了git checkout mastergit switch master

当我们使用git ls-remote连接到其他Git时,他们的 Git会为我们运行git for-each-ref来列出他们的名称。但他们会在列表前加上HEAD的值,如果我们添加--symref选项,1就可以得到他们的HEAD两个方式:

ref: refs/heads/master  HEAD
72c4083ddf91b489b7b7b812df67ee8842177d98        HEAD
71ca53e8125e36efbda17293c50027d31681a41f        refs/heads/maint
72c4083ddf91b489b7b7b812df67ee8842177d98        refs/heads/master
a3ce27912f980d787926b113d17f1f532e50162a        refs/heads/next
[snip]

所以这告诉我,他们的Git存储库将master设置为它们的HEAD。第一行输出直接明确了这一点;接下来的几行显示他们的HEADrefs/heads/master都指向提交72c4083ddf91b489b7b7b812df67ee8842177d98,虽然不太明确。2

git remote show命令具有调用其他Git的能力。3 这意味着他们可以让其列出其作为HEAD的分支。 当您看到:

  HEAD branch: develop

这只是意味着他们的Git将他们的HEAD附加到他们的分支名develop。如果他们的存储库不是bare,则这意味着他们运行了git checkout developgit switch develop
如果他们有一个bare存储库,他们仍然可以使用git --work-tree=... checkoutGIT_WORK_TREE=... git checkout或直接运行git symbolic-ref来获得相同的效果。他们的HEAD所附加的分支名称不一定是他们的mastermain,如果他们有一个的话,他们控制哪个分支名称(如果有的话)存储在他们的HEAD中。
这没有太大的意义。如果他们的HEAD命名为他们的develop,那只是意味着他们的HEAD附加到他们的develop。既然他们的分支名称是他们自己的而不是你的,那对你来说并不重要。
但是有一个地方确实很重要。如果你运行:
git clone <url>

而不是,比如说:

git clone -b somebranch <url>

如果你想要让你的 Git 调用他们的 Git 并“询问他们建议使用哪个分支名称”,那么你需要使用 -b branch,这表示你选择这个分支;如果你不提供此参数,则表示你让你的 Git 采用他们的建议。建议使用的分支名称是他们的 HEAD 所指向的分支。因此,无论是谁——控制另一个 Git 存储库的人——都应该采用任何可用手段将他们的 HEAD 连接到他们个人建议的分支名称。

你无需遵循他们的建议。你在克隆时已经有了自己的分支名称。你不需要使用与他们相同的名称。这取决于你自己。大多数人主要使用相同的名称来使事情更容易理解。


1旧版本的 Git 不支持此 --symref 选项。当新版 Git 与无法进行符号引用传输的旧版 Git 通信时,新版 Git 必须对旧版 Git 的 HEAD 进行猜测。这是一种有点丑陋的情况,我们假设您在这里使用的是全新的 Git 版本。

2这向您展示了旧版 Git 如何进行猜测:如果 HEAD 的哈希 ID 与一个分支名称完全匹配,则表示这是该分支名称。但是,如果 HEAD 的哈希 ID 与两个、三个或更多分支名称的哈希 ID 匹配怎么办?那就会变得很复杂。

3在现代 Git 中,这是您使用 git remote show 的默认方式。我不确定是否曾经存在非默认方式。然而,git remote 命令有许多子命令,其中许多子命令不需要调用其他 Git。


2
注意:最近的 git config clone.defaultRemoteName,甚至“origin”也不是一个安全的默认值(Git 2.30,2021年第一季度)。 - VonC

6

这并不直接回答问题本身,但从实际角度来看,核心问题在于确定仓库是否使用mainmaster命名标准。因此,在大多数情况下,我们可以这样做:

git branch | grep -o -m1 "\b\(master\|main\)\b"
  • -o — 只打印匹配项
  • -m1 — 只打印第一个匹配项

1
既然我在这个问题的讨论中引用了你的正则表达式,那么给它一个赞就是理所当然的了 - 谢谢你的分享! - David Farrell
1
我将其作为答案添加到https://dev59.com/al4b5IYBdhLWcg3wzUfa#70080259,但基本上我所做的就是:将其定义为别名,并在其他地方使用该别名。我喜欢不依赖于远程存在。 - Amory
这太完美了!我同意,将其作为别名并在任何其他命令中使用以刷新变量MAIN_BRANCH或其他内容,是一个很好的解决方案。 - Gandalf Saxe
是的,我做了一些研究,因为Git的“默认”分支master只是按照惯例设置的,而这个惯例已经被废弃了,现在唯一的方法就是使用启发式算法来解决。在一些仓库中,由于git subtree的存在,mainmaster可能都会存在,这使得问题变得更加棘手。 - undefined

2
要获取使用Github的git仓库的默认分支名称,请安装由Github构建的 gh 命令。然后运行以下命令:
cd agithubrepo/
gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name'

输出示例:

main

1
我不确定为什么这篇文章只有少数赞。"默认"分支的概念实际上只存在于这些外部提供者中,而不是Git本身(在Git中,它只是一个默认的init分支,可能与您从上游克隆的存储库不同)。因此,询问提供者的API是最可靠的方法,如果您已经安装了他们的工具,那么也是最简单的方法。感谢您的回答。 - afirth
可能是因为你可以在不使用gh命令的情况下获取默认的远程分支。你只需要使用git就可以做到。而且也因为那不是被问到的内容。请参考这个问题中的许多答案,了解如何获取默认的远程分支。 - undefined

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