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