参照されていないblobをgitリポジトリから削除する方法


124

マスターとリリースの2つのブランチを持つGitHubリポジトリがあります。

リリースブランチには、非常に大きなレポサイズ(> 250MB)の原因となるバイナリ配布ファイルが含まれていたため、クリーンアップすることにしました。

まず、リモートリリースブランチを削除しました。 git push origin :release

次に、ローカルのリリースブランチを削除しました。最初にを試しましたgit branch -d releaseが、gitは「エラー:ブランチの 'リリース'は現在のHEADの祖先ではありません」と言っていました。これは正しいので、git branch -D release強制的に削除しました。

しかし、私のリポジトリのサイズは、ローカルでもGitHubでも、まだ巨大でした。それで、私はのようなgitコマンドの通常のリストを実行しましたが、git gc --prune=today --aggressiveうまくいきませんでした。

SO 1029969でのCharles Baileyの指示に従うことで、最大のブロブのSHA1のリストを取得できました。次に、SO 460331のスクリプトを使用し てブロブを検索しました。最大の5つは存在しませんが、小さいブロブが見つかったため、スクリプトは機能しています。

これらのブログはリリースブランチのバイナリであると思います。ブランチは削除されたため、どういうわけか残ってしまいました。それらを取り除くための正しい方法は何ですか?


使用しているGitのバージョンは何ですか?そして、あなたはstackoverflow.com/questions/1106529/…を試しましたか?
VonC、2009

gitバージョン1.6.2.3 gcを試し、さまざまな引数をプルーニングしました。私は-a -d -lの再パックを試みていませんでした。実行しただけで、変更はありません。
kkrugler、2009

2
新しい情報-GitHubからの新しいクローンには、参照されていないblobがなくなり、250MBから84MBまで「のみ」になりました。
kkrugler 2009

回答:


218

...そしてさらに面倒なことなく、この便利なコマンド「git-gc-all」を提示して、余分な構成変数が表示されるまですべてのgitガベージを削除することが保証されます。

git -c gc.reflogExpire=0 -c gc.reflogExpireUnreachable=0 -c gc.rerereresolved=0 -c gc.rerereunresolved=0 -c gc.pruneExpire=now gc

最初にこれらのようなものを実行する必要があるかもしれません、まあ、gitは複雑です!!

