web-dev-qa-db-ja.com

git gc --aggressive vs git repack

gitリポジトリのサイズを縮小する方法を探しています。検索すると、ほとんどの場合git gc --aggressiveにつながります。また、これは推奨されるアプローチではないことも読みました。

どうして? gc --aggressiveを実行している場合、何に注意する必要がありますか?

git repack -a -d --depth=250 --window=250よりもgc --aggressiveが推奨されます。どうして? repackはリポジトリのサイズをどのように削減しますか?また、フラグ--depthおよび--windowについても明確ではありません。

gcrepackの間で何を選択すればよいですか?いつgcrepackを使用すべきですか?

75
Ajith R Nayak

現在、違いはありません。git gc --aggressiveは、2007年にLinusが提案したとおりに動作します。下記参照。バージョン2.11(2016年第4四半期)では、gitのデフォルトの深さは50です。サイズ250のウィンドウは各オブジェクトのより大きなセクションをスキャンするため良好ですが、250の深さはすべてのチェーンが非常に深い古いオブジェクト。これにより、ディスク使用量がわずかに低下するため、将来のgit操作が遅くなりますall


歴史的背景

Linusは、「[reallybad pack」または「本当に恐ろしいほど悪い」という言葉を使用している場合にのみ、git gc --aggressiveを使用することを提案しました(完全なメーリングリストの投稿については以下を参照)しかし、「ほとんどの場合、実際には行うのは本当に悪いことです。」その結果、リポジトリは開始時よりも悪い状態に陥ることさえあります!

「長く複雑な歴史」をインポートした後、これを適切に行うために彼が提案するコマンドは

git repack -a -d -f --depth=250 --window=250

ただし、これは、リポジトリの履歴から既に 不要な不要ファイル があり、 git filter-branch documentation にあるリポジトリを縮小するためのチェックリストに従っていることを前提としています。

git-filter-branchを使用して、通常--index-filter--subdirectory-filterの組み合わせを使用して、ファイルのサブセットを取り除くことができます。結果として得られるリポジトリは元のリポジトリよりも小さくなると予想されますが、実際に小さくするためには、Gitが指示するまでオブジェクトを失わないように努力するため、さらにいくつかの手順が必要です。最初に以下を確認してください:

  • Blobがその存続期間中に移動された場合、ファイル名のすべてのバリアントを本当に削除しました。 git log --name-only --follow --all -- filenameは、名前の変更を見つけるのに役立ちます。

  • すべての参照を本当にフィルタリングしました。--tag-name-filter cat -- --allを呼び出すときにgit filter-branchを使用します。

次に、より小さなリポジトリを取得する2つの方法があります。より安全な方法は、クローンを作成することです。これにより、元のファイルがそのまま保持されます。

  • git clone file:///path/to/repoで複製します。クローンには削除されたオブジェクトはありません。 git-cloneを参照してください。 (プレーンパスでクローンを作成すると、すべてがハードリンクされることに注意してください!)

何らかの理由でクローンを作成したくない場合は、代わりに次の点を(この順序で)チェックしてください。これは非常に破壊的なアプローチなので、バックアップを作成するか、クローン作成に戻ります。あなたは警告されました。

  • Git-filter-branchによってバックアップされた元の参照を削除します:say

    git for-each-ref --format="%(refname)" refs/original/ |
      xargs -n 1 git update-ref -d
    
  • すべてのreflogsをgit reflog expire --expire=now --allで期限切れにします。

  • ガベージは、参照されていないすべてのオブジェクトをgit gc --Prune=nowで収集します(またはgit gc--Pruneへの引数をサポートするほど新しくない場合は、代わりにgit repack -ad; git Pruneを使用します)。


Date: Wed, 5 Dec 2007 22:09:12 -0800 (PST)
From: Linus Torvalds <torvalds at linux-foundation dot org>
To: Daniel Berlin <dberlin at dberlin dot org>
cc: David Miller <davem at davemloft dot net>,
    ismail at pardus dot org dot tr,
    gcc at gcc dot gnu dot org,
    git at vger dot kernel dot org
Subject: Re: Git and GCC
In-Reply-To: <[email protected]>
Message-ID: <[email protected]>
References: <[email protected]>
            <[email protected]>
            <[email protected]>
            <[email protected]>
            <[email protected]>

2007年12月6日木曜日、ダニエルベルリンは次のように書いています。

実際、SVNリポジトリから変換したかどうかに関係なく、git-gc --aggressiveがファイルをパックするためにこの愚かなことをすることがあります。

絶対に。 git --aggressiveはほとんどが愚かです。 「本当に悪いパックがあることを知っており、私が行った悪いパッキングの決定をすべて捨てたい」という場合にのみ本当に役立ちます。

これを説明するには、git delta-chainsがどのように機能し、他のほとんどのシステムとどのように異なるかを説明する価値があります(おそらくご存知でしょうが、とにかく基本を説明します).

他のSCMでは、デルタチェーンは一般に固定されています。それは「前方」または「後方」であり、リポジトリを操作するにつれて少し進化する可能性がありますが、一般に、ある種の単一のSCMエンティティとして表される単一のファイルに対する一連の変更です。 CVSでは、明らかに*,vファイルであり、他の多くのシステムもかなり似たようなことをしています。

また、Gitはデルタチェーンを実行しますが、より「緩やかに」実行します。固定されたエンティティはありません。デルタは、gitが適切なデルタ候補と見なすランダムな他のバージョン(さまざまなかなり成功したヒューリスティックを使用)に対して生成され、ハードグループ化ルールはまったくありません。

これは一般的に非常に良いことです。さまざまな概念上の理由で優れています(ie、git内部ではリビジョンチェーン全体を実際に気にする必要さえありません—デルタに関してはまったく考えていません)。また、柔軟性のないデルタルールを取り除くことは、gitが2つのファイルをマージすることにまったく問題がないことを意味するため、素晴らしいです。たとえば、何らかの隠された意味を持つ任意の*,v「リビジョンファイル」はありません。

また、デルタの選択ははるかに自由な質問であることを意味します。デルタチェーンを1つのファイルのみに制限する場合、実際にデルタをどうするかについて多くの選択肢はありませんが、gitでは、まったく別の問題になる可能性があります。

本当に悪い名前の--aggressiveが出てくるのはここです。gitは一般にデルタ情報を再利用しようとしますが(良いアイデアであり、見つかったすべての良いデルタを再検索するCPU時間を無駄にしないためです)以前)、「空白の状態で最初からやり直し、以前のすべてのデルタ情報を無視して、新しいデルタのセットを生成しよう」と言いたいことがあります。

