5

git filter-branch機能を使用して、gitリポジトリ内のテキストデータを置き換えようとしています。

さまざまな用語を検索して置き換えるための簡単なスクリプトを作成しました。実行速度が非常に遅くなりました。検索結果と置換操作をカスタマイズするために、複数行のBASHコードを実行しました。私のコードはあまり効率的ではなかったと思います。私は先に進んで、半効率的であるはずの最初の行だけを試してみることにしました。コードベースをウォークスルーするには、まだ永遠に時間がかかります。

BASHまたは別の簡単なアプローチを使用してファイルを検索し、検索と置換の操作を並行して実行して、処理を高速化することは可能ですか?

そうでない場合、これをより適切に処理する方法について他に何か提案はありますか?

これが私が実行している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

5 に答える 5

4

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はできるだけ多くのファイル(引数)で実行されます。Findの「-print0」とxargsの「-0」は「ゼロバイトのファイル名を分離する」ことを意味します。したがって、ファイル名にスペース、新しい行、バイナリのゴミ箱などが含まれていても問題はありません。

于 2013-01-30T22:24:45.400 に答える
3

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

于 2013-02-03T15:18:46.670 に答える
1

この問題は面白いと思ったので、少し遊んで、この部分的に機能するスクリプトを共有します。私の元のアプローチは少し間違っていましたが、それは速いかもしれません。

置き換えたい文字列が変更に含まれているすべてのコミットで変更されたファイルを検索することで、パフォーマンスを向上させようとしましたgit log -Sstring。しかし、それらだけを変更すると、次のコミットで変更が表示されるので、スクリプトを数回実行する必要がありましたが、変更だけですべてのファイルがチェックされるわけではないため、これを複数回実行する方が高速な場合があります。あなたのバージョンですが、何もしない場合にフィルターブランチにかかる時間はわかりません。

その一部を使用できる場合があります。最初にすべてのファイル名を。で取得する場合がありますgit log -S...。ループの代わりにxargsbeforeを使用することで改善できますが、開発中はこのフォームの方が好きです。親を適切に検出する方法がわからないため、この方法で最初のコミットケースを個別に処理する必要がありました。sedfor

とにかく私も学ぶためにここにいるので、この問題に対処する良い方法を見つけたら共有してください:)

#!/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作業ディレクトリに配置するべきではないと思いますが、これについてはよくわかりません。

于 2013-01-31T19:22:17.740 に答える
1

BFG Repo-Cleanerが必要です。これgit-filter-branchは、JVMで実行され、Gitリポジトリからプライベートデータを削除するように明示的に設計された、より高速でシンプルな代替手段です。それはマルチスレッドであり、あなたが説明しているタスクに正確に合わせて最適化されています。通常は10〜50倍高速git-filter-branchです。リポジトリが大きいほど高速です。

Java jarをダウンロードし、private.txt削除するパスワードなどをリストしたファイル(1行に1つのエントリ)を作成してから、次のコマンドを実行します。

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

リポジトリの履歴でしきい値サイズ(デフォルトでは1MB)未満のすべてのファイルがスキャンされ、一致する文字列(最新のコミットに含まれていない)は文字列「***REMOVED***」に置き換えられます。次に、を使用git gcしてデッドデータをクリーンアップできます。

$ git gc --prune=now --aggressive
于 2013-02-02T23:17:41.007 に答える
0

再帰的なテキスト置換のいくつかのベンチマーク(ソース

0.131411 sec  find-xargs-sd.sh

0.323906 sec  find-xargsparallel-sed.sh
0.326623 sec  find-xargs-sed.sh
0.397934 sec  find-xargs-perl.sh

4.53739 sec  find-exec-sed.sh

10.3247 sec  parallel-sed.sh
于 2022-01-10T15:27:52.353 に答える