git remote rm origin
rm -rf .git/refs/original/ .git/refs/remotes/ .git/*_HEAD .git/logs/
git for-each-ref --format="%(refname)" refs/original/ | xargs -n1 --no-run-if-empty git update-ref -d

Zitraxのおかげで、いくつかのタグを削除する必要がある場合もあります。

git tag | xargs git tag -d

これをすべてgit-gc-all-ferociousというスクリプトに入れました。


1
面白い。私のより一般的な答えの良い選択肢。+1
VonC 2013

10
これはより多くの賛成票に値します。最後に、他のメソッドが保持する多くのgitオブジェクトが削除されました。ありがとう!
Jean-Philippe Pellet 2013年

1
賛成。うわー、私は今何をしたのか分かりませんが、それはたくさん片付けているようです。それが何をするかについて詳しく説明できますか?私はそれが私のすべてを片付けた感じがありobjectsます。それらは何であり、なぜそれらは(明らかに)無関係であるのですか?
Redsandro 2014年

1
@Redsandro、私が理解しているように、これらの「git rm origin」、「rm」、および「git update-ref -d」コマンドは、リモートなどの古いコミットへの参照を削除するため、ガベージコレクションを妨げている可能性があります。「git gc」のオプションは、さまざまな古いコミットを保持しないように指示します。それ以外の場合は、しばらく保持されます。たとえば、gc.rerereresolvedは「以前に解決した競合したマージのレコード」用で、デフォルトでは60日間保持されます。これらのオプションはgit-gcのマンページにあります。私はgitの専門家ではないので、これらすべてのことの正確な意味がわかりません。私はそれらをマンページから見つけ、コミット参照のために.gitをgreppingしました。
サムワトキンス

1
gitオブジェクトは、履歴からの古いものを含む、圧縮ファイルまたはツリー、またはgitリポジトリ内のコミットです。git gcは不要なオブジェクトをクリアします。現在のリポジトリとその履歴に必要なオブジェクトを保持します。
Sam Watkins、

81

ここで説明されているようにreflog経由でのみ参照されるすべてを永久に削除したい場合は、単に

git reflog expire --expire-unreachable=now --all
git gc --prune=now

git reflog expire --expire-unreachable=now --allの到達不能コミットのすべての参照を削除しますreflog

git gc --prune=now コミット自体を削除します。

注意git gc --prune=nowこれらのコミットは依然としてreflogで参照されているため、使用のみでは機能しません。したがって、reflogのクリアは必須です。また、使用rerereする場合、これらのコマンドではクリアされない追加の参照があることに注意してください。詳細についてはgit help rerere、を参照してください。さらに、ローカルまたはリモートのブランチまたはタグによって参照されるコミットは、gitによって貴重なデータと見なされるため、削除されません。


14
それは機能しましたが、どういうわけか私はプロセス中に保存された隠し場所を失いました(私の場合、大きなことは何もありません、他の人への警告だけです)
Amro

1
なぜ-アグレッシブではないのですか?
JoelFan 2017

2
この答えには明確な警告が必要であり、できれば上部が必要です。コメントで著者に提案するべきだと思うので、私の編集提案は拒否されましたか?この編集を受け入れるか、stackoverflow.com / review / suggested-edits / 26023988か、独自の方法で警告を追加してください。また、これはすべての隠し場所をドロップします。これは警告でも言及されるべきです!
イニゴ

私はgitバージョン2.17でテストしましたが、隠されたコミットは上記のコマンドでは削除されません。追加のコマンドを実行していませんか?
ミッコランタライネン

1
git fetch --pruneローカルBLOBを削除するため、サイズをさらに削減します。
ヘクタール

33

このSOの回答で述べたように、git gc実際にはリポジトリのサイズを増やすことができます!

このスレッドも参照してください

現在、gitには、「」の実行時に参照されてないオブジェクトをすぐに削除しない安全メカニズムがありますgit gc
デフォルトでは、参照されていないオブジェクトは2週間保持されます。これは、誤って削除されたブランチやコミットを簡単に回復したり、作成中のオブジェクトがまだ参照されていないがgit gc、並列に実行されている ' 'プロセスによって削除されたりする可能性がある競合を回避するためです。

そのため、パックされたが参照されていないオブジェクトに猶予期間を与えるために、再パックプロセスは、参照されていないオブジェクトをパックからルーズフォームにプッシュして、古くなり、最終的にプルーニングできるようにします。
参照されなくなったオブジェクトは通常それほど多くありません。参照されていないオブジェクトを404855持つことは非常に多く、それらのオブジェクトを最初にクローン経由で送信するのは愚かであり、ネットワーク帯域幅を完全に浪費します。

とにかく...問題を解決するにgit gcは、--prune=now引数を指定して' ' を実行し、猶予期間を無効にして、参照されていないオブジェクトをすぐに削除する必要があります(他のgitアクティビティが同時に実行されていない場合にのみ安全ワークステーション上で簡単に確認できます)。

ところで、 ' git gc --aggressive'を新しいgitバージョン(または ' git repack -a -f -d --window=250 --depth=250')で使用します

同じスレッドが言及します

 git config pack.deltaCacheSize 1

これにより、デルタキャッシュサイズがデフォルトの0(無制限)ではなく、1バイト(事実上無効化)に制限されます。これにより、git repack4GBのRAMと4つのスレッド(これはクアッドコア)を使用するx86-64システムで上記のコマンドを使用して、リポジトリを再パックできます。ただし、常駐メモリの使用量は3.3GBにまで増加します。

マシンがSMPであり、十分なRAMがない場合は、スレッドの数を1つだけに減らすことができます。

git config pack.threads 1

さらに、メモリ使用量を--window-memory argumentto ' git repack' でさらに制限できます。
たとえば--window-memory=128M、レポジトリに大量のファイルが多数含まれている場合、最適なデルタの一致が得られない可能性がありますが、使用すると、デルタ検索のメモリ使用量に適切な上限が維持されます。


フィルターブランチの前では、このスクリプトを(慎重に)検討できます。

#!/bin/bash
set -o errexit

# Author: David Underhill
# Script to permanently delete files/folders from your git repository.  To use 
# it, cd to your repository's root and then run the script with a list of paths
# you want to delete, e.g., git-delete-history path1 path2

if [ $# -eq 0 ]; then
    exit 0
fi

# make sure we're at the root of git repo
if [ ! -d .git ]; then
    echo "Error: must run this script from the root of a git repository"
    exit 1
fi

# remove all paths passed as arguments from the history of the repo
files=$@
git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch $files" HEAD

# remove the temporary history git-filter-branch otherwise leaves behind for a long time
rm -rf .git/refs/original/ && git reflog expire --all &&  git gc --aggressive --prune

stackoverflow.com/questions/359424/…は、filter-branchコマンドの使用を開始するのにも適しています。
VonC、2009

こんにちはVonC-NIはgit gc prune = nowを試したが運が悪かった。ブランチの削除後にローカルで参照されていないblobが発生するという点で、gitバグのように見えますが、GitHubリポジトリの新しいクローンではこれらは存在しないため、ローカルリポジトリの問題にすぎません。しかし、私が消去したい追加のファイルがあるので、上記で参照したスクリプトは素晴らしいです-ありがとうございます!
kkrugler、2009


12

HEADが移動するたびに、gitはでこれを追跡しreflogます。コミットを削除しても、reflog30日間はから参照されているため、「ダングリングコミット」が残っています。これは、誤ってコミットを削除した場合のセーフティネットです。

git reflog特定のコミットの削除、再パックなどのコマンドを使用するか、高レベルのコマンドのみを使用できます。

git gc --prune=now

5

使用できますgit forget-blob

使い方はかなり簡単git forget-blob file-to-forgetです。あなたはここでより多くの情報を得ることができます

https://ownyourbits.com/2017/01/18/completely-remove-a-file-from-a-git-repository-with-git-forget-blob/

履歴、reflog、タグなどのすべてのコミットから消えます

私は時々同じ問題に遭遇し、この投稿や他の記事に戻る必要があるたびに、プロセスを自動化したのはそのためです。

Sam Watkinsなどの貢献者へのクレジット


2

git-filter-branchを使用してみてください。大きなblobは削除されませんが、指定した大きなファイルをリポジトリ全体から削除できます。私にとっては、リポジトリのサイズを数百MBから12 MBに減らしています。


6
今、それは怖いコマンドです:)私は私のgit-FUが強く感じたときにそれを試してみる必要があるでしょう。
kkrugler、2009

もう一度言うことができます。リポジトリの履歴を操作するコマンドには常に注意しています。複数の人がそのリポジトリーを押したり引いたりしていて、突然gitが期待している大量のオブジェクトがそこにないときに、物事は非常にうまくいかない傾向があります。
ジョナサンデュメイン

1

場合によっては、「gc」があまり役に立たない理由は、古いコミットに基づいて未完成のリベースまたはスタッシュがあるためです。


または、古いコミットがHEAD、ORIG_HEAD、FETCH_HEAD、reflog、またはgitが価値のあるものを失うことがないように自動的に試行し続けるその他の何かによって参照されています。あなたが本当にそれらすべてを失いたいのなら、あなたはそれをするためにさらに一マイル行く必要があります。
ミッコランタライネン

1

別のヒントを追加するには、git gcを使用する前に、git remote pruneを使用してリモートの古いブランチを削除することを忘れないでください

あなたはそれらをgitブランチ-aで見ることができます

githubやforkされたリポジトリから取得する場合に便利です...


1

git filter-branchおよびgit gcを実行する前に、リポジトリに存在するタグを確認する必要があります。継続的インテグレーションやデプロイメントなどの自動タグ付けを備えた実際のシステムでは、不要なオブジェクトが引き続きこれらのタグによって参照されるgcため、それらを削除することはできず、リポジトリのサイズが依然として非常に大きい理由について疑問を抱き続けることになります。

すべての未たかったものを取り除くための最善の方法は、実行することですgit-filtergit gcし、新しい裸のレポにマスターを押してください。新しい裸のレポはクリーンアップされたツリーになります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.