我通过创建一系列特征分支来使用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
不需要做太多的工作而且速度快吗?
您可以通过将其从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 push
和git fetch
来协调Windows和Linux之间的任何操作。)
我对git的主要问题是git rebase --preserve-merges非常慢
对于Git 2.20+(Q8 2018)来说,这可能不是那么慢,因为它包括重写C中的“rebase”机制。 没有更多的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日)
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 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
以使用遗留的shellcript实现。 默认是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,commit c913c59,Elijah 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--merge
和git-rebase--interactive
。 删除git-rebase--merge.sh
,而不是在builtin/rebase.c
中实现它。
rebase
:定义线性化排序并强制执行
请参阅commit c91c944的表现。
此外,仍然Git 2.21(2019年2月):“git rebase --merge
”通过重复使用“git rebase -i
”使用的内部机制重新实现。
见commit 29d03f8的Elijah 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--merge
和git-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 460bc3c,commit 297b1e1,commit 0ea0847,commit 73fdc53,commit 3389853,commit 7d3488e,commit c44c246,commit 0609b74,commit 6023c92,commit 28dc09d,commit 146839c,commit 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
。
如答案开头所述:
git rebase
剧本。
见commit d03ebd4的Ævar Arnfjörð Bjarmason (avar
)(2019年3月18日)。
(由Junio C Hamano -- gitster
--合并于commit 4f3036c,2019年4月16日)git rebase
唯一剩下的脚本部分是the --preserve-merges
backend。
参见commit 082ef75的commit c3c003e,commit d4fe60e,commit b2b9a23,commit 311c00a,Johannes Schindelin (dscho
)(2019年5月14日)。
(由Junio C Hamano -- gitster
--合并于commit ed7f8ac,2019年6月13日)