真的压扁了git merge

时间:2012-09-04 19:47:14

标签: git merge rebase git-rewrite-history

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

假设有一个分支A,其中包含6月1日和8月1日的提交,而分支B则提交7月1日(更新以恢复下面描述的用例:分支是完全独立的,没有共同祖先,例如来自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来实现所需的安排,但这对于大型历史记录来说并不实用,所以我一直在寻找自动命令/脚本。

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

4 个答案:

答案 0 :(得分:11)

经过一番思考后,我想出了如何做How do I run git rebase --interactive in non-interactive manner?,这也为这个问题提供了完全脚本化的解决方案。

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>

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

答案 1 :(得分:2)

将单独的开发分开,直到它们合并为止,会出现什么问题?如果它们是分开的,那么它们是分开的。

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

答案 2 :(得分:1)

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

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

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

因此,如果提交的作者日期可见,则可以有效地使用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. 这似乎是重新提交提交的正确位置/方式:git show --format='%ai' -s " $2 "
  7. 切换到原始控制台并在编辑器中重新加载git-rebase-todo文件,然后退出编辑器。
  8. 瞧!实际上,如果sort -k3 git-rebase-todo >git-rebase-todo.new; mv git-rebase-todo.new git-rebase-todo可以在“非交互式”模式下工作,这可以完全编写脚本,我提交了How do I run git rebase --interactive in non-interactive manner?

答案 3 :(得分:0)

实际上,如果我理解正确,您可以使用git-stitch-repo轻松实现这一目标。