0e4020cd58628e138147b5286c38fd9680b0c111
[rubygit.git] / lib / git / raw / internal / loose.rb
blob0e4020cd58628e138147b5286c38fd9680b0c111
1 require 'zlib'
2 require 'digest/sha1'
4 require 'git/raw/internal/object'
6 module Git module Raw module Internal
7   class LooseObjectError < StandardError
8   end
10   class LooseStorage
11     def initialize(directory)
12       @directory = directory
13     end
15     def [](sha1)
16       sha1 = sha1.unpack("H*")[0]
18       path = @directory+'/'+sha1[0...2]+'/'+sha1[2..40]
19       begin
20         get_raw_object(File.read(path))
21       rescue Errno::ENOENT
22         nil
23       end
24     end
26     def get_raw_object(buf)
27       if buf.length < 2
28         raise LooseObjectError, "object file too small"
29       end
31       if legacy_loose_object?(buf)
32         content = Zlib::Inflate.inflate(buf)
33         header, content = content.split(/\0/, 2)
34         if !header || !content
35           raise LooseObjectError, "invalid object header"
36         end
37         type, size = header.split(/ /, 2)
38         if !%w(blob tree commit tag).include?(type) || size !~ /^\d+$/
39           raise LooseObjectError, "invalid object header"
40         end
41         type = type.to_sym
42         size = size.to_i
43       else
44         type, size, used = unpack_object_header_gently(buf)
45         content = Zlib::Inflate.inflate(buf[used..-1])
46       end
47       raise LooseObjectError, "size mismatch" if content.length != size
48       return RawObject.new(type, content)
49     end
51     # private
52     def unpack_object_header_gently(buf)
53       used = 0
54       c = buf[used]
55       used += 1
57       type = (c >> 4) & 7;
58       size = c & 15;
59       shift = 4;
60       while c & 0x80 != 0
61         if buf.length <= used
62           raise LooseObjectError, "object file too short"
63         end
64         c = buf[used]
65         used += 1
67         size += (c & 0x7f) << shift
68         shift += 7
69       end
70       type = OBJ_TYPES[type]
71       if ![:blob, :tree, :commit, :tag].include?(type)
72         raise LooseObjectError, "invalid loose object type"
73       end
74       return [type, size, used]
75     end
76     private :unpack_object_header_gently
78     def legacy_loose_object?(buf)
79       word = (buf[0] << 8) + buf[1]
80       buf[0] == 0x78 && word % 31 == 0
81     end
82     private :legacy_loose_object?
83   end
84 end end
86 if $0 == __FILE__
87   require 'find'
88   ARGV.each do |path|
89     storage = Git::Internal::LooseStorage.new(path)
90     Find.find(path) do |p|
91       next if !/\/([0-9a-f]{2})\/([0-9a-f]{38})$/.match(p)
92       obj = storage[[$1+$2].pack("H*")]
93       puts "%s %s" % [obj.sha1.unpack("H*")[0], obj.type]
94     end
95   end
96 end