使用 git 有效地重写(rebase -i)大量历史记录

2023-11-25

我有一个 git 存储库,最新版本中有大约 3500 个提交和 30,000 个不同的文件。它代表了多人大约 3 年的工作成果,我们已获得将其全部开源的许可。我正在努力发布整个历史记录,而不仅仅是最新版本。为此,我感兴趣的是“回到过去”并在创建文件时在文件顶部插入许可证标头。我实际上已经做到了这一点,但完全用虚拟磁盘运行需要大约 3 天的时间,并且仍然需要一点手动干预。我知道它可以快得多,但我的 git-fu 还不能完全胜任这项任务。

问题是:我怎样才能更快地完成同样的事情?

我目前所做的(在脚本中自动执行,但请耐心等待......):

  1. 识别将新文件添加到存储库的所有提交(其中只有不到 500 个,fwiw):

    git whatchanged --diff-filter=A --format=oneline
    
  2. 将环境变量 GIT_EDITOR 定义为我自己的脚本来替换pick with edit仅在文件的第一行出现一次(您很快就会明白原因)。这是操作的核心:

    perl -pi -e 's/pick/edit/ if $. == 1' $1
    
  3. 对于输出的每个提交git whatchanged上面,在添加文件的提交之前调用交互式变基:

    git rebase -i decafbad001badc0da0000~1
    

我的自定义 GIT_EDITOR (perl 一行)发生了变化pick to edit我们将进入 shell 对新文件进行更改。另一个简单的header-inserter脚本在我尝试插入的标头中查找已知的唯一模式(仅在已知的文件类型中(对我来说是*.[chS]))。如果不存在,则会将其插入,然后git add是文件。这种天真的技术不知道在当前提交期间实际添加了哪些文件,但它最终做了正确的事情并且是幂等的(可以安全地针对同一文件多次运行),并且无论如何都不是整个过程的瓶颈。

此时,我们很高兴我们已经更新了当前提交,并调用:

    git commit --amend
    git rebase --continue

The rebase --continue是昂贵的部分。由于我们调用了git rebase -i输出中的每个修订一次whatchanged,这是很多变基。该脚本运行的几乎所有时间都花在观察“Rebasing (2345/2733)”计数器增量上。

