更快捷的方式`git rebase --preserve-merges`

问题描述 投票:2回答:2

我通过创建一系列特征分支来使用git,并在使用git merge --no-ff时将它们合并为master。这将创建空的合并提交,用于标识先前功能分支的起点和终点。

为了处理多个并发分支,甚至嵌套分支,我使用rebase。我永远不会合并,我总是在最新提交时修改我的分支,测试并最终在完成任务后与--no-ff合并。使用嵌套分支,我也这样做:多个分支按顺序合并到主分支上,主分支本身最终合并为主分支。

为了保持有关嵌套分支的合并信息,我经常使用git rebase --preserve-merges。这完全符合我的要求,我的工作流程也没有问题。

我对git的主要问题是git rebase --preserve-merges非常慢(有时每次提交大约需要2秒)。在阅读What exactly does git's "rebase --preserve-merges" do (and why?)后,我意识到git必须执行大量工作才能保留合并,因为git必须处理任意图形。

我想知道的是:由于我的工作流程几乎导致图形相当于线性历史,有没有办法以更快的方式执行git rebase --preserve-merge等价物,因为我保证历史只有空合并提交的“线性” ?我不介意使用脚本或奇怪的命令,只要最终结果是正确的。

     A-B-C
    /     \   
(1)--------D-- master
    \
     \---F-----I-- feature
      \ / \   /
       E   G-H

     A-B-C   E   G-H
    /     \ / \ /   \
(2)--------D---F-----I-feature
         master

TL;博士:如何将(1)变换为(2)知道基础历史是线性的,所以git rebase --preserve-merges不需要做太多的工作而且速度快吗?

git merge rebase
2个回答
1
投票

您可以通过将其从bash脚本重写为更聪明的东西(例如,在Python中运行git rev-list --parents并在开始复制操作之前收集所有父信息)来加快速度,但从根本上说它很难。正如其他链接中的答案一样,git rebase -p也不能完全保留合并,而是重新创建它们。在完全一般的情况下 - 我知道你忽略了更有限的特殊情况;我只提到完整性 - git rebase -p完全没有应用特殊选项或处理的合并(--no-commit合并手动修改以产生“邪恶合并”,或合并运行与-X rename-threshold-X ours或类似),因为特殊情况实际上,信息仅存储在结果树中。 rebase代码甚至没有查找它(这将花费更长的时间:它必须首先重现原始合并,以查看无选项合并是否会重新创建原始结果)。

除此之外,这些选项的速度(或速度不足)取决于存储库及其文件的大小,以及是使用Windows(极慢)还是Unix系统(更快)。我没有理由知道Windows运行脚本的速度应该非常慢,但显然是这样,因为Git人员不断重写C中的内容,以使它们在Windows上执行可接受,因为脚本速度太慢。

因此,如果你在Windows上这样做,加速它的一种方法是停止使用Windows。 :-)(您可以在rebase本身的持续时间内执行此操作,使用git pushgit fetch来协调Windows和Linux之间的任何操作。)


1
投票

我对git的主要问题是git rebase --preserve-merges非常慢

