如何并行运行多个文件的递归查找和替换操作?

时间:2013-01-30 22:02:47

标签: git bash sed parallel-processing find

我正在尝试使用git filter-branch功能替换git存储库中的文本数据。

我写了一个简单的脚本来搜索各种术语并替换它们。它运行得非常慢。我有多行BASH代码执行来自定义我的搜索结果和替换操作。我知道我的代码效率不高。我决定继续尝试我的第一行应该是半效率的。它仍然需要永远遍历代码库。

是否可以使用BASH或其他简单方法搜索我的文件并执行Find&并行替换操作以加快速度?

如果没有,有没有其他建议如何更好地处理这个?

这是我正在执行的Git命令:

git filter-branch --tree-filter "sh /home/kurtis/.bin/redact.sh || true" \
    -- --all

这是我的命令基本上执行的代码:

find . -not -name "*.sql" -not -name "*.tsv" -not -name "*.class" \
    -type f -exec sed -i 's/01dPassw0rd\!/HIDDENPASSWORD/g' {} \;

4 个答案:

答案 0 :(得分:3)

git filter-branch无法并行处理提交,因为它需要知道父提交的哈希(id)来计算当前哈希值。

但是你可以加快每次提交的处理速度:

您的代码为每个文件执行sed。那很慢。请改用:

find . -not -name "*.sql" -not -name "*.tsv" -not -name "*.class" \
       -type f -print0 \
  | xargs -0 sed -i 's/01dPassw0rd\!/HIDDENPASSWORD/g'

此版本与您的版本完全相同,但sed使用尽可能多的文件(参数)执行。查找“-print0”和xargs的“-0”表示“零字节的separe文件名”。因此,当文件名包含空格,新行,二进制垃圾等时,没有任何问题。

答案 1 :(得分:2)

使用GNU Parallel,您可以在每个CPU上并行化:

find . -not -name "*.sql" -not -name "*.tsv" -not -name "*.class" \
   -type f -print0 |
parallel -q -0 sed -i 's/01dPassw0rd\!/HIDDENPASSWORD/g'

了解详情:https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

答案 2 :(得分:1)

我发现这个问题很有趣所以我玩了一点,我分享了这部分工作脚本。我原来的方法有点不对,但可能很快(呃)。

我尝试通过在每次提交中搜索修改后的文件来提高性能,其中修改包含要用git log -Sstring替换的字符串。但是我忘记了如果我只更改了那些修改将出现在下一次提交中,所以我不得不多次运行脚本,但它不会检查所有文件只是修改所以它可能更快运行多次然后你的版本,但我不确定如果什么都不做,过滤器分支需要多长时间。

您可以使用它的一部分,也许首先使用git log -S...获取所有文件名。您可以在xargs之前使用sed而不是for循环来改进它,但在开发过程中我更喜欢这种形式。我不知道如何正确地发现父母,这就是为什么我这样做,并且必须分别处理最初的提交案例。

无论如何我也在这里学习如果你找到一个很好的方法来处理这个问题请分享:)

#!/bin/bash

commit=$1
pattern=$2
replace=$3

function replaceall() {
  for f in `git log -S$pattern --pretty="format:" --name-only $1 | egrep -v '.sql$|.class$|.tsv$'`; do
    echo "FILE $f"
    sed -i "s/$pattern/$replace/g" $f
  done
}

parents=`git log --pretty=%P -n 1 $commit`
if test -z "$parents"; then
  echo "ROOT"
  replaceall $commit
else
  for p in $parents; do
    echo "PARENT $p"
    replaceall $p..$commit
  done
fi

用法:git filter-branch -f --tree-filter '/path/to/script.sh $commit 01dPassw0rd\! HIDDENPASSWORD' -- --all

我认为该脚本不应该在您的git工作目录中,因为树形过滤器会添加重写时找到的所有内容,但我不确定。

答案 3 :(得分:1)

您希望BFG Repo-Cleaner是在{JVM>中运行的git-filter-branch的更快,更简单的替代方案,并且明确用于从Git repos中删除私有数据。它是多线程的,并且针对您正在描述的任务进行了优化。它通常比git-filter-branch快10-50倍 - 你的回购越大,它就越快。

下载Java jar,创建一个列出要删除的密码等的private.txt文件(每行一个条目),然后运行此命令:

$ java -jar bfg.jar  --replace-text private.txt  my-repo.git

将扫描您的仓库历史记录中阈值大小(默认为1MB)的所有文件,并且任何匹配的字符串(不在最新提交中)将替换为字符串“ *** *** REMOVED”。然后,您可以使用git gc清除死数据:

$ git gc --prune=now --aggressive