jugyo-grit 2.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,137 @@
1
+ #
2
+ # converted from the gitrb project
3
+ #
4
+ # authors:
5
+ # Matthias Lederhofer <matled@gmx.net>
6
+ # Simon 'corecode' Schubert <corecode@fs.ei.tum.de>
7
+ # Scott Chacon <schacon@gmail.com>
8
+ #
9
+ # provides native ruby access to git objects and pack files
10
+ #
11
+
12
+ require 'zlib'
13
+ require 'digest/sha1'
14
+ require 'grit/git-ruby/internal/raw_object'
15
+
16
+ module Grit
17
+ module GitRuby
18
+ module Internal
19
+ class LooseObjectError < StandardError
20
+ end
21
+
22
+ class LooseStorage
23
+ def initialize(directory)
24
+ @directory = directory
25
+ end
26
+
27
+ def [](sha1)
28
+ sha1 = sha1.unpack("H*")[0]
29
+ begin
30
+ return nil unless sha1[0...2] && sha1[2..39]
31
+ path = @directory + '/' + sha1[0...2] + '/' + sha1[2..39]
32
+ get_raw_object(open(path, 'rb') { |f| f.read })
33
+ rescue Errno::ENOENT
34
+ nil
35
+ end
36
+ end
37
+
38
+ def get_raw_object(buf)
39
+ if buf.bytesize < 2
40
+ raise LooseObjectError, "object file too small"
41
+ end
42
+
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.bytesize != size
60
+ return RawObject.new(type, content)
61
+ end
62
+
63
+ # currently, I'm using the legacy format because it's easier to do
64
+ # this function takes content and a type and writes out the loose object and returns a sha
65
+ def put_raw_object(content, type)
66
+ size = content.bytesize.to_s
67
+ LooseStorage.verify_header(type, size)
68
+
69
+ header = "#{type} #{size}\0"
70
+ store = header + content
71
+
72
+ sha1 = Digest::SHA1.hexdigest(store)
73
+ path = @directory+'/'+sha1[0...2]+'/'+sha1[2..40]
74
+
75
+ if !File.exists?(path)
76
+ content = Zlib::Deflate.deflate(store)
77
+
78
+ FileUtils.mkdir_p(@directory+'/'+sha1[0...2])
79
+ File.open(path, 'wb') do |f|
80
+ f.write content
81
+ end
82
+ end
83
+ return sha1
84
+ end
85
+
86
+ # simply figure out the sha
87
+ def self.calculate_sha(content, type)
88
+ size = content.bytesize.to_s
89
+ verify_header(type, size)
90
+ header = "#{type} #{size}\0"
91
+ store = header + content
92
+
93
+ Digest::SHA1.hexdigest(store)
94
+ end
95
+
96
+ def self.verify_header(type, size)
97
+ if !%w(blob tree commit tag).include?(type) || size !~ /^\d+$/
98
+ raise LooseObjectError, "invalid object header"
99
+ end
100
+ end
101
+
102
+ # private
103
+ def unpack_object_header_gently(buf)
104
+ used = 0
105
+ c = buf.getord(used)
106
+ used += 1
107
+
108
+ type = (c >> 4) & 7;
109
+ size = c & 15;
110
+ shift = 4;
111
+ while c & 0x80 != 0
112
+ if buf.bytesize <= used
113
+ raise LooseObjectError, "object file too short"
114
+ end
115
+ c = buf.getord(used)
116
+ used += 1
117
+
118
+ size += (c & 0x7f) << shift
119
+ shift += 7
120
+ end
121
+ type = OBJ_TYPES[type]
122
+ if ![:blob, :tree, :commit, :tag].include?(type)
123
+ raise LooseObjectError, "invalid loose object type"
124
+ end
125
+ return [type, size, used]
126
+ end
127
+ private :unpack_object_header_gently
128
+
129
+ def legacy_loose_object?(buf)
130
+ word = (buf.getord(0) << 8) + buf.getord(1)
131
+ buf.getord(0) == 0x78 && word % 31 == 0
132
+ end
133
+ private :legacy_loose_object?
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,391 @@
1
+ #
2
+ # converted from the gitrb project
3
+ #
4
+ # authors:
5
+ # Matthias Lederhofer <matled@gmx.net>
6
+ # Simon 'corecode' Schubert <corecode@fs.ei.tum.de>
7
+ # Scott Chacon <schacon@gmail.com>
8
+ #
9
+ # provides native ruby access to git objects and pack files
10
+ #
11
+
12
+ require 'zlib'
13
+ require 'grit/git-ruby/internal/raw_object'
14
+ require 'grit/git-ruby/internal/file_window'
15
+
16
+ PACK_SIGNATURE = "PACK"
17
+ PACK_IDX_SIGNATURE = "\377tOc"
18
+
19
+ module Grit
20
+ module GitRuby
21
+ module Internal
22
+ class PackFormatError < StandardError
23
+ end
24
+
25
+ class PackStorage
26
+ OBJ_OFS_DELTA = 6
27
+ OBJ_REF_DELTA = 7
28
+
29
+ FanOutCount = 256
30
+ SHA1Size = 20
31
+ IdxOffsetSize = 4
32
+ OffsetSize = 4
33
+ CrcSize = 4
34
+ OffsetStart = FanOutCount * IdxOffsetSize
35
+ SHA1Start = OffsetStart + OffsetSize
36
+ EntrySize = OffsetSize + SHA1Size
37
+ EntrySizeV2 = SHA1Size + CrcSize + OffsetSize
38
+
39
+ def initialize(file)
40
+ if file =~ /\.idx$/
41
+ file = file[0...-3] + 'pack'
42
+ end
43
+ @name = file
44
+ @cache = {}
45
+ init_pack
46
+ end
47
+
48
+ def with_idx(index_file = nil)
49
+ index_file ||= @name[0...-4] + 'idx'
50
+
51
+ begin
52
+ idxfile = File.open(index_file, 'rb')
53
+ rescue Errno::ENOENT => boom
54
+ # file went away. bail out without yielding.
55
+ return
56
+ end
57
+
58
+ # read header
59
+ sig = idxfile.read(4)
60
+ ver = idxfile.read(4).unpack("N")[0]
61
+
62
+ if sig == PACK_IDX_SIGNATURE
63
+ if(ver != 2)
64
+ raise PackFormatError, "pack #@name has unknown pack file version #{ver}"
65
+ end
66
+ @version = 2
67
+ else
68
+ @version = 1
69
+ end
70
+
71
+ idx = FileWindow.new(idxfile, @version)
72
+ yield idx
73
+ idx.unmap
74
+ ensure
75
+ idxfile.close if idxfile
76
+ end
77
+
78
+ def with_packfile
79
+ begin
80
+ packfile = File.open(@name, 'rb')
81
+ rescue Errno::ENOENT
82
+ # file went away. bail out without yielding.
83
+ return
84
+ end
85
+ yield packfile
86
+ ensure
87
+ packfile.close if packfile
88
+ end
89
+
90
+ def cache_objects
91
+ @cache = {}
92
+ with_packfile do |packfile|
93
+ each_entry do |sha, offset|
94
+ data, type = unpack_object(packfile, offset, {:caching => true})
95
+ if data
96
+ @cache[sha] = RawObject.new(OBJ_TYPES[type], data)
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ def name
103
+ @name
104
+ end
105
+
106
+ def close
107
+ # shouldnt be anything open now
108
+ end
109
+
110
+ # given an index file, list out the shas that it's packfile contains
111
+ def get_shas
112
+ shas = []
113
+ each_sha1 { |sha| shas << sha.unpack("H*")[0] }
114
+ shas
115
+ end
116
+
117
+ def [](sha1)
118
+ if obj = @cache[sha1]
119
+ return obj
120
+ end
121
+
122
+ offset = find_object(sha1)
123
+ return nil if !offset
124
+ @cache[sha1] = obj = parse_object(offset)
125
+ return obj
126
+ end
127
+
128
+ def init_pack
129
+ with_idx do |idx|
130
+ @offsets = [0]
131
+ FanOutCount.times do |i|
132
+ pos = idx[i * IdxOffsetSize,IdxOffsetSize].unpack('N')[0]
133
+ if pos < @offsets[i]
134
+ raise PackFormatError, "pack #@name has discontinuous index #{i}"
135
+ end
136
+ @offsets << pos
137
+ end
138
+ @size = @offsets[-1]
139
+ end
140
+ end
141
+
142
+ def each_entry
143
+ with_idx do |idx|
144
+ if @version == 2
145
+ data = read_data_v2(idx)
146
+ data.each do |sha1, crc, offset|
147
+ yield sha1, offset
148
+ end
149
+ else
150
+ pos = OffsetStart
151
+ @size.times do
152
+ offset = idx[pos,OffsetSize].unpack('N')[0]
153
+ sha1 = idx[pos+OffsetSize,SHA1Size]
154
+ pos += EntrySize
155
+ yield sha1, offset
156
+ end
157
+ end
158
+ end
159
+ end
160
+
161
+ def read_data_v2(idx)
162
+ data = []
163
+ pos = OffsetStart
164
+ @size.times do |i|
165
+ data[i] = [idx[pos,SHA1Size], 0, 0]
166
+ pos += SHA1Size
167
+ end
168
+ @size.times do |i|
169
+ crc = idx[pos,CrcSize]
170
+ data[i][1] = crc
171
+ pos += CrcSize
172
+ end
173
+ @size.times do |i|
174
+ offset = idx[pos,OffsetSize].unpack('N')[0]
175
+ data[i][2] = offset
176
+ pos += OffsetSize
177
+ end
178
+ data
179
+ end
180
+ private :read_data_v2
181
+
182
+ def each_sha1
183
+ with_idx do |idx|
184
+ if @version == 2
185
+ data = read_data_v2(idx)
186
+ data.each do |sha1, crc, offset|
187
+ yield sha1
188
+ end
189
+ else
190
+ pos = SHA1Start
191
+ @size.times do
192
+ sha1 = idx[pos,SHA1Size]
193
+ pos += EntrySize
194
+ yield sha1
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ def find_object_in_index(idx, sha1)
201
+ slot = sha1.getord(0)
202
+ return nil if !slot
203
+ first, last = @offsets[slot,2]
204
+ while first < last
205
+ mid = (first + last) / 2
206
+ if @version == 2
207
+ midsha1 = idx[OffsetStart + (mid * SHA1Size), SHA1Size]
208
+ cmp = midsha1 <=> sha1
209
+
210
+ if cmp < 0
211
+ first = mid + 1
212
+ elsif cmp > 0
213
+ last = mid
214
+ else
215
+ pos = OffsetStart + (@size * (SHA1Size + CrcSize)) + (mid * OffsetSize)
216
+ offset = idx[pos, OffsetSize].unpack('N')[0]
217
+ return offset
218
+ end
219
+ else
220
+ midsha1 = idx[SHA1Start + mid * EntrySize,SHA1Size]
221
+ cmp = midsha1 <=> sha1
222
+
223
+ if cmp < 0
224
+ first = mid + 1
225
+ elsif cmp > 0
226
+ last = mid
227
+ else
228
+ pos = OffsetStart + mid * EntrySize
229
+ offset = idx[pos,OffsetSize].unpack('N')[0]
230
+ return offset
231
+ end
232
+ end
233
+ end
234
+ nil
235
+ end
236
+
237
+ def find_object(sha1)
238
+ obj = nil
239
+ with_idx do |idx|
240
+ obj = find_object_in_index(idx, sha1)
241
+ end
242
+ obj
243
+ end
244
+ private :find_object
245
+
246
+ def parse_object(offset)
247
+ obj = nil
248
+ with_packfile do |packfile|
249
+ data, type = unpack_object(packfile, offset)
250
+ obj = RawObject.new(OBJ_TYPES[type], data)
251
+ end
252
+ obj
253
+ end
254
+ protected :parse_object
255
+
256
+ def unpack_object(packfile, offset, options = {})
257
+ obj_offset = offset
258
+ packfile.seek(offset)
259
+
260
+ c = packfile.read(1).getord(0)
261
+ size = c & 0xf
262
+ type = (c >> 4) & 7
263
+ shift = 4
264
+ offset += 1
265
+ while c & 0x80 != 0
266
+ c = packfile.read(1).getord(0)
267
+ size |= ((c & 0x7f) << shift)
268
+ shift += 7
269
+ offset += 1
270
+ end
271
+
272
+ return [false, false] if !(type == OBJ_COMMIT || type == OBJ_TREE) && options[:caching]
273
+
274
+ case type
275
+ when OBJ_OFS_DELTA, OBJ_REF_DELTA
276
+ data, type = unpack_deltified(packfile, type, offset, obj_offset, size, options)
277
+ #puts type
278
+ when OBJ_COMMIT, OBJ_TREE, OBJ_BLOB, OBJ_TAG
279
+ data = unpack_compressed(offset, size)
280
+ else
281
+ raise PackFormatError, "invalid type #{type}"
282
+ end
283
+ [data, type]
284
+ end
285
+ private :unpack_object
286
+
287
+ def unpack_deltified(packfile, type, offset, obj_offset, size, options = {})
288
+ packfile.seek(offset)
289
+ data = packfile.read(SHA1Size)
290
+
291
+ if type == OBJ_OFS_DELTA
292
+ i = 0
293
+ c = data.getord(i)
294
+ base_offset = c & 0x7f
295
+ while c & 0x80 != 0
296
+ c = data.getord(i += 1)
297
+ base_offset += 1
298
+ base_offset <<= 7
299
+ base_offset |= c & 0x7f
300
+ end
301
+ base_offset = obj_offset - base_offset
302
+ offset += i + 1
303
+ else
304
+ base_offset = find_object(data)
305
+ offset += SHA1Size
306
+ end
307
+
308
+ base, type = unpack_object(packfile, base_offset)
309
+
310
+ return [false, false] if !(type == OBJ_COMMIT || type == OBJ_TREE) && options[:caching]
311
+
312
+ delta = unpack_compressed(offset, size)
313
+ [patch_delta(base, delta), type]
314
+ end
315
+ private :unpack_deltified
316
+
317
+ def unpack_compressed(offset, destsize)
318
+ outdata = ""
319
+ with_packfile do |packfile|
320
+ packfile.seek(offset)
321
+ zstr = Zlib::Inflate.new
322
+ while outdata.size < destsize
323
+ indata = packfile.read(4096)
324
+ if indata.size == 0
325
+ raise PackFormatError, 'error reading pack data'
326
+ end
327
+ outdata << zstr.inflate(indata)
328
+ end
329
+ if outdata.size > destsize
330
+ raise PackFormatError, 'error reading pack data'
331
+ end
332
+ zstr.close
333
+ end
334
+ outdata
335
+ end
336
+ private :unpack_compressed
337
+
338
+ def patch_delta(base, delta)
339
+ src_size, pos = patch_delta_header_size(delta, 0)
340
+ if src_size != base.size
341
+ raise PackFormatError, 'invalid delta data'
342
+ end
343
+
344
+ dest_size, pos = patch_delta_header_size(delta, pos)
345
+ dest = ""
346
+ while pos < delta.size
347
+ c = delta.getord(pos)
348
+ pos += 1
349
+ if c & 0x80 != 0
350
+ pos -= 1
351
+ cp_off = cp_size = 0
352
+ cp_off = delta.getord(pos += 1) if c & 0x01 != 0
353
+ cp_off |= delta.getord(pos += 1) << 8 if c & 0x02 != 0
354
+ cp_off |= delta.getord(pos += 1) << 16 if c & 0x04 != 0
355
+ cp_off |= delta.getord(pos += 1) << 24 if c & 0x08 != 0
356
+ cp_size = delta.getord(pos += 1) if c & 0x10 != 0
357
+ cp_size |= delta.getord(pos += 1) << 8 if c & 0x20 != 0
358
+ cp_size |= delta.getord(pos += 1) << 16 if c & 0x40 != 0
359
+ cp_size = 0x10000 if cp_size == 0
360
+ pos += 1
361
+ dest << base[cp_off,cp_size]
362
+ elsif c != 0
363
+ dest << delta[pos,c]
364
+ pos += c
365
+ else
366
+ raise PackFormatError, 'invalid delta data'
367
+ end
368
+ end
369
+ dest
370
+ end
371
+ private :patch_delta
372
+
373
+ def patch_delta_header_size(delta, pos)
374
+ size = 0
375
+ shift = 0
376
+ begin
377
+ c = delta.getord(pos)
378
+ if c == nil
379
+ raise PackFormatError, 'invalid delta header'
380
+ end
381
+ pos += 1
382
+ size |= (c & 0x7f) << shift
383
+ shift += 7
384
+ end while c & 0x80 != 0
385
+ [size, pos]
386
+ end
387
+ private :patch_delta_header_size
388
+ end
389
+ end
390
+ end
391
+ end