对于Git 2.20+(Q8 2018)来说,这可能不是那么慢,因为它包括重写C中的“rebase”机制。 没有更多的shell脚本。 (让我们明确一点:启动Git 2.22,2019年第二季度,old rebase script is no more

参见commit ac7f467commit c7b64aacommit 55071eaPratik Karki (prertik)(2018年8月6日)。 (由Junio C Hamano -- gitster --合并于commit 5ae5084,2018年11月2日)

rebase:开始将其作为内置实现

此提交模仿用于将difftool转换为内置的策略。 我们首先将shell脚本git-rebase.sh重命名为git-legacy-rebase.sh并引入一个简单执行shell脚本版本的builtin/rebase.c,除非配置设置rebase.useBuiltin设置为true

这背后的动机是在上述rebase.c中逐个重写shell脚本版本的所有功能,并能够通过配置rebase.useBuiltin方便地测试新功能。

在最初的difftool转换中,如果尝试运行遗留脚本版本的sane_execvp()以非负状态返回,则命令默默退出而不做任何成功的事情,但sane_execvp()不应该首先返回非负状态,所以我们使用die()注意到这种异常情况。

我们有意避免直接读取配置,以避免在我们需要回退到exec()shell脚本时弄乱GIT_*环境变量。


commit 62c2393commit d8d0a54Ævar Arnfjörð Bjarmason (avar)(2018年11月14日)。 (由Junio C Hamano -- gitster --合并于commit 4520c23,2018年11月18日)

The documentation现在声明:

rebase.useBuiltin:

如果false,设置为git rebase以使用遗留的shellcript实现。 默认是true,这意味着在C中使用它的内置重写。

C重写首先包含在Git版本2.20中。 如果在重写中发现任何错误,此选项可用于重新启用旧版本。 在将来的某个版本中将删除此选项和shellscript版本git-rebase

如果您发现某些理由将此选项设置为false而非一次性测试,则应将行为差异报告为git中的错误。


随着Git 2.21(2019年2月),“git rebase --merge”通过重复使用“git rebase -i”的内部机制重新实现。

参见commit 68aa495commit c91c944commit 7b76ac6commit 899b49ccommit 45339f7commit 5400677commit 72ee673commit c913c59Elijah Newren (newren)(2018年12月11日)。 (由Junio C Hamano -- gitster --合并于commit 8fe9c3f,2019年2月7日)

rebase:通过互动机器实施--merge

作为使rebase具有更一致行为的持续努力的一部分,通过在后者之上重新实现它,修改合并后端以表现得像交互式后端。

交互式rebase是通过cherry-pick而不是merge-recursive内置来实现的,但是cherry-pick还默认调用递归合并机制,并且可以接受特殊的合并策略和/或特殊策略选项。 因此,实际上不需要同时拥有git-rebase--mergegit-rebase--interactive。 删除git-rebase--merge.sh,而不是在builtin/rebase.c中实现它。

rebase:定义线性化排序并强制执行

请参阅commit c91c944的表现。

此外,仍然Git 2.21(2019年2月):“git rebase --merge”通过重复使用“git rebase -i”使用的内部机制重新实现。

commit 29d03f8Elijah Newren (newren)(2019年2月14日)。 (由Junio C Hamano -- gitster --合并于commit 6f07c7b,2019年2月14日)

rebase:通过交互式机器实现--merge

作为使rebase具有更一致行为的持续努力的一部分,通过在后者之上重新实现它,修改合并后端以表现得像交互式后端。

交互式rebase是通过cherry-pick而不是merge-recursive内置来实现的,但是cherry-pick还默认调用递归合并机制,并且可以接受特殊的合并策略和/或特殊策略选项。 因此,实际上不需要同时拥有git-rebase--mergegit-rebase--interactive。 删除git-rebase--merge.sh,而不是在builtin/rebase.c中实现它。

这会导致一些故意但很小的用户可见更改:

  • 修改进度输出(例如,参见t3406和t3420)
  • 现在修复了一些已知的测试失败(参见t3421)
  • 在rebase期间的bash-prompt - 现在是REBASE-i而不是REBASE-m。 原因:提示是后端使用的反映;这允许用户使用适当的后端信息向git邮件列表报告问题,并允许高级用户知道在哪里搜索相关的控制文件。 (见t9903)

由于“git rebase --preserve-merge”通过重复使用用于“git rebase -i”的内部机制而重新实现,因此这个Git 2.22(2019年第二季度)补丁很有意义:

参见commit 460bc3ccommit 297b1e1commit 0ea0847commit 73fdc53commit 3389853commit 7d3488ecommit c44c246commit 0609b74commit 6023c92commit 28dc09dcommit 146839ccommit fc4a673(2019年4月17日)和Phillip Wood (phillipwood)(2019年3月19日)。 (Junio C Hamano -- gitster --于2019年5月13日在commit 7ba06bc合并)

rebase -i:没有分叉rebase --interactive

当内置rebase启动交互式rebase时,它会解析选项,然后重新打包它们并分叉rebase--interactive。 将cmd_rebase__interactive()中的选项解析与业务逻辑分开,以允许通过直接调用rebase__interactive来运行交互式rebase,而无需分离run_rebase_interactive()

无需分叉即可启动交互式rebase,可以轻松调试顺控程序,而无需担心附加到子进程。 Ævar还报道了一些rebase perf tests are 30% faster

cmd_rebase__interactive()git-legacy-rebase.sh退役时,这个补丁也很容易在将来删除git-rebase--preserve-merges.sh


如答案开头所述:

© www.soinside.com 2019 - 2024. All rights reserved.