added Matthias and Simon to credits for the gitrb code
[rubygit.git] / lib / git / raw / internal / loose.rb
blob53d43344c013b6ef0758bc57d511f6d02362502a
2 # converted from the gitrb project
4 # authors: 
5 #    Matthias Lederhofer <matled@gmx.net>
6 #    Simon 'corecode' Schubert <corecode@fs.ei.tum.de>
8 # provides native ruby access to git objects and pack files
11 require 'zlib'
12 require 'digest/sha1'
14 require 'git/raw/internal/object'
16 module Git 
17   module Raw 
18     module Internal
19       class LooseObjectError < StandardError
20       end
22       class LooseStorage
23         def initialize(directory)
24           @directory = directory
25         end
27         def [](sha1)
28           sha1 = sha1.unpack("H*")[0]
30           path = @directory+'/'+sha1[0...2]+'/'+sha1[2..40]
31           begin
32             get_raw_object(File.read(path))
33           rescue Errno::ENOENT
34             nil
35           end
36         end
38         def get_raw_object(buf)
39           if buf.length < 2
40             raise LooseObjectError, "object file too small"
41           end
43           if legacy_loose_object?(buf)
44             content = Zlib::Inflate.inflate(buf)
45             header, content = content.split(/\0/, 2)
46             if !header || !content
47               raise LooseObjectError, "invalid object header"
48             end
49             type, size = header.split(/ /, 2)
50             if !%w(blob tree commit tag).include?(type) || size !~ /^\d+$/
51               raise LooseObjectError, "invalid object header"
52             end
53             type = type.to_sym
54             size = size.to_i
55           else
56             type, size, used = unpack_object_header_gently(buf)
57             content = Zlib::Inflate.inflate(buf[used..-1])
58           end
59           raise LooseObjectError, "size mismatch" if content.length != size
60           return RawObject.new(type, content)
61         end
63         # private
64         def unpack_object_header_gently(buf)
65           used = 0
66           c = buf[used]
67           used += 1
69           type = (c >> 4) & 7;
70           size = c & 15;
71           shift = 4;
72           while c & 0x80 != 0
73             if buf.length <= used
74               raise LooseObjectError, "object file too short"
75             end
76             c = buf[used]
77             used += 1
79             size += (c & 0x7f) << shift
80             shift += 7
81           end
82           type = OBJ_TYPES[type]
83           if ![:blob, :tree, :commit, :tag].include?(type)
84             raise LooseObjectError, "invalid loose object type"
85           end
86           return [type, size, used]
87         end
88         private :unpack_object_header_gently
90         def legacy_loose_object?(buf)
91           word = (buf[0] << 8) + buf[1]
92           buf[0] == 0x78 && word % 31 == 0
93         end
94         private :legacy_loose_object?
95       end
96     end 
97   end
98 end
100 if $0 == __FILE__
101   require 'find'
102   ARGV.each do |path|
103     storage = Git::Internal::LooseStorage.new(path)
104     Find.find(path) do |p|
105       next if !/\/([0-9a-f]{2})\/([0-9a-f]{38})$/.match(p)
106       obj = storage[[$1+$2].pack("H*")]
107       puts "%s %s" % [obj.sha1.unpack("H*")[0], obj.type]
108     end
109   end