gpt4 book ai didi

perl - 使用git高效地重写(rebase -i)许多历史记录

转载 作者:太空狗 更新时间:2023-10-29 13:14:49 25 4
gpt4 key购买 nike

我有一个git存储库,在最新版本中有3500个提交和30,000个不同的文件。它代表了大约3年多人工作的成果,并且我们已经批准将其全部开源。我正在努力发布整个历史记录,而不仅仅是最新版本。为此,我对“时光倒流”感兴趣,并在创建文件时在文件顶部插入许可证 header 。我实际上已经在进行这项工作,但是用完整个ramdisk大约需要3天的时间,但仍然需要一点点人工干预。我知道它可以快很多,但是我的git-fu不能完全胜任该任务。

问题:我怎样才能更快地完成同一件事?

我目前正在做什么(已在脚本中自动执行,但请耐心等待...):

  • 标识将新文件添加到存储库中的所有提交(fwow,其中只有约500个):
    git whatchanged --diff-filter=A --format=oneline
  • 将环境变量GIT_EDITOR定义为我自己的脚本,该脚本在文件的第一行仅一次将pick替换为edit(您很快就会看到原因)。这是操作的核心:
    perl -pi -e 's/pick/edit/ if $. == 1' $1
  • 对于上述git whatchanged输出中的每个提交,请在添加文件的提交之前开始调用交互式rebase:
    git rebase -i decafbad001badc0da0000~1

  • 我的自定义GIT_EDITOR(perl单行代码)将 pick更改为 edit,然后我们将其放置在shell中以对新文件进行更改。另一个简单的 header-inserter脚本在我要插入的 header 中查找已知的唯一模式(仅适用于已知文件类型(对我来说是*。[chS]))。如果不存在,则将其插入,然后将 git add作为文件。这种幼稚的技术不知道在当前提交期间实际添加了哪些文件,但是最终却做对了并且是幂等的(可以安全地对同一个文件运行多次),而且整个过程也不会成为瓶颈。

    在这一点上,我们很高兴我们已经更新了当前的提交,并调用了:
        git commit --amend
    git rebase --continue
    rebase --continue是昂贵的部分。由于我们为 git rebase -i输出中的每个修订版本调用一次 whatchanged,因此需要进行大量的基础调整。运行该脚本的几乎所有时间都花在了观察“Rebasing(2345/2733)”计数器增量上。

    它也不只是速度慢。必须定期解决冲突。至少在以下情况下可能会发生这种情况(但可能更多):(1)当"new"文件实际上是现有文件的副本,并且对其第一行进行了一些更改(例如 #include语句)。这是真正的冲突,但在大多数情况下可以自动解决(是的,请使用能解决该问题的脚本)。 (2)删除文件时。只需确认我们要使用 git rm删除它,就可以轻松解决此问题。 (3)在某些地方 diff似乎表现不佳,例如,更改仅是添加了一个空白行。其他更合理的冲突需要人工干预,但总的来说,这不是最大的瓶颈。最大的瓶颈绝对是坐在那里盯着“重新装订(xxxx/yyyy)”。

    现在,从重新提交到较旧的提交(即从 git whatchanged的输出的顶部开始)开始各个rebase。这意味着第一次重新设置会影响昨天的提交,并且最终我们将对3年前的提交进行重新设置。从“较新的”到“较旧的”似乎是违反直觉的,但是到目前为止,除非我们在调用rebase时将一个以上 pick更改为 edit,否则我并不认为这很重要。我之所以这样做是因为冲突确实会到来,而且我不想处理试图一劳永逸地恢复一切的冲突浪潮。也许有人知道避免这种情况的方法?我还没办法提出。

    我开始研究git对象 1的内部工作原理!似乎应该有一种更有效的方法来遍历对象图并进行我想进行的更改。

    请注意,此存储库来自SVN存储库,在该存储库中,我们实际上没有使用任何标签或分支(我已经 git filter-branch删除了它们),因此我们拥有直线历史记录的便利。没有git分支或 merge 。

    我确定我已经遗漏了一些关键信息,但是这篇文章似乎已经太长了。我将尽力提供所需的更多信息。最后,我可能只需要发布各种脚本即可。我的目标是弄清楚如何在git存储库中重写历史记录。不要争论其他可行的许可和代码发布方法。

    谢谢!

    2012年6月17日更新: Blog post及其所有细节。

    最佳答案

    使用

    git filter-branch -f --tree-filter '[[ -f README ]] && echo "---FOOTER---" >> README' HEAD

    本质上将在 README文件中添加页脚行,并且历史记录看起来好像自文件创建以来就存在过。我不确定它是否对您足够有效,但这是正确的方法。

    制作一个自定义脚本,您可能会获得良好的项目历史记录,做太多的“魔术”( rebase ,Perl,脚本编辑器等)可能最终会以意想不到的方式丢失或更改项目历史记录。

    jon(OP)使用此基本模式以显着的简化和加速来实现目标。
    git filter-branch -d /dev/shm/git --tree-filter \
    'perl /path/to/find-add-license.pl' --prune-empty HEAD

    一些对性能至关重要的观察。
  • 使用指向ramdisk目录的-d <directory>参数(例如/dev/shm/foo)将显着提高速度。
  • 使用脚本的内置语言功能,在单个脚本中进行所有更改,使用小型实用程序(例如find)完成的派生操作将使该过程减慢很多倍。避免这种情况:
    git filter-branch -d /dev/shm/git --tree-filter \
    'find . -name "*.[chS]" -exec perl /path/to/just-add-license.pl \{\} \;' \
    --prune-empty HEAD

  • 这是OP使用的perl脚本的净化版本:
    #!/usr/bin/perl -w
    use File::Slurp;
    use File::Find;

    my @dirs = qw(aDir anotherDir nested/DIR);
    my $header = "Please put me at the top of each file.";

    foreach my $dir(@dirs) {
    if (-d $dir) {
    find(\&Wanted, $dir);
    }
    }

    sub Wanted {
    /\.c$|\.h$|\.S$/ or return; # *.[chS]
    my $file = $_;
    my $contents = read_file($file);
    $contents =~ s/\r\n?/\n/g; # convert DOS or old-Mac line endings to Unix
    unless($contents =~ /Please put me at the top of each file\./) {
    write_file( $file, {atomic => 1}, $header, $contents );
    }
    }

    关于perl - 使用git高效地重写(rebase -i)许多历史记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10917835/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com