diff options
Diffstat (limited to 'lib/git/raw/internal/loose.rb')
-rw-r--r-- | lib/git/raw/internal/loose.rb | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/lib/git/raw/internal/loose.rb b/lib/git/raw/internal/loose.rb new file mode 100644 index 0000000..0e4020c --- /dev/null +++ b/lib/git/raw/internal/loose.rb @@ -0,0 +1,96 @@ +require 'zlib' +require 'digest/sha1' + +require 'git/raw/internal/object' + +module Git module Raw module Internal + class LooseObjectError < StandardError + end + + class LooseStorage + def initialize(directory) + @directory = directory + end + + def [](sha1) + sha1 = sha1.unpack("H*")[0] + + path = @directory+'/'+sha1[0...2]+'/'+sha1[2..40] + begin + get_raw_object(File.read(path)) + rescue Errno::ENOENT + nil + end + end + + def get_raw_object(buf) + if buf.length < 2 + raise LooseObjectError, "object file too small" + end + + if legacy_loose_object?(buf) + content = Zlib::Inflate.inflate(buf) + header, content = content.split(/\0/, 2) + if !header || !content + raise LooseObjectError, "invalid object header" + end + type, size = header.split(/ /, 2) + if !%w(blob tree commit tag).include?(type) || size !~ /^\d+$/ + raise LooseObjectError, "invalid object header" + end + type = type.to_sym + size = size.to_i + else + type, size, used = unpack_object_header_gently(buf) + content = Zlib::Inflate.inflate(buf[used..-1]) + end + raise LooseObjectError, "size mismatch" if content.length != size + return RawObject.new(type, content) + end + + # private + def unpack_object_header_gently(buf) + used = 0 + c = buf[used] + used += 1 + + type = (c >> 4) & 7; + size = c & 15; + shift = 4; + while c & 0x80 != 0 + if buf.length <= used + raise LooseObjectError, "object file too short" + end + c = buf[used] + used += 1 + + size += (c & 0x7f) << shift + shift += 7 + end + type = OBJ_TYPES[type] + if ![:blob, :tree, :commit, :tag].include?(type) + raise LooseObjectError, "invalid loose object type" + end + return [type, size, used] + end + private :unpack_object_header_gently + + def legacy_loose_object?(buf) + word = (buf[0] << 8) + buf[1] + buf[0] == 0x78 && word % 31 == 0 + end + private :legacy_loose_object? + end +end end + +if $0 == __FILE__ + require 'find' + ARGV.each do |path| + storage = Git::Internal::LooseStorage.new(path) + Find.find(path) do |p| + next if !/\/([0-9a-f]{2})\/([0-9a-f]{38})$/.match(p) + obj = storage[[$1+$2].pack("H*")] + puts "%s %s" % [obj.sha1.unpack("H*")[0], obj.type] + end + end +end |