您知道是否有办法列出每个分支的 git 存储库的空间使用情况?(喜欢df
或du
愿意)
分支的“空间使用”是指“尚未在存储库的其他分支之间共享的提交使用的空间”。
这没有正确的答案。如果您查看仅包含在特定分支中的提交,您会得到一个 blob 列表(基本上是文件版本)。现在您必须检查这些 blob 是否是其他分支中任何提交的一部分。完成此操作后,您将拥有一个仅属于您的分支的 blob 列表。
现在你可以总结这些 blob 的大小来得到一个结果——但这可能是非常错误的。Git 将这些 blob 相互压缩,因此 blob 的实际大小取决于您的存储库中的其他 blob。您可以删除 1000 个 blob,每个 10MB,并且只释放 1kb 的磁盘空间。
通常一个大的 repo 大小是由 repo 中的单个大文件引起的(如果不是,你可能做错了什么:)。有关如何找到这些的信息可以在这里找到:Find files in git repo over x MB, that doesn't exist in HEAD
存储库的大部分空间被包含文件的 blob 占用。
但是当一个 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,没有与任何其他分支共享。
今天早上我遇到了同样的问题并写了一个快速脚本:
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
目录的大小。
有了这个,我就可以找到推送一个包含大文件的分支的人。
请记住在另一个克隆目录中执行此操作,因为它会清除所有未提交的内容
在 git 2.3.1 它支持--disk-usage
# reachable objects
git rev-list --disk-usage --objects --all