gitの履歴で大規模なコミットを検索/識別する方法は?


366

300 MBのgitリポジトリがあります。現在チェックアウトされているファイルの合計サイズは2 MBで、残りのgitリポジトリの合計サイズは298 MBです。これは基本的にコードのみのリポジトリであり、数MBを超えてはなりません。

誰かが誤っていくつかの大きなファイル(ビデオ、画像など)をコミットしてから削除したのではないかと思いますが、gitからではないため、履歴にはまだ役に立たない大きなファイルが含まれています。git履歴で大きなファイルを見つけるにはどうすればよいですか?400以上のコミットがあるため、1つずつ実行するのは現実的ではありません。

:私の質問、ファイルを削除する方法ではなく、そもそもファイル見つける方法についてです。



回答:


143

このスクリプトは、過去にgitリポジトリで大きな(そして自明ではない)オブジェクトを見つけるのに非常に役立つことがわかりました。


#!/bin/bash
#set -x 

# Shows you the largest objects in your repo's pack file.
# Written for osx.
#
# @see https://stubbisms.wordpress.com/2009/07/10/git-script-to-show-largest-pack-objects-and-trim-your-waist-line/
# @author Antony Stubbs

# set the internal field separator to line break, so that we can iterate easily over the verify-pack output
IFS=$'\n';

# list all objects including their size, sort by size, take top 10
objects=`git verify-pack -v .git/objects/pack/pack-*.idx | grep -v chain | sort -k3nr | head`

echo "All sizes are in kB's. The pack column is the size of the object, compressed, inside the pack file."

output="size,pack,SHA,location"
allObjects=`git rev-list --all --objects`
for y in $objects
do
    # extract the size in bytes
    size=$((`echo $y | cut -f 5 -d ' '`/1024))
    # extract the compressed size in bytes
    compressedSize=$((`echo $y | cut -f 6 -d ' '`/1024))
    # extract the SHA
    sha=`echo $y | cut -f 1 -d ' '`
    # find the objects location in the repository tree
    other=`echo "${allObjects}" | grep $sha`
    #lineBreak=`echo -e "\n"`
    output="${output}\n${size},${compressedSize},${other}"
done

echo -e $output | column -t -s ', '

これにより、BLOBのオブジェクト名(SHA1sum)が得られ、次のようなスクリプトを使用できます。

...これらの各BLOBを指すコミットを検索します。


31
この回答は、私を上記の投稿に送ったので、本当に役に立ちました。投稿のスクリプトは機能しましたが、非常に遅いことがわかりました。だから私はそれを書き直しましたが、今では大規模なリポジトリで大幅に高速になっています。ご覧
Nick K9

7
オフサイトのリンクだけでなく、完全な指示を回答に含めてください。stubbisms.wordpress.comが必然的にダウンした場合、どうしますか?
ThorSummoner 2014

@ NickK9興味深いことに、私はあなたのスクリプトとその他から異なる出力を取得します。あなたが見逃しているように見える大きなオブジェクトがたくさんあります。行方不明のものはありますか?
UpAndAdam

かっこいい、イケてる!スクリプトを高速化してくれてありがとう@nick \ k9:D @UpAndAdam、私のスクリプトが誤った出力を生成したと言っていますか?
Antony Stubbs

1
これらのコメントは、サイズをバイト単位で報告しているように聞こえますが、私はキロバイトを取得します。
Kat、2017年

683

shell非常に高速なシェルワンライナー🚀

このシェルスクリプトは、リポジトリ内のすべてのblobオブジェクトを最小から最大にソートして表示します。

私のサンプルリポジトリでは、ここにある他のリポジトリよりも約100倍速く実行されました。
私の信頼できるAthlon II X4システムでは、560万のオブジェクトを持つLinuxカーネルリポジトリ1分強で処理します。

基本スクリプト

git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| sed -n 's/^blob //p' \
| sort --numeric-sort --key=2 \
| cut -c 1-12,41- \
| $(command -v gnumfmt || echo numfmt) --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest

