如何将同一作者的所有提交连续分组?

时间:2019-04-01 20:08:21

标签: git

考虑在一个分支中有大量提交(超过2万个)的存储库,对于所有提交,我想将同一提交的一行中的每个提交都压缩。示例:

  • 提交09-作者BBBB
  • 提交08-作者BBBB
  • 提交07-作者AAAA
  • 提交06-作者AAAA
  • 提交05-作者AAAA
  • 提交04-作者CCCC
  • 提交03-作者CCCC
  • 提交02-作者AAAA
  • 提交01-作者BBBB

它最终像:

  • 提交05-作者BBBB
  • 提交04-作者AAAA
  • 提交03-作者CCCC
  • 提交02-作者AAAA
  • 提交01-作者BBBB

如何使用git编写脚本?

2 个答案:

答案 0 :(得分:2)

没有内置的方法可以做到这一点。

nologin effectively noted in a comment一样,如果实现了所需的一组提交,则您有一个 new 历史记录,与原始历史记录不兼容。如果可以,那么您可以执行一个过程-该过程不是内置的,但不是极其困难的,通过该过程您可以实现所需的一组提交。不过,首先要确定您想要什么。

您将提交描述为线性的,实际上它们可能是线性的,但可能不是。它们在某些区域将是线性的。但是提交形成有向无环图或DAG。此图是存储库中的历史记录。在线性的那些部分,这很简单:

... <-F <-G <-H   <-- master

在这里,分支名称 master标识或指向提交H。更准确地说,名称master存储提交H哈希ID 。同时,提交H存储H的父提交G的哈希ID,该哈希ID存储其父F的哈希ID,依此类推。通过从头开始并向后工作,git log会向您显示这些提交,并且历史。

但是,某些提交是 merge 提交。这样的提交有两个(或更多,但通常只有两个)父母。我们可以这样绘制它们:

       I--J
      /    \
...--H      M   <-- dev
      \    /
       K--L

此处分支名称dev指向提交M,但是M指向两者 J LJ指向IL指向KIK都指向提交,分支中的两个子分支从该提交形成,即提交H(名称master可能指向: H和更早版本的提交都在 master dev上。

如果提交ILM都是作者BBBB做出的,但是JK是作者AAAA做出的,您怎么办打算在这里做什么?如果您保留M(由BBBB保留),并且由于其他作者AAAA而保留J,则即使由BBBB保留,也必须保留L。但是,如果所有I-JK-LM都由AAAA来创建,则可以选择将它们全部折叠成一个父节点为H的提交:

...--H--M'  <-- dev

因此,确定您要保留的提交以及要执行的合并提交的工作是您的 工作。如果需要保留结构(在HM处的分叉合并),则必须保留合并提交。如果要消除分支合并结构,则必须丢弃合并提交,但是您必须弄清楚如何处理{{1} }和I(如果它们是其他作者的话)。

无论您决定什么,当最终完成时,获得所需结果的方法是:

  • 首先列出要保留的所有提交(按哈希ID)和/或希望丢弃的所有提交。 (两种方法都足够了,因为我们假设您在执行此操作时将保持所有提交的范围稳定-即,在计算这些列表时,不要在存储库中添加 new 提交并对存储库进行更改。)

  • 然后运行L。至少选择git filter-branch。您可能需要其他过滤器,具体取决于您打算在此处丢弃其他哪些历史数据。 (例如,每个提交都有一条日志消息:您是要 combine 记录所有日志消息,还是要丢弃那些要丢弃快照的提交?这些您在做什么:您正在制作一个虚构的历史记录,您可以根据自己的喜好组成尽可能多的内容,仅保留原始历史记录中的任何内容,然后丢弃其余内容。您的新存储库与旧存储库不兼容:即使更改历史记录中的任何位置也会使剩余的历史记录无效且不兼容,因此您可以随心所欲:它实际上是全有或全无! )

    在您的提交过滤器中-阅读the git filter-branch documentation了解详细信息-使用--commit-filter跳过您不需要不需要的提交,并使用skip_commit进行提交你想保留。要确定,只需查看git commit-tree "$@"是否在保留或丢弃列表中即可。

filter-branch命令将按正确的顺序一次枚举每个提交,这样您就可以在进行过程中将提交提交或从创建的历史记录中排除。在每次这样的提交上调用提交过滤器之后,它将把最后复制的提交的哈希ID写入哈希名称。现在,原始历史记录已经有效地消失了(但仍可以通过$GIT_COMMIT名称找到;该名称​​不会出现在任何新克隆中,可以在准备就绪时将其丢弃;再次,请参阅文档)。

答案 1 :(得分:0)

基于这个答案https://stackoverflow.com/a/46403701/926064,我得出了这个答案。它真的像一种魅力:

$ GIT_EDITOR='cat' \
GIT_SEQUENCE_EDITOR='todofile=$1; awk '"'"'{if ($1 != "#" && $1 != "") { author=$3; if (lastauthor != author) { lastauthor=author; printf "pick %s %s\n", $2, $3 } else { printf "squash %s %s\n", $2, $3 }}}'"'"' $todofile>$todofile.temp; mv -f $todofile.temp $todofile; cat $todofile' \
git -c "rebase.instructionFormat=%ae" rebase -i $(git log --oneline --reverse --pretty=format:%H  | head -n1)

注释:

第一个GIT_EDITOR确保将南瓜提交消息像默认的git南瓜消息一样保留,不要触碰它们-它们将是串联的消息。

第二个过滤器GIT_SEQUENCE_EDITOR将完成所需的工作,即过滤器,表示将根据作者压缩哪个提交。但这取决于作者的电子邮件,因此,当我们调用git rebase时,我们必须格式化“重新设置说明”,要求git将作者的电子邮件放在列表中。

第三个也是最后一个是git rebase,但是我们必须格式化“ rebase指令”,以便在处理(编辑)rebase指令列表时需要的所有信息都放在它们上。

为方便起见,下面是GIT_SEQUENCE_EDITOR变量中内联的格式化awk脚本:

{ 
    if ($1 != "#" && $1 != "") { 
        author=$3; 
        if (lastauthor != author) { 
            lastauthor=author; 
            printf "pick %s %s\n", $2, $3 
        } else {
            printf "squash %s %s\n", $2, $3
        }
    }
}