したがって、--aggressiveは積極的であることではなく、以前に行った決定を再実行するためにCPU時間を浪費することです。

時々それは良いことです。特にいくつかのインポートツールは、本当にひどく悪いデルタを生成する可能性があります。たとえば、git fast-importを使用するものはどれも、大きなデルタレイアウトをあまり持たない可能性が高いため、「きれいなスレートから始めたい」と言う価値があるかもしれません。

しかし、ほとんどの場合、他の場合では、実際には本当に悪いことです。 CPU時間を浪費することになります。特に、実際に以前のデルタ処理で良い仕事をしていた場合は、最終結果はすべてのgoodデルタを再利用しません見つかったので、実際にはさらに悪い結果になります!

git gc --aggressiveドキュメントを削除するために、Junioにパッチを送信します。役立つこともありますが、一般的には、それが何をしているのかを非常に深いレベルで本当に理解していて、そのドキュメントがそれを助けていない場合にのみ役立ちます。

一般に、インクリメンタルgit gcを実行するのが適切なアプローチであり、git gc --aggressiveを実行するよりも優れています。古いデルタを再利用し、それらの古いデルタが見つからない場合(そもそもインクリメンタルGCを行う理由!)、新しいデルタを作成します。

一方、「長く複雑な履歴の最初のインポート」は、本当に良いデルタを見つけるのに多くの時間を費やす価値がある点であることは間違いありません。そうすれば、それ以降のすべてのユーザーは(git gc --aggressiveを使用して元に戻さない限り)、その1回限りのイベントの利点を享受できます。そのため、特に長い歴史を持つ大規模なプロジェクトの場合は、デルタ検索コードがワイルドになるように指示して、余分な作業を行う価値があるでしょう。

そのため、git gc --aggressiveに相当しますが、が適切に行われます—

git repack -a -d --depth=250 --window=250

その深さは、デルタチェーンがどれだけ深くできるか(古い履歴では長くする-スペースのオーバーヘッドに値する)であり、ウィンドウのことは、各デルタ候補がスキャンするオブジェクトウィンドウの大きさです。

そしてここでは、-fフラグ(「古いデルタをすべてドロップ」)を追加することをお勧めします。これは、実際にこの候補が実際に適切な候補を見つけるようにしているためです。