上記のコードを実行すると、次のような人間が読める形式の出力が得られます。

...
0d99bb931299  530KiB path/to/some-image.jpg
2ba44098e28f   12MiB path/to/hires-image.png
bd1741ddce0d   63MiB path/to/some-video-1080p.mp4

MacOSのユーザー:ので、numfmtMacOSで利用できない、あなたはどちらかの生のバイトサイズの最後の行と取引を省略するか、またはすることができますbrew install coreutils

フィルタリング

達成するために、さらにフィルタリングを、次の行のいずれかを挿入する前に、sortラインを

に存在するファイルHEAD除外するには、次の行を挿入します。

| grep -vF --file=<(git ls-tree -r HEAD | awk '{print $3}') \

指定されたサイズ(1 MiB = 2 20  Bなど)を超えるファイルのみ表示するには、次の行を挿入します。

| awk '$2 >= 2^20' \

コンピュータの出力

コンピューターでさらに処理するのにより適した出力を生成するには、基本スクリプトの最後の2行を省略します。彼らはすべてのフォーマットを行います。これにより、次のようなものが残ります。

...
0d99bb93129939b72069df14af0d0dbda7eb6dba 542455 path/to/some-image.jpg
2ba44098e28f8f66bac5e21210c2774085d2319b 12446815 path/to/hires-image.png
bd1741ddce0d07b72ccf69ed281e09bf8a2d0b2f 65183843 path/to/some-video-1080p.mp4

ファイルの削除

実際のファイルの削除については、このトピックに関するSOの質問を確認してください。


14
これは私の賛成票以上の価値があります!コンピュータと人間が読める形式の両方の出力を提供してくれたことに感謝します。
ミシェルユング

2
これは非常に高速で使いやすいです!
Chin

32
Macでこれを使用するには、する必要がありbrew install coreutils、次に置き換えるcutgcutしてnumfmtgnumfmt
Nick Sweeting

2
もう一度強調させてください-これは、私が見た他のすべてのリストよりもはるかに高速です。
Sridhar Sarnobat 2017年

4
これは素晴らしいgitエイリアスになります:) git large誰か?
anarcat

160

ETHチューリッヒ物理学wikiページ(そのページの終わり近く)でワンライナーソリューションを見つけました。git gc古いジャンクを削除するには、

git rev-list --objects --all \
  | grep "$(git verify-pack -v .git/objects/pack/*.idx \
           | sort -k 3 -n \
           | tail -10 \
           | awk '{print$1}')"

リポジトリ内の最大10個のファイルが表示されます。

レイジーソリューションも利用可能になりました。GitExtensionsには、UIでこれを実行するプラグインが含まれています(履歴の書き換えも処理します)。

GitExtensionsの「大きなファイルを探す」ダイアログ


8
このワンライナーは、単一の最大のファイルを取得する場合にのみ機能します(つまり、tail -1を使用します)。改行は何か大きなものの邪魔になります。:あなたはgrepのは、素敵な再生されますので、改行を変換するためにsedを使用することができますgit rev-list --objects --all | grep -E `git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -10 | awk '{print$1}' | sed ':a;N;$!ba;s/\n/|/g'`
Throctukes

10
grep:a70783fca9bfbec1ade1519a41b6cc4ee36faea0:そのようなファイルやディレクトリはありません
Jonathan Allard


11
GitExtensionsを見つけることは、金の壺と虹の終わりを見つけるようなものです-ありがとう!
ckapilla 2016年

3
ファイルのサイズを印刷する拡張機能もありますか?
マイケル

27

ステップ1すべてのファイルSHA1をテキストファイルに書き込みます。

git rev-list --objects --all | sort -k 2 > allfileshas.txt

ステップ2 blobを最大のものから最小のものに並べ替え、結果をテキストファイルに書き込みます。

git gc && git verify-pack -v .git/objects/pack/pack-*.idx | egrep "^\w+ blob\W+[0-9]+ [0-9]+ [0-9]+$" | sort -k 3 -n -r > bigobjects.txt

