问题 真的压扁了git merge


关于StackOverflow上的“flattening merge”的问题很少,答案通常是“git rebase”。这些答案虽然错过了一个关键点 - 提交顺序。

假设有一个分支A,提交6月1日和8月1日,分支B提交7月1日(UPDATE 恢复下面描述的用例:分支完全独立,没有共同的祖先,例如来自2个不同的存储库)。将B合并为A时,将有以下历史记录(每个git日志):

Merged branch 'B'
Aug 1
Jul 1
Jun 1

现在,我正在寻找的是获得相同结果的方法,但是没有合并提交(因此在该顺序中具有基础线性历史,是的,这意味着重新提交提交的父级)。 git rebase在这里没有用,就像它一样,你会得到以下历史:

Jul 1
Aug 1
Jun 1

要么

Aug 1
Jun 1
Jul 1

换句话说,git rebase总是将一个分支堆叠在另一个分支之上,而我正在寻找解决方案,它将按作者的提交日期排序提交。

显然,对于简单的情况,可以通过使用git rebase -i手动后处理git rebase来实现所需的安排,但这对大型历史来说并不实用,所以我一直在寻找自动命令/脚本。

用例?如果A和B代表同一个项目的不同部分恰好在不同的回购中,并且时间已经通过将它们合并在一起来纠正,那么很自然地希望线性历史以实际的开发顺序展开。


2988
2017-09-04 19:47


起源

听起来你正试图将线性开发路径固定在非线性版本控制系统上。想要按时间顺序排列所有提交似乎是“自然的”,但这将是一个虚假的历史,因为你的团队实际上并非当时彼此合作。最重要的是最终状态,整合两个团队的努力,而不是在事实不完整的合并步骤之后。 - Peter Bratton
@ jordan002:这个问题指出了“团队”在两个分支上“合作”作为起始条件的事实。至于“至关重要”,这个问题恰恰是关于什么,而不是关于发展方法的意见。 - pfalcon
@pfalcon:实际上,它并没有在你问题的任何地方说出来。此外,您在这里尝试解决的实际问题是什么?我们了解你 想 去做;但是你要解决的问题是什么? - Infiltrator
FWIW, git rebase 处理合并合理的好。即提交顺序保持不变。除非你期望它们按时间顺序排列,因为根据 非线性 历史 rebase 必须处理提交 不 以该顺序。 - fork0
@Infiltrator:我在下面的答案评论中给出了实际的例子。否则,我确实试图将问题表述为抽象的git one,从而可以重复使用,而不是“一时冲动”。 - pfalcon


答案:


经过一番思考后,我想出了怎么做 如何以非交互方式运行git rebase --interactive? ,它还为这个问题提供了完全脚本化的解决方案。

1。 将来自不同存储库的2个分支放入一个存储库(git remote add + git fetch)

2。 Rebase(非交互式)一个分支在另一个分支之上(顺序很重要,考虑首先提交您想要拥有的分支作为第一次提交的合并分支)。

3。 准备以下脚本(rebase-reoder-by-date):

#!/bin/sh
awk '
/^pick/ {
            printf "%s %s ", $1, $2;
            system("echo -n `git show --format='%ai' -s " $2 "`");
            for (i = 3; i <= NF; i++) printf " %s", $i; printf "\n";
        }
' $1 | sort -k3 > $1.tmp
mv $1.tmp $1

4。 跑: GIT_SEQUENCE_EDITOR=./rebase-reoder-by-date git rebase -i <initial commit>

免责声明:所有这些操作都应该发生在原始存储库的副本上,审查/验证/测试组合分支,以确保它符合您的预期并包含您的期望,保持备份方便。


10
2017-09-12 19:47





将合并后的单独开发留在合并之前有什么问题?如果他们是分开的,那么他们是分开的。

有许多方法可以按时间顺序查看历史记录,而不会在您尝试时破坏历史记录。你有没有尝试过 git log --pretty --date-order


2
2017-09-05 05:30



+1我甚至不知道这存在。 - Peter Bratton
好吧,如果问题中的一般描述不够,这里有更具体的例子:项目的客户端和服务器部分最初创建为2个单独的git repos。但是他们的开发并行进行,比如功能被添加到服务器,相关代码添加到客户端等等。因此,没有“单独的开发线”,只有repos被分开。稍后,很明显客户端和服务器都是 一 项目,他们一直在努力,剩下的就是将它们合并成1个代表他们的仓库 共同 发展路线。 - pfalcon
您可以将上面的“服务器”和“客户端”替换为“主应用”和“库”,或者替换为“语言A中的实现”和“语言B中的实现”,或者使用“接口”和“实现”。很明显,这样的用例或多或少是通用的,这就是我如何制定这个问题,想要找到社区可重用的解决方案,而不是仅仅抓住我暂时的痒。是的,它更像是智力挑战(“git可以做很多事,可以做到这一点”)。所以,是的,我想找到一个解决方案,如果devel从一开始就“正确”完成,而不仅仅是一个解决方案,那将使repo看起来像是 - pfalcon
为了记录,我遇到了这个问题,因为我正在尝试合并两个从Subversion克隆的Git存储库。 Subversion / Git转换过程不是很擅长选择单个子目录,因此我们创建了单独的Git存储库。 - Huw Walters


[请参阅我的另一个答案,了解完全自动化我将此作为导致最终解决方案的路径的一个例子,以防有人将面临类似的不那么明显的解决任务。

好吧,这不是问题的真正答案(完全脚本化,自动化的解决方案),但思考和示例如何(基于交互式rebase)处理可以自动化。

嗯,首先,为了最终的解决方案 git filter-branch --parent-filter看起来确实需要什么。除了我的git-fu不允许我用它编写1代,2代或3行代码,并且编写独立脚本以解析所有修订版的方法并不比rebase -i更酷,更省力。

因此,如果提交的作者日期可见,则可以有效地使用rebase -i。我的第一个想法是暂时修改提交消息以使用作者日期开始 git filter-branch --msg-filter,运行rebase -i,然后重新发送消息。

第二个想法是:为什么要麻烦,更好地修改rebase -i使用的rebase提交列表。那么,过程将是:

  1. 像往常一样,将不同回购的分支A和B带入一个回购。
  2. Rebase(非交互式)一个分支在另一个分支上。考虑应该对哪个分支进行重新分配,以使初始提交权利(不能用rebase轻松重写)。
  3. 开始 git rebase -i
  4. 在另一个控制台中,转到$ REPO / .git / rebase-merge /
  5. 跑: awk '/^pick/ {printf "%s %s ", $1, $2; system("echo -n git show --format='%ai' -s " $2 ""); for (i = 3; i <= NF; i++) printf " %s", $i; printf "\n"; }' git-rebase-todo > git-rebase-todo.new; mv git-rebase-todo.new git-rebase-todo
  6. 这似乎是重新排序提交的正确位置/方式: sort -k3 git-rebase-todo >git-rebase-todo.new; mv git-rebase-todo.new git-rebase-todo 
  7. 切换到原始控制台并在编辑器中重新加载git-rebase-todo文件,然后退出编辑器。

瞧!实际上,这可以完全编写脚本 git rebase -i 我提交了,可以在“非交互”模式下工作 如何以非交互方式运行git rebase --interactive? 为了那个原因。


1
2017-09-12 18:52





实际上,如果我理解正确,你可以轻松实现这一目标 混帐十字绣回购


0
2017-10-22 16:09



有趣的工具不幸的是,结果是不同的分支,而不是一个。此工具的结果是此问题的起点 - Daniel Alder