そして、それは永遠に1日かかります(i.e。、「一晩やって」)。しかし、最終的な結果は、そのリポジトリの下流にいるすべての人が、自分自身で労力を費やすことなく、はるかに優れたパックを取得することです。

          Linus
68
Greg Bacon

いつgc&repackを使用すべきですか?

Git Garbageコレクションは完全に機能しないようです 」で述べたように、 git gc --aggressive はそれだけでは不十分です。
そして、 以下で説明します のように、しばしば必要ありません。

最も効果的な組み合わせは、 git repack を追加することですが、 git Prune も追加します。

git gc
git repack -Ad      # kills in-pack garbage
git Prune           # kills loose garbage

注:Git 2.11(2016年第4四半期)では、デフォルトのgc aggressiveの深さが50に設定されます

commit 07e7dbf (2016年8月11日)by Jeff King(peff を参照してください。
C浜野順夫-gitster- in commit 0952ca8 、2016年9月21日)

gc:デフォルトの積極的な深さ50

"git gc --aggressive"は、デルタチェーンの長さを250に制限するために使用されます。これは、スペースをさらに節約するには深すぎ、実行時のパフォーマンスに有害です。
制限は50に削減されました。

要約すると、現在のデフォルトの250はスペースをあまり節約せず、CPUのコストがかかります。それは良いトレードオフではありません。

--aggressiveの「git-gc」フラグは、次の3つのことを行います。

  1. -f」を使用して既存のデルタを破棄し、ゼロから再計算します
  2. 「--window = 250」を使用して、デルタをより厳密に調べます
  3. 「--depth = 250」を使用して、より長いデルタチェーンを作成します

アイテム(1)と(2)は、「積極的な」再梱包に適しています。
彼らは、より良いパックを得るために、より多くの計算作業を再パックに依頼します。再梱包中に費用を支払うと、他の操作では利益のみが表示されます。

項目(3)はそれほど明確ではありません。
より長いチェーンを許可することは、デルタに対する制限が少なくなることを意味します。これは、より良いものを見つけてスペースを節約する可能性があることを意味します。
しかし、デルタにアクセスする操作は、より長いチェーンに従う必要があることも意味します。これは、パフォーマンスに影響します。
それはトレードオフであり、トレードオフが良いものであることも明らかではありません。

研究のためにコミット を参照)

深さを減らすと、通常の操作のCPU節約が改善されることがわかります。
しかし、深さが深くなるにつれてスペースの節約はそれほど大きくないこともわかります。 10と50の間で5〜10%を節約することは、おそらくCPUのトレードオフに値します。 50%から100%に1%を節約したり、100%から250%に0.5%を節約したりすることはおそらくないでしょう。


CPUの節約といえば、「git repack」は--threads=<n>オプションを受け入れて、それをパックオブジェクトに渡すことを学びました。

commit 40bcf31 (2017年4月26日)by Junio C Hamano(gitster を参照してください。
浜野邦夫-gitster- in commit 31fb6f4 、2017年5月29日)

再パック:--threads=<n>を受け入れてpack-objectsに渡す

--window=<n>--depth=<n>についてはすでにそうしています。これは、ユーザーが複数のスレッドの競合に影響されることなく、再現可能なテストのために--threads=1を強制する場合に役立ちます。

48
VonC

git gc --aggressiveの問題は、オプション名とドキュメントが誤解を招くことです。

Linus自身がこのメールで説明しています のように、git gc --aggressiveは基本的に次のことを行います:

Gitは通常、デルタ情報を再利用しようとしますが(これは良い考えであり、以前に見つかったすべての良いデルタを再検索するCPU時間を無駄にしないためです)、時々、「最初からやり直しましょう空白の状態で、以前のすべてのデルタ情報を無視し、新しいデルタのセットを生成しようとします。」.

通常、gitはこれらのデルタを非常に柔軟に決定するため、gitでデルタを再計算する必要はありません。本当に悪いデルタがあることを知っている場合にのみ意味があります。 Linusが説明しているように、主にgit fast-importを利用するツールはこのカテゴリに分類されます。

ほとんどの場合、gitは有用なデルタを決定するのに非常に優れた仕事をしており、git gc --aggressiveを使用すると、多くのCPU時間を浪費しながら潜在的にさらに悪いデルタが残ります。