ステップ3a両方のテキストファイルを結合して、ファイル名/ sha1 /サイズ情報を取得します。

for SHA in `cut -f 1 -d\  < bigobjects.txt`; do
echo $(grep $SHA bigobjects.txt) $(grep $SHA allfileshas.txt) | awk '{print $1,$3,$7}' >> bigtosmall.txt
done;

ステップ3bファイル名またはスペースを含むパス名がある場合は、このバリエーションのステップ3aを試してください。cut代わりにawkを使用して、必要な列を含めます。7桁目から行末までのスペース:

for SHA in `cut -f 1 -d\  < bigobjects.txt`; do
echo $(grep $SHA bigobjects.txt) $(grep $SHA allfileshas.txt) | cut -d ' ' -f'1,3,7-' >> bigtosmall.txt
done;

これで、bigtosmall.txtファイルを確認して、Git履歴から削除するファイルを決定できます。

ステップ4削除を実行するには(特定したファイルに関するデータについて履歴内のすべてのコミットを検査するため、この部分は時間がかかることに注意してください):

git filter-branch --tree-filter 'rm -f myLargeFile.log' HEAD

ソース

手順1〜3aは、Git履歴からの大きなファイルの検索と削除からコピーされました

編集

記事は2017年の後半に削除されましたが、アーカイブされたコピーには引き続きウェイバックマシンを使用してアクセスできます。


6
同じことを行うライナー:git gc && join -e ERROR -a 2 -j 1 -o 2.1,2.3,1.2 --check-order <( git rev-list --objects --all | sort -k 1 ) <( git verify-pack -v .git/objects/pack/pack-*.idx | gawk '( NF == 5 && $2 == "blob" ){print}' | sort -k1 ) | sort -k2gr
Iwan Aucamp

1
@イワン、ワンライナーをありがとう!スペースが含まれているファイル名は処理しませんjoin -t' ' -e ERROR -a 2 -j 1 -o 2.1,2.3,1.2 --check-order <( git rev-list --objects --all | sed 's/[[:space:]]/\t/' | sort -k 1 ) <( git verify-pack -v .git/objects/pack/pack-*.idx | gawk '( NF == 5 && $2 == "blob" ){print}' | sort -k1 | sed 's/[[:space:]]\+/\t/g' ) | sort -k2gr | less。これは次のようです。geekbraindump.blogspot.ru/2009/04/unix-join-with-tabs.htmljoin -t'
Nickolay

2
@Nickolayとbash $'\t'でタブが表示されます。echo -n $'\t' | xxd -ps->09
Iwan Aucamp、2015

1
@IwanAucamp:チップをありがとう!(残念ながら、前のコメントは編集できません。まあ。)
Nickolay

1
@ Sridhar-Sarnobat記事はWayback Machineによって保存されました!:) web.archive.org/web/20170621125743/http://www.naleid.com/blog/...
friederbluemle

18

BFG Repo-Cleanerを使用する必要があります。

ウェブサイトによると:

BFGは、Gitリポジトリの履歴から不良データをクレンジングするためのgit-filter-branchのシンプルで高速な代替手段です。

  • Crazy Big Filesの削除
  • パスワード、資格情報、その他のプライベートデータの削除

リポジトリのサイズを減らすための古典的な手順は次のとおりです。

git clone --mirror git://example.com/some-big-repo.git
java -jar bfg.jar --strip-biggest-blobs 500 some-big-repo.git
cd some-big-repo.git
git reflog expire --expire=now --all
git gc --prune=now --aggressive
git push

4
BFG Repo-Cleanerは非常に優れています。それは速く軽くなり、非常に確実に動作します。
fschmitt '20年

30
ただし、最大のファイルをすべてリストする方法はわかりません。
アンディジェイ

5
これの問題は、実際にそれらを削除せずに大きなファイルが何であるかを単に見ることができないことです。大きなファイルを一覧表示するだけの最初の予行演習なしでは、これを行うのは快適ではありません。
Sridhar Sarnobat

