如何通过一个简单的步骤将存储库中 *Foo* 的 *所有* 实例(内容、文件名和提交消息)批量替换为 *Bar*?

How to batch-replace *all* instances (content, filenames and commit messages) of *Foo* to *Bar* in a repo in a single, simple step?

假设我有一个名为 "Hammerstein" 的尚未发布的软件产品的大型存储库,由我是德国著名软件公司 "Apfel" 的员工编写。

有一天,"Apfel" 分拆出 Hammerstein 部门并将其出售给 more 著名公司 "Oráculo",该公司将 "Hammerstein" 重命名为"Reineta" 出于民族自豪感,决定将其开源。

协议要求 所有 对 "Hammerstein" 和 "Apfel" 的引用在存储库中替换为 "Oráculo" 和 "Reineta"。

必须替换所有文件名、所有提交消息、所有内容

因此,例如:

  1. src/core/ApfelCore/main.cpp 必须变成 src/core/OraculoCore/main.cpp.

  2. "Add support for Apfel Groupware Server" 的提交信息必须变成"Add support for Oraculo Groupware Server"

  3. 字符串ApfelServerInstance* local_apfel#define REINETAUrl("http://apfel.de")必须变成OraculoServerInstance* local_oraculo#define HAMMERSTEIN

这也适用于 不再在 HEAD 中的文件。

最少的人工干预实现它的最简单和最无痛的方法是什么(以便它可以应用于批处理到可能大量 repositories/assets)?

  1. BFG 可以替换字符串,但它似乎只有 --delete-file 选项,而不是 --rename-file,即使那样它也不会将模式作为参数
  2. This approach 似乎只适用于 HEAD 而不是整个历史;我没有运气将它与 --tree-filter
  3. 一起使用

in a single, simple step?

不是单一的,也不是那么简单,但有可能:


要更新提交消息,您需要使用 the --msg-filter of git filter-branch

git filter-branch -f --msg-filter 'sed "s/Apfel/Oraculo/"' -- --all

注意--all,以便filter in all commits of every branches
您可能需要多次重复该命令以处理不同的情况。


移动文件(不使用--tree-filter),可参考this answer (and this article),并适配以构建新路径:

git filter-branch --index-filter 'git ls-files -s | \
  sed "s,/ApfelCore/,/OraculoCore/," | \
  GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && \
  mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' \
-- --all

如前所述,BFG can replace all strings; see "How to substitute text from files in git history?"

java -jar bfg.jar --replace-text replacements.txt my-repo.git

The replacements.txt file should contain all the substitutions you want to do, in a format like this (one entry per line)

Apfel==>Oraculo
apfel==>oraculo
REINETA==>HAMMERSTEIN

完全披露:我是 BFG Repo-Cleaner

的作者

正如您在问题中所说,BFG 支持使用 --replace-text 标志替换文件内容 - 但此标志不会扩展到文件 names 和提交消息。那么,要使 BFG 的 --replace-text 操作也扩展到这些代码库,需要对代码库进行哪些改动?

这归结为挂钩一些用于文件内容更改的新 Cleaner[V] implementations, where V is the type of thing you want to clean (a commit message, a directory listing), and the Cleaner just has the job of producing a new, clean V from an old, dirty V. To perform the actual text change, you can re-use the same text-replacing function

文件名

使用 Cleaner[Seq[Tree.Entry]] - 'tree' 是 Git 调用文件夹 ('file tree') - 所以你只需更新每个 Tree.Entry.

提交消息

使用 Cleaner[CommitNode] - 同样,您只是替换 message 字段中的文本 - 请参阅 CommitMessageObjectIdsUpdater 以了解您要执行的操作的非常接近的示例.当你在那里时,如果你愿意,你可以对作者和提交者的电子邮件地址做一些事情(例如清除 ...@apfel.com,我猜)。

速度

正如@VonC 在他的回答中提到的,filter-branch 可以 执行这两个替换(文件名和提交消息)但是 --msg-filter 标志应该相当快地完成提交消息更新,我相信 filter-branch 在像您这样的大型代码库中重命名文件会相当 非常慢 。 BFG 正是针对此类操作进行了优化,速度将提高几 100 倍。

BFG 在 https://www.bountysource.com/teams/bfg-repo-cleaner 接受捐赠 - 所以如果您愿意支持此功能的开发,或者如果您发现 BFG 对解决您的问题很有用问题,这就是你可以有所作为的地方。