12

StackOverflow 上几乎没有关于“扁平化合并”的问题,答案通常是“git rebase”。这些答案虽然错过了一个关键点——提交的顺序。

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

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 代表同一项目的不同部分,恰好位于不同的 repos 中,并且时间已经通过将它们合并在一起来纠正这一点,那么自然希望线性历史按照实际的发展顺序展开。

4

4 回答 4

14

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

1.将不同仓库的2个分支合并到一个仓库中(git remote add + git fetch)

2.将一个分支重新设置在另一个分支之上(以非交互方式)(顺序很重要,请考虑您希望将哪个分支的第一次提交作为合并分支的第一次提交)。

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>

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

于 2012-09-12T19:47:34.217 回答
2

[有关完全自动化的解决方案,请参阅我的另一个答案。我将把它作为最终解决方案的路径示例,以防有人面临类似的不太明显的解决任务。]

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

嗯,首先,最终的解决方案git filter-branch --parent-filter看起来正是我们所需要的。除了我的 git-fu 不允许我用它编写 1-、2- 或 3-liner 之外,编写独立脚本来解析所有修订的方法并不酷,而且比 rebase -i 更费力。

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

第二个想法是:为什么要麻烦,最好修补 rebase -i 使用的 rebase 提交列表。因此,该过程将是:

  1. 像往常一样,将来自不同仓库的分支 A 和 B 合并到一个仓库中。
  2. 将一个分支重新设置为另一个分支(非交互式)。考虑哪个分支应该基于哪个分支,具有初始提交权(这不能轻易地用 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?为了那个原因。

于 2012-09-12T18:52:57.750 回答
0

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

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

于 2012-09-05T05:30:26.767 回答
0

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

于 2013-10-22T16:09:37.317 回答