它也不仅仅是慢。必须定期解决一些冲突。至少在这些情况下(但可能更多)可能会发生这种情况:(1)当“新”文件实际上是现有文件的副本时,对其第一行进行了一些更改(例如,#include声明)。这是一个真正的冲突,但在大多数情况下可以自动解决(是的,有一个处理该问题的脚本)。 (2) 当文件被删除时。只需确认我们想要删除它即可轻松解决git rm。 (3)有些地方看起来像diff只是表现得很糟糕,例如,更改只是添加了一个空行。其他更合理的冲突需要人工干预,但总的来说它们并不是最大的瓶颈。最大的瓶颈绝对是坐在那里盯着“Rebasing (xxxx/yyyy)”。

现在,各个变基是从较新的提交到较旧的提交启动的,即从输出的顶部开始git whatchanged。这意味着第一次变基会影响昨天的提交,最终我们将变基 3 年前的提交。从“新”到“旧”似乎违反直觉,但到目前为止,我不相信这很重要,除非我们改变多个pick to an edit当调用变基时。我害怕这样做,因为冲突确实会到来,而且我不想因试图一次性重新建立一切而应对冲突的浪潮。也许有人知道避免这种情况的方法?我一直想不出一个。

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

请注意,这个存储库来自 SVN 存储库,我们实际上没有使用标签或分支(我已经git filter-branch编辑掉它们),所以我们确实有一个直线历史的便利。没有 git 分支或合并。

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

Thanks!

2012年6月17日更新:博客文章以及所有血淋淋的细节。


Using

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

一些性能关键的观察结果。

  • 使用-d <directory>指向 ramdisk 目录的参数(例如/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 );
  }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 git 有效地重写(rebase -i)大量历史记录 的相关文章

  • 在 Perl 中如何打印到变量而不是文件?

    如何使用 Perl 打印到变量 我已经在一个程序上工作了一段时间 它记录了它的迭代的以非常详细的方式取得进展 print loghandle some message 但是 我还想有选择地将一些消息打印到不同的文件中 当然 我可以在代码中添
  • Git 分支之间未跟踪的文件

    我一直在这里寻找答案 看来我可能只是对 git 分支应该如何工作做出了错误的假设 我有我的master分支 我创建了一个名为的功能分支profiles我正在对个人资料进行一些特定的工作 在处理配置文件时 我更改了 5 或 6 个文件 并添加
  • Java 相当于 Perl 的 s/// 运算符?

    我有一些代码正在从 Perl 转换为 Java 它大量使用了正则表达式 包括s 操作员 我已经使用 Perl 很长时间了 但仍然习惯 Java 的做事方式 特别是 字符串似乎更难使用 有谁知道或有一个完全实现的Java函数s 这样它就可以处
  • 将远程更改合并到非当前分支的分支中

    我有多个分支 我想将远程更改合并到一个分支中不是我当前的分支 例如 git merge remote branch some other branch 仅当本地分支可以快速转发到远程头时 这才是可行的 在任何分支中 要从源获取远程分支并更新
  • 更改 Windows 安装的 Git Bash 中 ~ 目录的位置

    我什至不确定我问的是正确的问题 让我解释一下我的情况 这是关于 Windows 7 上的 Git 我的公司在网络驱动器上设置 Windows 用户目录 而不是在本地硬盘驱动器上 用于备份和超出本问题范围的其他目的 我无法改变这项政策 然而
  • 设置单独的遥控器以仅推送和拉动子文件夹

    假设我有两个存储库 repo A and repo B 其中包含一个文件夹 其中的代码与存储库 A 中的代码类似 这实际上如何发生并不重要 但是好吧 让我们假设我刚刚从 A 复制了内容 现在我想要以下内容 就像我在 repo A 中习惯的那
  • Git-svn:批量删除孤立的远程分支

    我正在处理的 SVN 项目 通过 git svn 有经常创建的分支 然后与主干重新集成 然后删除 现在项目大约有10个分支没有被删除 但是在git中 gitbranch r显示大约有50个 我可以一次删除这些 检查它们是否仍然存在于 svn
  • 将代码从没有权限的存储库推送到私有存储库?

    我有一个来自外部存储库 我们称之为 ExRepo 的文件夹 我没有任何推送权限 我有一个名为 MyOwnRepo 的个人存储库 如何获取 ExRepo 并将其合并到 MyOwnRepo 中 我已经尝试过这个 C Users
  • 以编程方式将工作项关联到拉取请求

    我可以得到 https www visualstudio com en us docs integrate api git pull requests get a pull request https www visualstudio co
  • Git 用户配置文件 - 自定义忽略

    我们有一个由 5 名开发人员组成的团队致力于硬件项目 我们有一个 user config h 文件 其中包含每个用户的特定环境和偏好的 defines 例如他们使用的硬件版本 是否应该打开声音等 目前 该文件位于我们的 gitignore
  • 在 Perl 中解析 RSS/Atom 的最佳库是什么? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我注意到XML RSS 解析器 http search cpan org dist XML RSS P
  • Eclipse git checkout(又名恢复)

    是否可以做相当于git checkout在 Eclipse 中使用 Egit 插件 我有一个已修改的文件 我想放弃更改并将文件恢复到源存储库中的内容 在 Subversion 中 这称为恢复 在 git 中 相当于 checkout 我在团
  • 是否有 git-merge --dry-run 选项?

    我正在合并一个可能有很多冲突的远程分支 我怎么知道它是否会发生冲突 我没有看到任何类似的东西 dry run on git merge 如前所述 传入 no commit标志 但为了避免快进提交 也传入 no ff 像这样 git merg
  • 使用“严格引用”时不能使用字符串作为 ARRAY 引用

    我正在尝试执行下面提到的代码并观察到错误 在使用 严格引用 时 无法使用字符串 RCSoWLAN ePDG 2 Qguest ASUS ATT 作为 ARRAY 引用 Perl代码 perl64 bin perl use strict us
  • 防止在 Git 中签出

    我目前正在研究使用 Git 管理 OpenInsight 应用程序的源代码 由于 OI 代码存储在数据库表中 因此需要进行一定量的手动工作才能将源代码导出为文本 反之亦然 到目前为止 我已经成功地使用 Git 挂钩自动化了很多这项工作 但是
  • 如何让“git status”始终使用短格式?

    我想要git status始终使用短格式 git status short M file1 M dir file2 file untracked3 dir file untracked4 似乎不存在这方面的配置选项 并且git config
  • Visual Studio 2017 - 无法将提交的更改推送到本地存储库

    我在 Win 10 Enterprise 上运行 VS 2017 Enterprise 15 7 3 我的本地硬盘上有一个 git 存储库 我在其中进行了一些更改 现在希望使用 VS 将这些更改 提交并推送 到我们位于网络文件共享之一的中央
  • 我是否需要在裸仓库上运行 git gc ?

    man git gc http www kernel org pub software scm git docs git gc html其中没有明显的答案 而且我在谷歌上也没有任何运气 尽管我可能只是使用了错误的搜索词 我明白你应该偶尔跑步
  • 如何在 Perl 中发送此 SOAP XML?

    我必须发送下面的 XML 我有no idea从哪儿开始 我知道我需要在 Perl 中查找 SOAP 但大致就是这样
  • 如何使用最近的标签来装饰 git 日志?

    git log decorate将相关引用的信息添加到日志输出中 commit 9e895ace5d82df8929b16f58e9f515f6d54ab82d tag v3 10 rc7 Author Linus Torvalds lt

