更好的堆叠拉出请求模型

2021-04-24 07:05:22

此页面上的一些可视化可能在具有小屏幕的设备上不一致地呈现。如果您注意到任何奇怪性,请在较大的屏幕(甚至横向模式)上拍摄此镜头。

代码审查使用GitHub拉出请求(PRS)通常很不错,但是您'重新遇到中大的变化,特别是当涉及的依赖性意味着您可以将它们分成不同的PRS 。每个更改/功能通常都在单个PR中。在原始版本上的更改要么在相同的PR中转移,或者必须等到PR合并,所以可以创建第二个PR。

当单个大的PR有良好的犯罪,它'序列相当容易审查它,但是还有其他潜在的问题。可以单独批准,所以你必须将整个公关视为批准正在进行直到它'所做的,即使只有一个小部分是争用。依赖性但不相关的变化也是有问题的。你必须把它们放在一个pr中,这可能会混淆。

这个问题的唯一真实解决方案(我知道,无论如何)是堆叠的PRS的概念。这里的基本想法:

当它'时间合并时,从最顶层的PR开始(从掌握最远的那个)并在每个PR中合并/ rebase,直到您将整个堆栈合并到掌握

github(遗憾的是)没有为堆叠的prs提供任何本机支持,但(假设您可以' t切换到另一个工具),管理一堆PRS ISN' TOMELINGALY。要记住几个细微差别,以及可以帮助自动化大部分令人讨厌的比特的工具。我' ll涵盖这篇文章的其余部分。

在使用堆叠的PR工作流程一段时间后,效果很明显:

依赖关系:您的功能取决于(但否则不相关)可以进入单独的PR。

审阅者:堆栈中的每一个PR都可能被分配给不同的审稿人集,这可能有助于一些不同的上下文 - 斜坡,专业领域或工作量。

首先,让' s转过一堆PRS的视觉示例。我发现Git概念比文本更容易理解,而不是文字!

在这里,&#39

首先,这里的一个简单的可视化,它增加了一些提交给主分支。请注意,您可以悬停/点击Git操作列表部分中的条目,以跳过/后面。

现在我们在主人上有一些提交,让' s创建一个名为f1的新功能分支,并为它添加一些提交。

最后,将每个分支推到github。这将添加原点/ *(假设您的远程用于Github是命名原点)跟踪分支针对每个本地特征分支。

此时,您将使用GitHub' S拉请求UI为每个分支创建PR。这将最终查看这样的东西(请注意每个公关的合并到目标中的合并):

您现在发送这三个PRS OUT以进行代码审查。如果他们'重新获得批准,没有要求更改,你很高兴!从上到下,在堆栈中合并 - 在这种情况下,我们'重新合并功能3' s pr,功能2' s pr,并且在该命令中,使用github和#39; s ui。

GitHub' S主分支现在指向一个合并的提交,包括PR堆栈中所有变化的总和 - 我们'重新完成堆栈!

如果您发现此处的所有合并都使得Git历史难以阅读,您可以使用Git Merge --Squash在最终合并到掌握,而是将为您带来一个新的提交。

当然,代码评论aren' t通常在实践中这种摩擦无止。请求更改,您来回前往,问题分支在拉出请求的寿命期间看到了主动开发。

假设所有三个特征分支都需要每个特征分支需要更改,从而增加了每个要素分支的提交:

现在,如果您从提交的提交中添加到F3(1741)并按照祖先链一直掌握,您将错过添加到F2(11F4)和F1(CA9F)的新提交。这可能是一个问题 - 这两个提交可能包含f3取决于的变化,但它们'重新成为该链的部分。我们如何更改此操作,因此提交再次形成线性序列?

最简单的选择是再次使用git-merge,但这将使您的git历史非常难以阅读,鉴于您'重新通过多轮代码审查,每个都可能需要一系列合并。

另一种解决方案是使用Git-Rebase。从最低单位的特征分支开始(在这种情况下F2),我们将每个分支绑定到下面的分支。这看起来像:

这让您回到有一个线性链条。请注意,使用Git-Rebase也需要强制推动,因为我们'重新将分支指针F2和F3移动到完全新的提交。

到现在为止还挺好!您可以使用像这样的重生序列以在代码评估期间在同步中保持PRS堆栈。这在某些情况下足够好,但是在某些情况下,具有这种方法的一个辉煌问题 - 它可以' t手柄冲突。 Git-Rebase包含重复数据删除机制,以帮助它避免采摘重复的提交,但这并不适用于冲突的提交。

列出从头到达的所有提交,但不是来自< branch_name>

在图7中,Git Rebase F1相当简单 - 它选择了所有F2和#39; S致力于F1。下一个rebase(git rebase f2)是一个不同的故事,但是。在应用此rebase之前,可以从F2访问的F3访问有四个提交,但是从F2访问,但rebase仅导致两种提交应用于F2。

这是正确的行为,但这是如何工作的? Git-Rebase如何知道不重新申请D949和1039?

这里的描述("在Git-Rebase文档中,' s步骤3的描述

然后将先前保存到临时区域中的提交,按顺序将其逐个重新插入当前分支。请注意,在头部中引入与头部提交相同的文本变化的任何提交.. <上游&gt;省略(即,将跳过已经接受了不同提交消息或时间戳的上游的补丁。

Git-Rebase Defuplicates基于其内容而不是SHA提交,因此请尽一项重复的提交。这让我们留下了我们想要的两个F3提交。在上面的可视化中,提交D949和1039的内容与提交D7AF和E4BE的内容匹配,因此Git-Rebase离开了Git Rebase F2操作的D949和1039。

绑定遇到冲突时会发生什么?解决冲突后,重复数据删除逻辑是否仍然有效?不幸的是,答案是否定的。发生冲突(并解决),问题中的两个提交不再具有相同的内容,所以git rebase&#39;重复数据删除的启发式失败,将我们的一系列提交人员离开:

请注意,我们从两个粉红色的提交开始,但以四个结尾! DE69是D949的版本,其中发生冲突并被修复 - 结果是他们的文本内容并不匹配。当GIT Rebase F2正在创建一个提交的申请清单时,它将来自D949到1586的所有提交所因为冲突举动它&#39; S重复数据删除逻辑,导致D949和1039正在重新删除(通常导致进一步的冲突+一般悲伤在实践中)。

这是堆叠的PR工作流程的主要问题。当您对导致冲突进行更改时,Git-Rebase尝试多次选择相同的提交,这使得修复堆栈难以变得不可能。你&#39;重复重复的提交,进一步冲突,也没有简单的方法。

在那里有一种更好的方法来思考堆积的PRS,尽管如此,它完全避免了这个问题,我&#39; ll进入下一个。

这里&#39;核心想法:从掌握开始,樱桃 - 选择每个分支的每一个分支,按顺序。你沿途修复冲突,这并不影响任何后续的樱桃选择。当你&#39; rectrry-chicking,将每个分支指针移动到新的提交链中。这看起来像:

注意,在整个樱桃选择操作期间,分支机构&#39; t向上推到github。这是因为原点/ *分支方便地用作每个樱桃镐的边界的标记。您甚至可以概括为:

如果您尚不熟悉这种双点语法,Git将它使用它来选择提交范围。在樱桃镐的背景下,a..b意味着:

一旦堆栈已经用这种樱桃选秀序列修理,我们就可以立即将所有分支推到Github上:

这真的是有改进的堆叠PR工作流程。我现在使用了这几个月了,哈登&#39;甚至与之遇到了冲突的地方。

用手管理所有这些元数据是非常烦人的,这将我们带来了......

自动化大量此工作流程的最简单方法是一个简单的shell脚本。这里&#39;脚本使用上面概述的樱桃选择策略修复堆栈。它在文件中读取在文件的特征分支(以堆栈顺序,从最低到最高到最低)中读取,并修复堆栈:

#!/ usr / bin / env bash git checkout --detach主边界=&#34;原产地/硕士&#34;推=&#34;&#34;读线;做git cherry-pick&#34; $边界&#34; ..&#34; $行&#34; git branch --force&#34; $行&#34;边界=&#34;来源/ $行&#34;推=&#34; $推$ line:refs / head / $ line&#34; Donegit推送-f Origin&#34; $ Push&#34;

它需要将堆栈中的分支列表传递给它,这很烦人为构建和amp;维持。

它可以&#39;当发生冲突时,所以你必须修复冲突,(以某种方式)从它停止的点重新运行它,这是最好的。

我不得不担心这种事情,所以我写了一个工具 - GH-stact - 它处理关于此工作流程的所有恼人的比特。

给定&#39; s的给定堆栈的标识符,它在标题中寻找标识符(使用github api)的所有PR,并构建依赖图以确定哪个PR合并到其中。我通常使用JIRA票号作为标识符。

GH-Stack将在堆栈中的每个PR的描述中添加(Markdown)表,其中包含堆栈中所有PRS的列表。这有助于您在PR Page&#39的时候快速恢复轴承。在这里&#39; s(编辑)例子:

首先,在github上设置pr堆栈,所以每个pr包含&#34; jira-19205&#34;在标题中。一旦它与GitHub身份验证,GH-Stack现在可以向您展示堆栈中的PRS列表:

❯导出ghstack_oauth_token =&lt; github_personal_access_token&gt;❯gh-stack log jira-19205#1:[Jira-19205] 1:协同分布式解决方案(基础)#2:[Jira-19205] 2:汇总无摩擦系统(合并到#1中)#3:[Jira-19205] 3:白板垂直哈希特拉斯(合并到#2)#4:[Jira-19205] 4:目标支持Web的范式(合并到#3)

❯GH-stack注释JIRA-192051:[JIRA-19205] 1:协同分布式解决方案2:[JIRA-19205] 2:汇总无摩擦系统3:[JIRA-19205] 3:白板垂直散列3:[JIRA-19205] 4:目标启用Web的范式标准以更新这些PRS☝️类型&#39;是&#39;继续:yesdone!

这增加了A&#34;堆叠的PR&#34;堆栈中每个拉出请求的描述部分,如:

这设置为idempotent,因此如果存在多次运行,则会替换现有注释,而不是将多个表附加到PR描述。

另一件事GH-Stack所做的是使用上述相同的樱桃选择策略修复给定堆栈的PRS,但比Shell脚本更智能地智能地。鉴于一堆PRS,您可以使用:

其中-c是repo的路径,-b是第一个樱桃选择的边界(通常这是一个原点/主机)。

这会检查-b表示的参考文献,并启动每个特征分支的樱桃选择提交,一个接一个。请注意,唯一标识符是您所需的全部 - 有没有必要记住您和#39的所有分支机构的名称;重新处理。

当樱桃选择遇到冲突时,GH-stack暂停,并允许您解决冲突(通过将分辨率与GIT添加)进行标记)。一旦完成,它的完成了,它的副标签并继续樱桃捡起堆栈。

本地分支机构在最终更新,因此在持续的autorebase中止,留下了杂乱/不一致状态的存储库。

签出提交{id:e5082978be5469d24e506e4d4a10b59fc1fe8374,摘要:&#34;提交硕士&#34;在Pr:&#34; f1&#34; cherry-picking:提交{id:2b2e2785fcdea567d58a29025b42019e992cd12,摘要:&#34;&#34;在Pr:&#34; f2&#34; cherry-picking:提交{id:8e80c1f7e623f7b8f3b77980ede643af3ccf2fa,摘要:&#34;&#34; }冲突!但是,手动解决,而是GIT添加(Don&#39; T&#39;逃跑任何`git cherry-pick`命令)。类型&#39;是和#39;要继续:在Pr:&#34; f3&#34; cherry-picking:提交{id:80d0fb9b01e091e2b7919f0db62a5f16c63f4,摘要:&#34; f3&#34提交; } [&#34; 59066eb5cce861475ee77cf617ffe6e6b9bfa7b4:参/头/ F1&#34;,&#34; 61dd498917148e6d753c5c86e7528c2f08c95843:参/头/ F2&#34;,&#34; 9e0d1133cc1587ab025bbf7e8699bb317fbb74fd:参/头/ F3&#34;]要推这些refspecs☝️型&#39;是&#39;要继续:是,是的,完成对象:240,完成对象:100%(240/240),DONE.Delta压缩使用最多8个线程按压对象:100%(201/201),完成。写对象:100%(227 / 227),35.62 kib | 1.62 MIB / s,DONE.TOTAL 227(DELTA 102),重新使用1(DELTA 0)REMOTE:解析DELTAS:100%(102/102),完成10个本地对象。 + 2B2E2785FC ... 59066EB5CC 59066EB5CCE861475EE77CF617FFFE6E6B9BFA7B4 - &GT; F1(强制更新)+ 8097F7ED0C ... 61DD498917 61DD498917148E6D753C5C86E7528C2F08C95843 - &GT; F2(强制更新)+ 69AF8F8C4C ... 3FEB5E177C 3FEB5E177CC09F26212B3FF43D8DE5FBFAD6FF6F - &GT; F3(强制更新)更新本地分支以便它们指向新堆栈。 +分支机构F1现在指向59066EB5CCE861475EE77CF61775E6B9BFA7B4 +分支机构现在指向61DD498917148C9528C2F08C9528C2F08C958C2F08C958C2F08C958C2F08C958C2F86C958C2F86C958C2F86C958C2F08C958C2F08C9528C2F08C958C2F08C958C2F08C9528C23 +分支机构F3目前指向3FEB5E177CC09F26212B3FF43D8DE5FBFAD6FF6FOFT!

在过去的几个月里,我使用了GH-Stack Autorebase数十个次数,它&#39;&#39;&#39;显着提升到生产力。我用它使用它来管理长度为5-12条PRS /分支的PRS堆栈,它&#39; S一直舒适。一旦我&#39; ve对堆栈中的分支进行了更改,将整个堆栈恢复到一个良好状态(单线线性的提交)实际上是几秒钟&#39;工作。

一般堆叠的PR工作流程可能非常有用,但它不是一个银弹。有顾虑&amp;权衡考虑:

力量推动几乎是一个要求,所以这不适合协同特征分支。

作者更初步的开销。它&#39;很容易(首先,无论如何)粘在单个公关中的整个变化。

最终,堆叠PRS只是另一个帮助管理基于Github的代码审查的工具。 我发现它适用于大型变化(4+ PRS),但在单个公关上的提交中,中小型变化通常会更好。 毫无疑问,如果情况呼叫它,毫无疑问,无疑需要在这里施加一些需要应用的酌情权,但是害怕使用一堆PRS! 工具形势不可否认。 我没有找到另一个工具,这是一个gh-stack所做的,以及那里有很多改善的空间。 我也希望在不久的将来的GitHub原生的东西。 🤞 至于立即下一个步骤,我想向GH-stack添加功能,以便为您创建GitHub上的初始PRS。 现在,您必须手动创建它们,并确保每个PR具有正确的合并到值集,这可能会出现一些容易出错的。 您可以在此帖子中找到所有可视化的代码,此处&#39;再次gh-stack repo。