Linusはgit repack--depthの大きい--windowがほとんどの場合より良い選択であるという結論でメールを終了します。特に、大規模なプロジェクトをインポートし、gitが適切なデルタを見つけられるようにしたい場合。

したがって、git gc --aggressiveに相当しますが、適切に実行されます-(一晩)

git repack -a -d --depth=250 --window=250

その深さは、デルタチェーンがどれだけ深くできるか(古い履歴では長くする-スペースオーバーヘッドの価値がある)であり、ウィンドウのことは、各デルタ候補がスキャンするオブジェクトウィンドウの大きさです。

そしてここで、-fフラグ(「古いデルタをすべてドロップ」)を追加することをお勧めします。これは、実際にこのフラグが実際に適切な候補を見つけることを確認しようとしているためです。

13
Sascha Wolf

あぶない。バックアップがない場合、リモートと同期されていないリポジトリでgit gc --agressiveを実行しないでください。

この操作により、デルタがゼロから再作成され、正常に中断されるとデータが失われる可能性があります。

私の8GBコンピューターの場合、積極的なgcは1Kbのコミットで1Gbリポジトリーのメモリーを使い果たしました。 OOMキラーがgitプロセスを終了すると、リポジトリがほとんど空になり、作業ツリーと少数のデルタのみが生き残りました。

もちろん、リポジトリの唯一のコピーではなかったので、再作成してリモートからプルしました(壊れたレポでフェッチが機能せず、「デルタの解決」ステップで数回デッドロックしましたが、レポがリモートをまったく使用しない単一開発者のローカルリポジトリ-最初にバックアップします。

6
Sage Pointer

注:Git 2.22(2019年第2四半期)で明らかになっているように、git gc --aggressiveの使用に注意してください。

commit 0044f77commit daecbf2commit 7384504commit 22d4e3bcommit 080a448 を参照してください、 コミット54d56f5コミットd257e0fコミットb6a8d09 (2019年4月7日)、および コミットfc559fbcommit cf9cd77commit b11e856 (2019年3月22日)by ÆvarArnfjörðBjarmason(avar .
C浜野順夫-gitster- in commit ac70c5 、2019年4月25日)

gc docs:--aggressiveの有用性を軽視する

既存の「gc --aggressive」ドキュメントは、定期的に実行することをユーザーに推奨するだけでは不十分です。
これらのドキュメントをこのオプションを使用するためのアドバイスとして受け取った多くのユーザーと個人的に話しましたが、通常それは(ほとんど)時間の無駄です

それでは、実際に何をするのかを明確にし、ユーザーが独自の結論を導き出しましょう。

ジェフキングの説明 の簡単なバージョンを言い換えるために、「効果[...]は持続的です」も明確にしましょう。

つまり、 git-gcドキュメントには現在含まれています

攻撃的

--aggressiveオプションが指定されると、 git-repack-fフラグを指定して呼び出され、これが--no-reuse-deltagit-pack-objects に渡します。
これにより、既存のデルタが破棄され、再計算に時間がかかりますが、再計算に時間がかかります。

この影響はほとんど持続的です。パックとゆるいオブジェクトが互いに合体すると、そのパック内の既存のデルタが再利用される可能性がありますが、代わりに新しいパックから次善のデルタを選択するさまざまなケースもあります。

さらに、--aggressiveを指定すると、 --depth に渡される--windowおよびgit-repackオプションが調整されます。
以下のgc.aggressiveDepthおよびgc.aggressiveWindowの設定を参照してください。
より大きなウィンドウサイズを使用することで、より最適なデルタを見つける可能性が高くなります。

おそらく、カスタマイズされたパフォーマンスベンチマークを実行せずに、特定のリポジトリでこのオプションを使用する価値はありません
さらに時間がかかり、結果として生じるスペース/デルタの最適化には価値がある場合とない場合があります。これをまったく使用しないことは、ほとんどのユーザーとそのリポジトリにとって正しいトレードオフです。

そして( コミット080a448 ):

gc docs:--aggressive--window--depthに与える影響に注意してください

7e7dbfgc:50、2016-08-11、Git v2.10.1のデフォルトのアグレッシブ深度)なので、--aggressiveの下でデフォルトと同じように同じ深さを多少混乱させて使用します。

意味のあるコミットで述べたように、「アグレッシブ」のデフォルトをより深くすることは間違っていて、実行時のパフォーマンスを犠牲にしてディスクスペースを節約します。これは通常「アグレッシブgc」を好む人の反対です欲求。

3
VonC