我通过创建一系列功能分支来使用git,并在使用git merge --no-ff
完成后将它们合并到主控。这将创建空的合并提交,用于标识先前功能分支的起点和终点。
为了处理多个并发分支,甚至是嵌套分支,我使用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
等价物,因为我保证&#34 ;线性"只有空合并提交的历史记录?我不介意使用脚本或奇怪的命令,只要最终结果是正确的。
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
不需要做多少工作并且快速完成?
答案 0 :(得分:1)
你可能能够通过将其从bash脚本重写为更聪明的东西来加快速度(例如,在Python中运行git rev-list --parents
并收集所有在开始复制操作之前的父信息),但从根本上说它很难。正如其他链接注释中的答案一样,git rebase -p
也不会完全保留合并,而不是重新创建它们。在完全一般的情况下 - 我知道你忽略了更有限的特殊情况;我只提到这一点是为了完整性 - git rebase -p
完全失败了已经应用了特殊选项或处理的合并(--no-commit
与手动修改合并以产生“邪恶合并”,或者合并与{{1}合并或者-X rename-threshold
或类似的),因为特殊情况信息实际上只存储在结果树中。 rebase代码甚至没有查找它(这将花费更长的时间:它必须首先重现原始合并,以查看无选项合并是否会重新创建原始结果。)
除此之外,这些选项的速度(或速度不足)取决于存储库及其文件的大小,以及是使用Windows(非常慢)还是使用Unixish系统(更快)。我没有理由知道Windows运行脚本的速度应该非常慢,但显然是这样,因为Git人员不断重写C中的内容,使其在Windows上表现可接受,因为脚本速度太慢。
因此,如果你在Windows上这样做,加速它的一种方法是停止使用Windows。 :-)(您可以在rebase本身的持续时间内执行此操作,使用-X ours
和git push
来协调Windows和Linux或其他任何内容。)
答案 1 :(得分:1)
我对git的主要问题是git rebase --preserve-merges非常慢
考虑到它包括用C语言重写“ rebase”机制,在Git 2.20+(2018年第四季度)中可能不会那么慢。
没有更多的shell脚本。
(请清楚:从Git 2.22开始,2019年第二季度,old rebase script is no more)
请参见commit ac7f467的commit c7b64aa,commit 55071ea,Pratik Karki (prertik
)(2018年8月6日)。
(由Junio C Hamano -- gitster
--在commit 5ae5084中合并,2018年11月2日)
变基:开始将其实现为内置
此提交模仿了用于将
difftool
转换为内置变量的策略。
我们首先将Shell脚本git-rebase.sh
重命名为git-legacy-rebase.sh
,然后介绍一个builtin/rebase.c
,它简单地执行Shell脚本版本, 除非配置设置rebase.useBuiltin
设置为true
。其背后的动机是重写 上述
rebase.c
中的shell脚本版本,一一并 通过配置可以方便地测试新功能rebase.useBuiltin
。在原始的
difftool
转换中,如果sane_execvp()
试图 运行以非负状态返回的旧脚本版本, 命令悄无声息地退出,没有做任何成功,但是sane_execvp()
的第一个不应返回非负状态 位置,因此我们使用die()
来注意到这种异常情况。我们有意避免直接阅读配置以避免 当我们需要回退到
GIT_*
环境变量时 exec()编写Shell脚本。
请参见commit 62c2393的commit 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
以使用旧版shellscript实现。
默认为true
,这意味着使用C中的内置重写。C重写最初包含在Git 2.20版本中。
此选项提供了逃生功能,可以在任何情况下重新启用旧版本 在重写中发现错误。
在将来的某些发行版中,将删除此选项和shellscript版本git-rebase
。如果您发现除一次性测试之外将此选项设置为
false
的其他原因,则应将行为差异报告为git中的错误。
在Git 2.21(2019年2月)中,通过重新使用用于“ git rebase --merge
”的内部机制,重新实现了“ git rebase -i
”。
请参见commit 68aa495,commit c91c944,commit 7b76ac6,commit 899b49c,commit 45339f7,commit 5400677,commit 72ee673,{{3} }(2018年12月11日),由commit c913c59。
(由Elijah Newren (newren
)在Junio C Hamano -- gitster
--中合并,2019年2月7日)
rebase
:通过互动机制实施--merge
作为使基础具有更统一行为的不断努力的一部分,通过在后端之上重新实现,将合并后端修改为类似于交互式后端。
交互式rebase是根据Cherry-pick而非内置的递归合并实现的,但是Cherry-pick默认情况下也调用递归合并机制,并且可以接受特殊的合并策略和/或特殊的策略选项。 > 因此,实际上并不需要同时使用
git-rebase--merge
和git-rebase--interactive
了。
删除git-rebase--merge.sh
,而在builtin/rebase.c
中实施。
rebase
:定义线性化顺序并强制执行
有关效果,请参见commit 8fe9c3f。
此外,仍然是Git 2.21(2019年2月):“ git rebase --merge
”已通过重新使用用于“ git rebase -i
”的内部机制而重新实现。
请参见commit c91c944的commit 29d03f8(2019年2月14日)。
(由Elijah Newren (newren
)在Junio C Hamano -- gitster
--中合并,2019年2月14日)
rebase:通过交互式机器实现--merge
作为使基础具有更统一行为的不断努力的一部分,通过在后端之上重新实现,将合并后端修改为类似于交互式后端。
交互式重载是根据Cherry-pick而非内置递归合并实现的,但是Cherry-pick也调用了 递归合并机制默认情况下,可以接受特殊的合并策略和/或特殊的策略选项。
因此,实际上并不需要同时使用git-rebase--merge
和git-rebase--interactive
了。
删除git-rebase--merge.sh
,而在builtin/rebase.c
中实施。这会导致一些故意但很小的用户可见更改:
- 修改进度输出(例如,请参阅t3406和t3420)
- 现在解决了一些已知的测试失败(请参见t3421)
重新设置基元时-重新合并时的- bash提示现在为
REBASE-i
,而不是REBASE-m
。 原因:提示反映了正在使用的后端;这使用户可以使用适当的后端信息向git邮件列表报告问题,并允许高级用户执行以下操作: 知道在哪里搜索相关的控制文件。 (请参阅t9903)
由于通过重新使用用于“ git rebase --preserve-merge
”的内部机制而重新实现了“ git rebase -i
”,因此此Git 2.22(2019年第二季度)补丁很受关注:
请参见commit 6f07c7b,commit 460bc3c,commit 297b1e1,commit 0ea0847,commit 73fdc53,commit 3389853,commit 7d3488e,{{3} },commit c44c246,commit 0609b74,commit 6023c92(2019年4月17日)和commit 28dc09d(2019年3月19日)之前为commit 146839c。
(由commit fc4a673在Phillip Wood (phillipwood
)中合并,2019年5月13日)
rebase -i
:运行时无需分叉rebase --interactive
当内置rebase启动交互式rebase时,它将解析选项,然后将其重新打包并分叉
rebase--interactive
。
将cmd_rebase__interactive()
中的选项解析与业务逻辑分开,以允许在不通过直接调用rebase__interactive
来分叉run_rebase_interactive()
的情况下运行交互式基础。在不进行分叉的情况下启动交互式rebase使其易于调试 音序器,无需担心会附着在孩子身上 流程。
Ævar还报告了一些Junio C Hamano --gitster
--。此补丁程序还可以轻松删除以下位置的
cmd_rebase__interactive()
git-legacy-rebase.sh
和git-rebase--preserve-merges.sh
退休的未来。