HN 日本語サマリー

← 一覧へ戻る
プログラミング

Gitリポジトリの履歴を迅速に書き換える

Quickly rewrite Git repository history (github.com)

52 pointsby modinfo4 コメント

要約

git-filter-repoは、履歴を書き換えるための多機能ツールであり、他の場所では見つけられない機能も備えています。これはgit filter-branchと同様の領域に位置しますが、パフォーマンスの悪さがなく、より多くの機能と、些細な書き換えケースを超えて使いやすさがスケールする設計を備えています。gitプロジェクトは現在、git filter-branchの代わりにgit-filter-repoを推奨しています。ほとんどのユーザーはfilter-repoをシンプルなコマンドラインツールとして使用するでしょうが、その中核には履歴書き換えツールを作成するためのライブラリが含まれています。

全文翻訳

git filter-repoは、履歴を書き換えるための多機能ツールであり、他の場所では見つけられない機能も備えています。これはgit filter-branchと同様の領域に位置しますが、 capitulation-inducing(降伏を誘発するような)パフォーマンスの悪さがなく、より多くの機能と、些細な書き換えケースを超えて使いやすさがスケールする設計を備えています。git filter-repoは現在、gitプロジェクトによってgit filter-branchの代わりに推奨されています。ほとんどのユーザーはfilter-repoをシンプルなコマンドラインツールとして使用するでしょうが(おそらく数個のフラグしか使用しないでしょう)、その中核には履歴書き換えツールを作成するためのライブラリが含まれています。そのため、特殊なニーズを持つユーザーは、これを利用して完全に新しい履歴書き換えツールを迅速に作成できます。目次前提条件インストール方法は?使用方法は?なぜ他の代替手段ではなくfilter-repoなのか?filter-branchBFG Repo Cleaner簡単な例と比較filter-repoでこれを解決するBFG Repo Cleanerでこれを解決するfilter-branchでこれを解決するfast-export/fast-importfilter-repoの設計思想貢献方法は?行動規範はありますか?アップストリームの改善前提条件filter-repoには以下が必要です:git >= 2.36.0python3 >= 3.6インストール方法は?git-filter-repoリポジトリには多くのファイルがありますが、主要なロジックはすべてgit-filter-repoという単一ファイルのPythonスクリプトに含まれています。これは、多くのシステムで基本的な使用のためにインストールを非常に簡単にするために行われました。そのファイルを$PATHに配置するだけです。基本的な使用法や特別なケース以外のことは、INSTALL.mdを参照してください。より詳細な手順は、以下のいずれかに該当する場合にのみ必要です:上記の簡単なインストールのコメントが直感的に明白でない場合「python3」以外の名前のpython3実行可能ファイルで作業している場合ドキュメント(-hで表示される組み込みドキュメント以外)をインストールしたい場合contribの例を実行したい場合filter-repoをモジュール/ライブラリとして使用して独自のPythonフィルタリングスクリプトを作成したい場合使用方法は?包括的なドキュメントについては:ユーザーマニュアルを参照してください。ユーザーマニュアルの代替フォーマットは、さまざまな外部サイト(例)で利用可能です。htmlpreview.github.ioのレイアウトが気に入らない方向けですが、最新リリース時点でのみ最新である可能性があります。例から学習することを好む場合:filter-branchコマンドを変換するためのチートシートがあり、filter-branchマニュアルのすべての例をカバーしています。BFG Repo Cleanerコマンドを変換するためのチートシートがあり、BFGウェブサイトのすべての例をカバーしています。以下の簡単な例が興味を引くかもしれません。ユーザーマニュアルには、広範な例のセクションがあります。ユーザーから報告された問題に基づいたフィルタリング例のセットを収集しました。いずれの場合も、よくある質問も役立つかもしれません。なぜ他の代替手段ではなくfilter-repoなのか?これはGit Rev Newsの記事でfilter-repoについてより詳細にカバーされましたが、主な競合他社に関するいくつかのハイライトです:filter-branchfilter-branchは、非些細なリポジトリでは極端に、または使用不能なほど遅いです(本来あるべき速度の数倍遅い)。filter-branchは、サイレントに書き換えを破損する可能性のある多くの落とし穴があり、少なくとも、開始したものよりも問題が多く厄介なものを提供することで、「クリーンアップ」の取り組みを妨げます。filter-branchは、わずかに非些細な書き換えであっても、使用するのが非常に困難です。gitプロジェクトは、上記のfilter-branchの問題は後方互換性を損なわずに修正できないと述べており、filter-branchの使用を中止することを推奨しています。filter-branchの熱狂的なファンは、filter-repoに基づいたfilter-branchの再実装であるfilter-lamely(別名filter-branch-ish)に興味があるかもしれません(ただし、filter-repoほど高速または安全ではありません)。filter-branchマニュアルの例コマンドをfilter-repoコマンドに変換する方法を示すチートシートが利用可能です。BFG Repo CleanerBFG Repo Cleanerはその時代には優れたツールでしたが、一部の処理を単純化する一方で、数種類の書き換えに限定されています。そのアーキテクチャは、より多くの種類の書き換えを処理するようにはなっていません。そのアーキテクチャは、意図されたユースケースであっても、いくつかの欠点とバグを提示します。BFGのファンは、BFGに基づいたBFGの再実装であり、BFGと比較していくつかの新機能とバグ修正が含まれているbfg-ishに興味があるかもしれません。BFG Repo Cleanerマニュアルの例コマンドをfilter-repoコマンドに変換する方法を示すチートシートが利用可能です。簡単な例と比較リポジトリの一部を抽出したいとしましょう。その意図は、その一部だけを別の大きなリポジトリにマージすることです。抽出のために、私たちは以下を行いたいとします:単一のディレクトリ、src/の履歴を抽出します。これは、src/以下のパスのみがリポジトリに残され、このディレクトリ以外のパスのみに触れたコミットが削除されることを意味します。すべてのファイルを新しいディレクトリ、my-module/という名前のプレフィックスを持つようにリネームします(例:src/foo.cがmy-module/src/foo.cになる)。抽出されたリポジトリ内のすべてのタグを'my-module-'というプレフィックスを持つようにリネームします(後でこのリポジトリを他のものにマージする際の競合を避けるため)。filter-repoでこれを解決するfilter-repoでこれを行うのは、次のコマンドと同じくらい簡単です:git filter-repo --path src/ --to-subdirectory-filter my-module --tag-rename '':'my-module-'(単一引用符は不要ですが、空文字列をプレフィックスとしてmy-module-に置き換えていることを人間がより明確に理解できるようにします)。BFG Repo Cleanerでこれを解決するBFG Repo Cleanerはこの種の書き換えを行う能力がありません。実際、要求された3種類の変更はすべてその機能の範囲外です。filter-branchでこれを解決するfilter-branchには、必要な呼び出し方法を理解した後でも、多くの注意点があります(以下で詳しく説明します):git filter-branch \ --tree-filter 'mkdir -p my-module && \ git ls-files \ | grep -v ^src/ \ | xargs git rm -f -q && \ ls -d * \ | grep -v my-module \ | xargs -I files mv files my-module/' \ --tag-name-filter 'echo "my-module-$(cat)"' \ --prune-empty -- --all git clone file://$(pwd) newcopy cd newcopy git for-each-ref --format="delete %(refname)" refs/tags/ \ | grep -v refs/tags/my-module- \ | git update-ref --stdin git gc --prune=now上記のfilter-branch呼び出しが--tree-filterを使用しているため非常に遅くなることに気づく人もいるかもしれません。代わりにfilter-branchの--index-filterオプションを使用することもできます。上記のコマンドを次のように変更します:git filter-branch \ --index-filter 'git ls-files \ | grep -v ^src/ \ | xargs git rm -q --cached; git ls-files -s \ | sed "s%$(printf \t)%&my-module/%" \ | git update-index --index-info; git ls-files \ | grep -v ^my-module/ \ | xargs git rm -q --cached' \ --tag-name-filter 'echo "my-module-$(cat)"' \ --prune-empty -- --all git clone file://$(pwd) newcopy cd newcopy git for-each-ref --format="delete %(refname)" refs/tags/ \ | grep -v refs/tags/my-module- \ | git update-ref --stdin git gc --prune=nowしかし、どちらのfilter-branchコマンドにも多くの注意点があります。まず、なぜfilter-branchに5つのコマンドをリストアップするのか疑問に思う人もいるかもしれません。--allと--tag-name-filterの使用、およびfilter-branchのマニュアルページがクローンがあれば古いオブジェクトを取得するのに十分であると主張しているにもかかわらず、他のタグを削除し、プッシュする前に新しい履歴と古い履歴の混同を避けるために別のgcを実行する追加の手順が必要です。その他の注意点:コミットメッセージは書き換えられません。そのため、コミットメッセージが(省略された)sha1で以前のコミットを参照している場合、書き換え後、それらのメッセージはもはや履歴の一部ではないコミットを参照するようになります。それらの(省略された)sha1参照を新しいコミットIDを参照するように書き換える方が良いでしょう。--prune-emptyフラグは、削除されるべきコミットを見逃すことがあり、また、フィルタリングの結果として空になったのではなく、最初から空であったコミットも削除します。バージョン管理や公開目的で意図的に空のコミットを使用するリポジトリの場合、これは有害になる可能性があります。