何をし--strip-biggest-blobs 500ますか?
2540625

gitはこのツールによる変更を拒否します。
クリストファー

9

大きなファイルのリストだけが必要な場合は、次のワンライナーを提供したいと思います。

join -o "1.1 1.2 2.3" <(git rev-list --objects --all | sort) <(git verify-pack -v objects/pack/*.idx | sort -k3 -n | tail -5 | sort) | sort -k3 -n

その出力は次のようになります。

commit       file name                                  size in bytes

72e1e6d20... db/players.sql 818314
ea20b964a... app/assets/images/background_final2.png 6739212
f8344b9b5... data_test/pg_xlog/000000010000000000000001 1625545
1ecc2395c... data_development/pg_xlog/000000010000000000000001 16777216
bc83d216d... app/assets/images/background_1forfinal.psd 95533848

リストの最後のエントリは、git履歴で最大のファイルを指します。

この出力を使用して、BFGで履歴に必要なものを削除していないことを確認できます。


2
驚くばかり!!ただし、このコマンドを実行する前に、-mirrorオプションを使用してリポジトリを複製する必要があることに注意してください。
アンディジェイ

気になり1.1, 1.2, 2.3ます、何の数字ですか?
ympostor

番号は<filenumber>.<field>組み合わせの順番を指定したリストです。詳細については、man.cx / joinを参照してください。
schmijos 2017年

6

Windowsを使用している場合は、リポジトリ内の最大の10個のファイルを出力するPowerShellスクリプトを次に示します。

$revision_objects = git rev-list --objects --all;
$files = $revision_objects.Split() | Where-Object {$_.Length -gt 0 -and $(Test-Path -Path $_ -PathType Leaf) };
$files | Get-Item -Force | select fullname, length | sort -Descending -Property Length | select -First 10

1
これにより、@ raphinesseとは異なる回答が生成され、私のリポジトリにある最大のファイルの束が欠落します。また、1つの大きなファイルに多くの変更が加えられている場合、最大サイズのみが報告されます。
kristianp 2017

このスクリプトは次のエラーで失敗しました:You cannot call a method on a null-valued expression. At line: 2 char: 1。ただし、この回答は機能しました:stackoverflow.com/a/57793716/2441655(これも短い)
Venryx

4

お試しくださいgit ls-files | xargs du -hs --threshold=1M

CIパイプラインで以下のコマンドを使用します。gitリポジトリに大きなファイルが見つかった場合は停止します。

test $(git ls-files | xargs du -hs --threshold=1M 2>/dev/null | tee /dev/stderr | wc -l) -gt 0 && { echo; echo "Aborting due to big files in the git repository."; exit 1; } || true

2

--batch-checkGit 1.8.3へのコマンドラインスイッチ(使用する必要があります)は引数を受け入れないため、最も一般的な回答を利用できませんでした。次の手順は、CentOS 6.5とBash 4.1.2で試行されました

主要な概念

Gitでは、ブロブという用語はファイルの内容を意味します。コミットにより、ファイルまたはパス名の内容が変更される場合があることに注意してください。したがって、コミットに応じて、同じファイルが異なるblobを参照する可能性があります。特定のファイルは、1つのコミットではディレクトリ階層で最大になる可能性がありますが、別のコミットでは最大ではありません。したがって、大きなファイルではなく大きなコミットを見つけるという問題は、問題を正しい見方にしています。

せっかちな人のために

サイズの降順でblobのリストを出力するコマンドは次のとおりです。

git cat-file --batch-check < <(git rev-list --all --objects  | \
awk '{print $1}')  | grep blob  | sort -n -r -k 3

出力例:

3a51a45e12d4aedcad53d3a0d4cf42079c62958e blob 305971200
7c357f2c2a7b33f939f9b7125b155adbd7890be2 blob 289163620

そのようなblobを削除するには、他の回答で述べられているように、BFG Repo Cleanerを使用します。blobs.txtblobハッシュのみを含むファイルがあるとします。次に例を示します。

3a51a45e12d4aedcad53d3a0d4cf42079c62958e
7c357f2c2a7b33f939f9b7125b155adbd7890be2

行う:

java -jar bfg.jar -bi blobs.txt <repo_dir>

問題は、BLOBを見つけるよりも作業が多いコミットを見つけることです。知って、読んでください。

今後の作業

コミットハッシュを指定すると、それに関連するすべてのオブジェクト(BLOBを含む)のハッシュを出力するコマンドは次のとおりです。

git ls-tree -r --full-tree <commit_hash>

したがって、リポジトリ内のすべてのコミットでこのような出力を利用できる場合、BLOBハッシュを指定すると、コミットの束がいずれかの出力に一致するものになります。このアイデアは、次のスクリプトにエンコードされています。

#!/bin/bash
DB_DIR='trees-db'

find_commit() {
    cd ${DB_DIR}
    for f in *; do
        if grep -q $1 ${f}; then
            echo ${f}
        fi
    done
    cd - > /dev/null
}

create_db() {
    local tfile='/tmp/commits.txt'
    mkdir -p ${DB_DIR} && cd ${DB_DIR}
    git rev-list --all > ${tfile}

    while read commit_hash; do
        if [[ ! -e ${commit_hash} ]]; then
            git ls-tree -r --full-tree ${commit_hash} > ${commit_hash}
        fi
    done < ${tfile}
    cd - > /dev/null
    rm -f ${tfile}
}

create_db

while read id; do
    find_commit ${id};
done

コンテンツがという名前のファイルに保存されている場合、find-commits.sh通常の呼び出しは次のようになります。

cat blobs.txt | find-commits.sh

以前と同様に、ファイルにblobs.txtは、1行に1つずつ、Blobハッシュがリストされています。このcreate_db()関数は、すべてのコミットリストのキャッシュを現在のディレクトリのサブディレクトリに保存します。

OSが24個の仮想コアとして提示する2つのIntel(R)Xeon(R)CPU E5-2620 2.00GHzプロセッサーを搭載したシステムでの私の実験からのいくつかの統計:

  • リポジトリ内のコミットの総数=ほぼ11,000
  • ファイル作成速度= 126ファイル/秒。スクリプトは、コミットごとに1つのファイルを作成します。これは、キャッシュが初めて作成されるときにのみ発生します。
  • キャッシュ作成オーバーヘッド= 87秒。
  • 平均検索速度= 522コミット/秒。キャッシュの最適化により、実行時間が80%短縮されました。

スクリプトはシングルスレッドであることに注意してください。したがって、一度に使用されるコアは1つだけです。


2

Windows GitのPowershellソリューション、最大のファイルを見つける:

git ls-tree -r -t -l --full-name HEAD | Where-Object {
 $_ -match '(.+)\s+(.+)\s+(.+)\s+(\d+)\s+(.*)'
 } | ForEach-Object {
 New-Object -Type PSObject -Property @{
     'col1'        = $matches[1]
     'col2'      = $matches[2]
     'col3' = $matches[3]
     'Size'      = [int]$matches[4]
     'path'     = $matches[5]
 }
 } | sort -Property Size -Top 10 -Descending

0

git履歴で大きなファイルを追跡するにはどうすればよいですか?

根本原因の分析、検証、選択から始めます。git-repo-analysis助けるために使用します。

また、BFG Repo-Cleanerによって生成された詳細なレポートでいくつかの値が見つかる場合があります。これは、10MiB /秒のネットワークスループットを使用してDigital Ocean液滴にクローンを作成することで非常に迅速に実行できます。


あなたはBFG提案で良い一般的な答えを持っていると思いますが、詳細を提供せずに別のサードパーティのサービスを使用することを提案することによって(また説明なしで)それを台無しにします。これをクリーンアップして、このBFGの使用法のコマンドライン例を提供できますか?
phord

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