大多数开发者都熟悉标准的Git工作流:创建分支、进行更改,然后将这些更改推送回主仓库的同一分支。Git称此为集中式工作流。这种方式直接明了,适用于许多项目。
然而,有时您可能希望将不同分支的更改直接拉取到您的特性分支,以帮助保持分支更新,而无需不断合并或变基。但您仍希望将本地更改推送到自己的分支。这就是三角工作流的用武之地。
可能有些人已经在使用三角工作流,甚至没有意识到。当您分叉一个仓库,为您的分叉做出贡献,然后向原始仓库打开拉取请求时,您就在使用三角工作流。虽然这在github.com上可以无缝工作,但使用GitHub CLI时,这个过程并不总是顺畅。
GitHub CLI团队最近进行了改进(在v2.71.2版本中发布),以更好地支持这些三角工作流,确保gh pr命令与您的Git配置顺畅配合。因此,无论您是使用集中式工作流还是更复杂的三角工作流,GitHub CLI都能更好地满足您的需求。
如果您已经熟悉Git如何处理三角工作流,可以跳过前面部分,直接了解如何使用gh pr命令与三角工作流。否则,让我们深入了解Git和GitHub CLI历史上的差异,以及在我们首次请求四年半后,我们终于解锁了在GitHub CLI中使用三角工作流管理拉取请求的功能。
首先,Git基础课程
为了为我们设定的目标提供框架,首先理解一些Git基础知识很重要。Git的核心是一种存储和分类仓库更改并在该仓库副本之间通信这些更改的方式。这种工作流通常如下图所示:
图1:典型的git分支设置
该图的构建块说明了两个重要的Git概念,您可能每天都在使用:引用和推送/拉取。
引用
引用是对仓库和分支的引用。它有两个部分:远程(通常是一个名称,如origin或upstream)和分支。如果远程是本地仓库,则为空。因此,在上面的示例中,紫色框中的origin/branch是一个远程引用,指的是名为origin的仓库上一个名为branch的分支,而绿色框中的branch是一个本地引用,指的是本地机器上一个名为branch的分支。
在使用GitHub时,远程引用通常是您托管在GitHub上的仓库。在上图中,您可以将紫色框视为GitHub,绿色框视为您的本地机器。
推送和拉取
推送和拉取指的是相同的操作,但从两个不同的角度。您是推送还是拉取取决于您是发送还是接收更改。我可以将一个提交推送到您的仓库,或者您可以从我的仓库拉取该提交,对该操作的引用是相同的。
为了消除歧义,我们将不同的引用称为headRef或baseRef,其中headRef是发送更改(推送它们),baseRef是接收更改(拉取它们)。
图2:为推送/拉取操作消除headRef和baseRef的歧义
在处理分支时,我们通常将其拉取操作的headRef称为其pullRef,将其推送操作的baseRef称为其pushRef。这是因为在这些情况下,工作分支是拉取的baseRef和推送的headRef,所以它们已经消除了歧义。
@{push}修订语法
事实证明,Git有一个方便的内置工具来引用分支的pushRef:@{push}修订语法。您通常可以通过运行以下命令来确定分支的pushRef:
|
|
如果能够确定,这将产生一个人类可读的引用,如origin/branch。
拉取请求
在GitHub上,拉取请求是将更改从一个引用集成到另一个引用的提议。特别是,它们在实际执行集成操作(通常称为合并)之前充当一个简单的“暂停”,当更改从一个引用推送到另一个引用时。这个暂停允许人类(代码审查)和机器人(GitHub Copilot审查和GitHub Actions工作流)在更改集成之前检查代码。拉取请求的名称 specifically 来自这种语言:您请求一个引用拉取您的更改到自身。
图3:演示GitHub拉取请求如何对应推送和拉取
常见的Git工作流
既然您了解了基础知识,让我们谈谈我们通常每天使用Git的工作流。
集中式工作流是大多数人与Git和GitHub交互的方式。在这种配置中,任何给定的分支都推送和拉取自具有相同分支名称的远程引用。对于我们大多数人来说,这种类型的配置在克隆仓库和推送分支时默认设置。这是图1中所示的情况。
相比之下,三角工作流推送到和拉取自不同的引用。这种配置的一个常见用例是直接将更改从远程仓库的默认分支拉取到您的本地特性分支,消除了在您的特性分支上运行诸如git rebase <default>或git merge <default>之类的命令的需要,以确保您正在处理的分支始终与默认分支保持同步。然而,在推送更改时,这种配置通常会推送到具有与特性分支相同分支名称的远程引用。
图4:并置集中式工作流和三角工作流。
当考虑拉取请求时,我们完成了三角形:headRef是本地引用的pushRef,baseRef是本地分支的pullRef:
图5:三角工作流
我们可以更进一步,使用不同的远程设置三角工作流。这最常发生在您在分叉上开发时。在这种情况下,您通常为分叉和源远程赋予不同的名称。我将使用origin表示分叉,upstream表示源,因为这些是在这些设置中常用的名称。这与上面的三角工作流功能完全相同,但pushRef和pullRef上的远程和分支不同:
图6:并置三角工作流和具有不同远程(如分叉)的集中式工作流
使用Git配置文件进行三角工作流
有两种主要方式可以使用Git配置设置三角工作流——通常在.git/config或.gitconfig文件中定义。在解释这些之前,让我们看一下在集中式工作流的仓库.git/config文件中相关部分的典型配置:
|
|
图7:在.git/config中找到的典型Git配置设置
[remote "origin"]部分将位于github.com/OWNER/REPO.git的Git仓库命名为origin,因此我们可以在其他地方通过该名称引用它。我们可以在特定[branch]配置中看到该引用用于default和branch分支的remote键。这个键与分支名称结合,通常构成分支的pushRef:在这个例子中,它是origin/branch。
remote和merge键结合构成分支的pullRef:在这个例子中,它是origin/branch。
设置三角分支工作流
组装三角工作流的最简单方法是将分支的merge键设置为不同的分支名称,如下所示:
|
|
图8:在.git/config中找到的三角分支的Git配置
这将导致分支pullRef为origin/default,但pushRef为origin/branch,如图9所示。
图9:三角分支工作流
设置三角分叉工作流
使用三角分叉需要比三角分支更多的自定义,因为我们处理的是多个远程。因此,我们的Git配置中的远程看起来与之前在图7中显示的不同:
|
|
图10:在.git/config中找到的多远程Git设置的Git配置
upstream和origin是在这种构造中最常用的名称,所以我在这里使用了它们,但它们可以命名为任何您想要的名称¹。
然而,在upstream和origin之间切换分支的remote键实际上不会设置三角分叉工作流——它只会设置与这些远程中任何一个的集中式工作流,如图6中所示的集中式工作流。幸运的是,有两个常见的Git配置选项可以改变这种行为。
设置分支的pushremote
分支的配置有一个名为pushremote的键,它完全按照名称所示:配置分支将推送到的远程。使用pushremote的三角分叉工作流配置可能如下所示:
|
|
图11:在.git/config中找到的使用pushremote的三角分叉的Git配置
这组装了我们在图12中看到的三角分叉仓库。pullRef是upstream/default,由结合remote和merge键确定,而pushRef是origin/branch,由结合pushremote键和分支名称确定。
图12:三角分叉工作流
设置仓库的remote.pushDefault
要配置仓库中的所有分支具有与您在图12中看到的相同行为,您可以改为设置仓库的pushDefault。此配置如下:
|
|
图13:在.git/config中找到的使用remote.pushDefault的三角分叉的Git配置
这组装了与上面图12相同的三角分叉仓库,但这次pushRef由结合remote.pushDefault键和分支名称确定, resulting in origin/branch。
当同时使用分支的pushremote和仓库的remote.pushDefault键时,Git将优先解析分支的配置 over 仓库的,因此pushremote上设置的远程取代remote.pushDefault上设置的远程。
更新gh pr命令集以反映Git
以前,gh pr命令集没有以与Git相同的方式解析pushRefs和pullRefs。这是由于技术设计决策使得此更改既困难又复杂。与其讨论那种复杂性——一个足够大的话题,本身就可以写一整篇文章——我将在这里重点介绍您现在可以使用更新的gh pr命令集做什么。
如果您按照上述方式设置三角Git工作流,我们将根据您的Git配置自动解析gh pr命令。
更具体地说,当尝试解析分支的拉取请求时,GitHub CLI将首先尊重@{push}解析到的内容,如果它解析成功。然后它将回退到尊重分支的pushremote,如果未设置,最后查找仓库的remote.pushDefault配置设置。
这意味着CLI假设您的分支的pullRef是拉取请求的baseRef,分支的pushRef是拉取请求的headRef。换句话说,如果您配置了git pull和git push工作,那么gh pr命令应该就能工作²。下面的图,是图5的通用版本,很好地演示了这一点:
图14:GitHub CLI支持的三角工作流,关于分支的pullRef和pushRef。这是图5的通用版本
结论
我们不断努力改进GitHub CLI,并希望GitHub CLI的行为合理反映Git的行为。这是一个团队努力——每个人都为理解、审查和测试代码以启用此增强的gh pr命令集功能做出了贡献。
如果没有我们贡献者的支持,这也不可能发生,因此我们向他们表示感谢:
- @Frederick888 打开原始拉取请求
- @benknoble 为拉取请求审查和反馈提供支持
- @phil-blain 在原始问题上强调我们在这里讨论的配置
- @neutrinoceros 和 @rd-yan-farba 报告了团队在v2.66.1中修复的几个错误
- @pdunnavant 报告了我们在v2.71.1中修复的错误
- @cs278 报告了我们在v2.71.2中修复的错误
CLI对三角工作流的原生支持经过了4.5年的努力,我们很自豪能够为社区提供此更新。
GitHub CLI团队 @andyfeller, @babakks, @bagtoad, @jtmcg, @mxie, @RyanHecht, 和 @williammartin
¹ gh中的一些命令对远程名称有意见,将按此顺序解析远程:upstream, github, origin, <其他远程不稳定排序>。有一个方便的命令您可以运行来取代此行为:* gh repo set-default [<repository>] 以覆盖上述默认行为并优先将<repository>解析为默认远程仓库。
² 如果您发现一个Git配置不起作用,请在OSS仓库中打开一个问题,以便我们修复它。