随机推荐

  • 如何从字符串中删除所有空格

    如何去除Python字符串中的所有空格 例如 我想要一个像这样的字符串strip my spaces将变成stripmyspaces 但我似乎无法做到这一点strip gt gt gt strip my spaces strip strip
  • 如何在单个 HTML 表单中提交多个值?

    所以我有一个 HTML 表单
  • 如何在QT中接收正确的UDP数据包?

    我正在尝试编写一个QT程序来接收UDP数据包 我正在尝试从Packet Sender软件接收 这是我的代码 socket new QUdpSocket this bool result socket gt bind QHostAddress
  • angularjs $http.get如何强制UTF-8编码

    我必须读取使用 utf 8 字符集编码的 JSON 文件我使用这个语法 http get resources negozi json header Content Type application json charset UTF 8 su
  • 将警告视为错误没有任何效果

    在 Visual Studio 的项目设置中 我已将 将警告视为错误 设置为 全部 警告级别设置为 4 我通过故意引入违反 CA1305 的代码来对此进行测试 但它成功构建 并重建 并返回警告 我所期望的是构建会失败并返回一个错误 我的理解
  • Permissions.askAsync 未按预期工作

    我在通知 Permissions askAsync 时遇到问题 const status await Permissions askAsync Permissions NOTIFICATIONS 当使用 Permissions askAsy
  • 如何处理指向通用接口的指针的 JPA 注释

    我有一个泛型类 它也是一个映射的超类 它有一个私有字段 该字段保存指向同一类型的另一个对象的指针 MappedSuperclass public abstract class MyClass
  • kotlin 中数据类的流畅方法

    我们熟悉在java和其他编程语言中调用方法的流畅接口 例如 Picasso with this load url into imageView 这是通过返回所需类型对象的 setter 方法实现的 public Picasso with C
  • 使用 python 进行 oauth 谷歌

    我对网络编程相当陌生 我想在这里从头开始 我试图在网上搜索 但最终完全困惑了 现在我想学习的是如何通过Python脚本验证Google帐户 任何人都可以向我提供代码片段或任何示例 预先非常感谢 在过去几周的几次失败尝试之后 我花了一整天的时
  • 模拟私有构造函数

    The Site课程是由外部团队向我提供的 并且有private构造函数 public class Site int id String brand private Site int id String brand this id id t
  • Entity Framework 4.1“Code First”SetInitializer 在 Database.Delete 之后不会再次调用

    首先尝试使用 EF 4 1 代码进行一些单元测试 我有我的实时数据库 SQL Server 和我的单元测试数据库 Sql CE 在与 EF Sql CE 4 0 和事务支持进行斗争 并失败 后 我决定运行测试的最简单方法是 创建数据库 运行
  • 从 CouchDB 中的 Erlang 视图发出元组

    CouchDB 版本 0 10 0 使用本机 erlang 视图 我有一个以下形式的简单文档 id user 1 rev 1 9ccf63b66b62d15d75daa211c5a7fb0d type user identifiers AB
  • 使用 SCM 进行没有历史记录的 git 克隆

    我们的项目很大 我们希望避免克隆所有 git 历史记录 是否有可能git clone通过depth 1 using checkout scm在詹金斯 我找不到任何有关如何配置的文档SCM或者如果可能的话如何传递参数 Added 找到文档了
  • 无法验证以下目标配置(S3 到 SQS)

    我正在尝试使用无服务器设置一个工作流程 该工作流程创建一个新的 S3 存储桶 一个新的 SQS 队列 当在 S3 存储桶中创建一个对象时 将消息放入队列中 并在队列上有足够的消息时启动 lambda队列 我的资源块中有以下内容 resour
  • WinForms | C# |在文本框中间自动完成?

    我有一个自动完成功能的文本框 如下所示 txtName AutoCompleteMode AutoCompleteMode Suggest txtName AutoCompleteSource AutoCompleteSource Cust
  • Ascii 文件中的 Python BOM 错误

    我在使用 Python 2 6 时遇到了一个奇怪且烦人的问题 我正在尝试在我的嵌入式 Linux ARM 板上运行此文件 以及其他文件 http svn tuxisalive com software suite v3 smart core
  • python multiprocessing.Pool杀死*特定*长时间运行或挂起的进程

    我需要执行许多并行数据库连接和查询的池 我想使用 multiprocessing Pool 或并发 futures ProcessPoolExecutor Python 2 7 5 在某些情况下 查询请求花费太长时间或永远无法完成 挂起 僵
  • Java中如何对URL进行编码以避免特殊字符? [复制]

    这个问题在这里已经有答案了 我需要java代码来编码URL以避免特殊字符 例如空格和 和 等 URL 构造很棘手 因为 URL 的不同部分对于允许使用的字符有不同的规则 例如 加号在 URL 的查询部分中保留 因为它代表空格 但在 URL
  • 从 wav 文件读取样本

    我正在尝试用 html5 制作一个网页 它将 wav 文件中的示例数据存储在数组中 有没有办法用javascript获取样本数据 我正在使用文件输入来选择 wav 文件 在 javascript 中我已经添加了 document getEl
  • 使用 git 有效地重写(rebase -i)大量历史记录

    我有一个 git 存储库 最新版本中有大约 3500 个提交和 30 000 个不同的文件 它代表了多人大约 3 年的工作成果 我们已获得将其全部开源的许可 我正在努力发布整个历史记录 而不仅仅是最新版本 为此 我感兴趣的是 回到过去 并在