ブランチごとのgitリポジトリのスペース使用量を一覧表示する方法があるかどうか知っていますか?(好きdf
またはdu
そうする)
ブランチの「スペース使用量」とは、「リポジトリの他のブランチ間でまだ共有されていないコミットによって使用されるスペース」を意味します。
これには適切な答えがありません。特定のブランチにのみ含まれているコミットを見ると、blob(基本的にはファイルバージョン)のリストが表示されます。次に、これらのBLOBが他のブランチのコミットの一部であるかどうかを確認する必要があります。これを行うと、ブランチの一部にすぎないBLOBのリストが表示されます。
これで、これらのブロブのサイズを合計して結果を得ることができますが、それはおそらく非常に間違っているでしょう。Gitはこれらのブロブを相互に圧縮するため、ブロブの実際のサイズは、リポジトリ内の他のブロブによって異なります。1000個のblobを削除できます。それぞれ10MBで、空きディスク容量は1kbだけです。
通常、大きなリポジトリサイズは、リポジトリ内の単一の大きなファイルが原因で発生します(そうでない場合は、おそらく何か間違ったことをしています:)。それらを見つける方法についての情報はここで見つけることができます:HEADに存在しないxメガバイトを超えるgitリポジトリでファイルを見つける
リポジトリのスペースのほとんどは、ファイルを含むBLOBによって使用されます。
ただし、blobが2つのブランチ(または同じコンテンツを持つ2つのファイル)で共有されている場合、blobは複製されません。リポジトリのサイズは、ブランチのサイズの合計とは考えられません。枝がとる空間という概念はありません。
また、小さなファイルの変更にかかるスペースを節約するための多くの圧縮があります。
通常、ブランチを切断すると、非常に小さく、予測できないスペースしか解放されません。
Gitは、コミットの有向非巡回グラフを維持し、(単純な意味で)各コミットはディスクスペースを使い果たします。
すべてのブランチが最初のコミットから分岐しない限り、さまざまなブランチに共通のコミットがあります。つまり、各ブランチはある程度のディスクスペースを「共有」します。
これにより、共有される量と共有される他のブランチで修飾する必要があるため、ディスク使用量の「ブランチごと」の数値を提供することが困難になります。
そのようなものはすでに存在しないように思われるので、これが私がそのために行ったRubyスクリプトです。
#!/usr/bin/env ruby -w
require 'set'
display_branches = ARGV
packed_blobs = {}
class PackedBlob
attr_accessor :sha, :type, :size, :packed_size, :offset, :depth, :base_sha, :is_shared, :branch
def initialize(sha, type, size, packed_size, offset, depth, base_sha)
@sha = sha
@type = type
@size = size
@packed_size = packed_size
@offset = offset
@depth = depth
@base_sha = base_sha
@is_shared = false
@branch = nil
end
end
class Branch
attr_accessor :name, :blobs, :non_shared_size, :non_shared_packed_size, :shared_size, :shared_packed_size, :non_shared_dependable_size, :non_shared_dependable_packed_size
def initialize(name)
@name = name
@blobs = Set.new
@non_shared_size = 0
@non_shared_packed_size = 0
@shared_size = 0
@shared_packed_size = 0
@non_shared_dependable_size = 0
@non_shared_dependable_packed_size = 0
end
end
dependable_blob_shas = Set.new
# Collect every packed blobs information
for pack_idx in Dir[".git/objects/pack/pack-*.idx"]
IO.popen("git verify-pack -v #{pack_idx}", 'r') do |pack_list|
pack_list.each_line do |pack_line|
pack_line.chomp!
if not pack_line.include? "delta"
sha, type, size, packed_size, offset, depth, base_sha = pack_line.split(/\s+/, 7)
size = size.to_i
packed_size = packed_size.to_i
packed_blobs[sha] = PackedBlob.new(sha, type, size, packed_size, offset, depth, base_sha)
dependable_blob_shas.add(base_sha) if base_sha != nil
else
break
end
end
end
end
branches = {}
# Now check all blobs for every branches in order to determine whether it's shared between branches or not
IO.popen("git branch --list", 'r') do |branch_list|
branch_list.each_line do |branch_line|
# For each branch
branch_name = branch_line[2..-1].chomp
branch = Branch.new(branch_name)
branches[branch_name] = branch
IO.popen("git rev-list #{branch_name}", 'r') do |rev_list|
rev_list.each_line do |commit|
# Look into each commit in order to collect all the blobs used
for object in `git ls-tree -zrl #{commit}`.split("\0")
bits, type, sha, size, path = object.split(/\s+/, 5)
if type == 'blob'
blob = packed_blobs[sha]
branch.blobs.add(blob)
if not blob.is_shared
if blob.branch != nil and blob.branch != branch
# this blob has been used in another branch, let's set it to "shared"
blob.is_shared = true
blob.branch = nil
else
blob.branch = branch
end
end
end
end
end
end
end
end
# Now iterate on each branch to compute the space usage for each
branches.each_value do |branch|
branch.blobs.each do |blob|
if blob.is_shared
branch.shared_size += blob.size
branch.shared_packed_size += blob.packed_size
else
if dependable_blob_shas.include?(blob.sha)
branch.non_shared_dependable_size += blob.size
branch.non_shared_dependable_packed_size += blob.packed_size
else
branch.non_shared_size += blob.size
branch.non_shared_packed_size += blob.packed_size
end
end
end
# Now print it if wanted
if display_branches.empty? or display_branches.include?(branch.name)
puts "branch: %s" % branch.name
puts "\tnon shared:"
puts "\t\tpacked: %s" % branch.non_shared_packed_size
puts "\t\tnon packed: %s" % branch.non_shared_size
puts "\tnon shared but with dependencies on it:"
puts "\t\tpacked: %s" % branch.non_shared_dependable_packed_size
puts "\t\tnon packed: %s" % branch.non_shared_dependable_size
puts "\tshared:"
puts "\t\tpacked: %s" % branch.shared_packed_size
puts "\t\tnon packed: %s" % branch.shared_size, ""
end
end
これを使用すると、2Mo gitリポジトリに、他のブランチと共有されていない1Moのblobを取得する1つの役に立たないブランチがあることがわかりました。
今朝も同じ問題が発生し、簡単なスクリプトを作成しました。
for a in $(git branch -a | grep remotes | awk '{print $1}' | sed 's/remotes\/origin\///'); do echo -n ${a} -\ ; git clean -d -x -f > /dev/null 2>&1 ;git checkout ${a} > /dev/null 2>&1; du -hs -I --exclude-dir=.git .;done
これにより、コンテンツをリセットした後、すべてのリモートブランチがチェックアウトされ、クリーンにチェックアウトされます。.git
次に、ディレクトリなしのサイズが表示されます。
これで、大きなファイルが入ったブランチをプッシュした人を見つけることができました。
コミットされていないものはすべて消去されるため、別のクローンディレクトリでこれを行うことを忘れないでください
git2.3.1ではサポートしています--disk-usage
# reachable objects
git rev-list --disk-usage --objects --all