mojombo-grit 0.8.1 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/History.txt +7 -0
  2. data/Manifest.txt +18 -1
  3. data/grit.gemspec +56 -10
  4. data/lib/{mojombo-grit.rb → grit.rb} +20 -4
  5. data/lib/grit/commit.rb +32 -11
  6. data/lib/grit/commit_stats.rb +104 -0
  7. data/lib/grit/git-ruby.rb +182 -0
  8. data/lib/grit/git-ruby/commit_db.rb +52 -0
  9. data/lib/grit/git-ruby/file_index.rb +186 -0
  10. data/lib/grit/git-ruby/git_object.rb +344 -0
  11. data/lib/grit/git-ruby/internal/loose.rb +136 -0
  12. data/lib/grit/git-ruby/internal/mmap.rb +58 -0
  13. data/lib/grit/git-ruby/internal/pack.rb +382 -0
  14. data/lib/grit/git-ruby/internal/raw_object.rb +37 -0
  15. data/lib/grit/git-ruby/object.rb +319 -0
  16. data/lib/grit/git-ruby/repository.rb +729 -0
  17. data/lib/grit/git.rb +33 -15
  18. data/lib/grit/head.rb +6 -15
  19. data/lib/grit/index.rb +121 -0
  20. data/lib/grit/ref.rb +95 -0
  21. data/lib/grit/repo.rb +95 -6
  22. data/lib/grit/status.rb +151 -0
  23. data/lib/grit/tree.rb +3 -2
  24. data/test/test_blob.rb +5 -0
  25. data/test/test_commit.rb +7 -5
  26. data/test/test_diff.rb +1 -1
  27. data/test/test_git.rb +20 -2
  28. data/test/test_grit.rb +32 -0
  29. data/test/test_head.rb +30 -5
  30. data/test/test_real.rb +8 -6
  31. data/test/test_remote.rb +14 -0
  32. data/test/test_repo.rb +86 -79
  33. data/test/test_tag.rb +2 -6
  34. data/test/test_tree.rb +5 -0
  35. metadata +40 -40
  36. data/test/fixtures/blame +0 -131
  37. data/test/fixtures/cat_file_blob +0 -1
  38. data/test/fixtures/cat_file_blob_size +0 -1
  39. data/test/fixtures/diff_2 +0 -54
  40. data/test/fixtures/diff_2f +0 -19
  41. data/test/fixtures/diff_f +0 -15
  42. data/test/fixtures/diff_i +0 -201
  43. data/test/fixtures/diff_mode_only +0 -1152
  44. data/test/fixtures/diff_new_mode +0 -17
  45. data/test/fixtures/diff_p +0 -610
  46. data/test/fixtures/for_each_ref +0 -0
  47. data/test/fixtures/for_each_ref_tags +0 -0
  48. data/test/fixtures/ls_tree_a +0 -7
  49. data/test/fixtures/ls_tree_b +0 -2
  50. data/test/fixtures/ls_tree_commit +0 -3
  51. data/test/fixtures/rev_list +0 -26
  52. data/test/fixtures/rev_list_count +0 -655
  53. data/test/fixtures/rev_list_single +0 -7
  54. data/test/fixtures/rev_parse +0 -1
  55. data/test/fixtures/show_empty_commit +0 -6
  56. data/test/fixtures/simple_config +0 -2
  57. data/test/helper.rb +0 -17
  58. data/test/profile.rb +0 -21
  59. data/test/suite.rb +0 -6
@@ -0,0 +1,136 @@
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
+ path = @directory+'/'+sha1[0...2]+'/'+sha1[2..39]
31
+ get_raw_object(File.read(path))
32
+ rescue Errno::ENOENT
33
+ nil
34
+ end
35
+ end
36
+
37
+ def get_raw_object(buf)
38
+ if buf.length < 2
39
+ raise LooseObjectError, "object file too small"
40
+ end
41
+
42
+ if legacy_loose_object?(buf)
43
+ content = Zlib::Inflate.inflate(buf)
44
+ header, content = content.split(/\0/, 2)
45
+ if !header || !content
46
+ raise LooseObjectError, "invalid object header"
47
+ end
48
+ type, size = header.split(/ /, 2)
49
+ if !%w(blob tree commit tag).include?(type) || size !~ /^\d+$/
50
+ raise LooseObjectError, "invalid object header"
51
+ end
52
+ type = type.to_sym
53
+ size = size.to_i
54
+ else
55
+ type, size, used = unpack_object_header_gently(buf)
56
+ content = Zlib::Inflate.inflate(buf[used..-1])
57
+ end
58
+ raise LooseObjectError, "size mismatch" if content.length != size
59
+ return RawObject.new(type, content)
60
+ end
61
+
62
+ # currently, I'm using the legacy format because it's easier to do
63
+ # this function takes content and a type and writes out the loose object and returns a sha
64
+ def put_raw_object(content, type)
65
+ size = content.length.to_s
66
+ LooseStorage.verify_header(type, size)
67
+
68
+ header = "#{type} #{size}\0"
69
+ store = header + content
70
+
71
+ sha1 = Digest::SHA1.hexdigest(store)
72
+ path = @directory+'/'+sha1[0...2]+'/'+sha1[2..40]
73
+
74
+ if !File.exists?(path)
75
+ content = Zlib::Deflate.deflate(store)
76
+
77
+ FileUtils.mkdir_p(@directory+'/'+sha1[0...2])
78
+ File.open(path, 'w') do |f|
79
+ f.write content
80
+ end
81
+ end
82
+ return sha1
83
+ end
84
+
85
+ # simply figure out the sha
86
+ def self.calculate_sha(content, type)
87
+ size = content.length.to_s
88
+ verify_header(type, size)
89
+ header = "#{type} #{size}\0"
90
+ store = header + content
91
+
92
+ Digest::SHA1.hexdigest(store)
93
+ end
94
+
95
+ def self.verify_header(type, size)
96
+ if !%w(blob tree commit tag).include?(type) || size !~ /^\d+$/
97
+ raise LooseObjectError, "invalid object header"
98
+ end
99
+ end
100
+
101
+ # private
102
+ def unpack_object_header_gently(buf)
103
+ used = 0
104
+ c = buf[used]
105
+ used += 1
106
+
107
+ type = (c >> 4) & 7;
108
+ size = c & 15;
109
+ shift = 4;
110
+ while c & 0x80 != 0
111
+ if buf.length <= used
112
+ raise LooseObjectError, "object file too short"
113
+ end
114
+ c = buf[used]
115
+ used += 1
116
+
117
+ size += (c & 0x7f) << shift
118
+ shift += 7
119
+ end
120
+ type = OBJ_TYPES[type]
121
+ if ![:blob, :tree, :commit, :tag].include?(type)
122
+ raise LooseObjectError, "invalid loose object type"
123
+ end
124
+ return [type, size, used]
125
+ end
126
+ private :unpack_object_header_gently
127
+
128
+ def legacy_loose_object?(buf)
129
+ word = (buf[0] << 8) + buf[1]
130
+ buf[0] == 0x78 && word % 31 == 0
131
+ end
132
+ private :legacy_loose_object?
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,58 @@
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
+ module Grit
13
+ module GitRuby
14
+ module Internal
15
+ class Mmap
16
+ def initialize(file, version = 1)
17
+ @file = file
18
+ @offset = nil
19
+ if version == 2
20
+ @global_offset = 8
21
+ else
22
+ @global_offset = 0
23
+ end
24
+ end
25
+
26
+ def unmap
27
+ @file = nil
28
+ end
29
+
30
+ def [](*idx)
31
+ idx = idx[0] if idx.length == 1
32
+ case idx
33
+ when Range
34
+ offset = idx.first
35
+ len = idx.last - idx.first + idx.exclude_end? ? 0 : 1
36
+ when Fixnum
37
+ offset = idx
38
+ len = nil
39
+ when Array
40
+ offset, len = idx
41
+ else
42
+ raise RuntimeError, "invalid index param: #{idx.class}"
43
+ end
44
+ if @offset != offset
45
+ @file.seek(offset + @global_offset)
46
+ end
47
+ @offset = offset + len ? len : 1
48
+ if not len
49
+ @file.read(1)[0]
50
+ else
51
+ @file.read(len)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+
@@ -0,0 +1,382 @@
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/mmap'
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
+ if !index_file
50
+ index_file = @name
51
+ idxfile = File.open(@name[0...-4]+'idx')
52
+ else
53
+ idxfile = File.open(index_file)
54
+ end
55
+
56
+ # read header
57
+ sig = idxfile.read(4)
58
+ ver = idxfile.read(4).unpack("N")[0]
59
+
60
+ if sig == PACK_IDX_SIGNATURE
61
+ if(ver != 2)
62
+ raise PackFormatError, "pack #@name has unknown pack file version #{ver}"
63
+ end
64
+ @version = 2
65
+ else
66
+ @version = 1
67
+ end
68
+
69
+ idx = Mmap.new(idxfile, @version)
70
+ yield idx
71
+ idx.unmap
72
+ idxfile.close
73
+ end
74
+
75
+ def with_packfile
76
+ packfile = File.open(@name)
77
+ yield packfile
78
+ packfile.close
79
+ end
80
+
81
+ def cache_objects
82
+ @cache = {}
83
+ with_packfile do |packfile|
84
+ each_entry do |sha, offset|
85
+ data, type = unpack_object(packfile, offset, {:caching => true})
86
+ if data
87
+ @cache[sha] = RawObject.new(OBJ_TYPES[type], data)
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ def name
94
+ @name
95
+ end
96
+
97
+ def close
98
+ # shouldnt be anything open now
99
+ end
100
+
101
+ # given an index file, list out the shas that it's packfile contains
102
+ def get_shas
103
+ shas = []
104
+ each_sha1 { |sha| shas << sha.unpack("H*")[0] }
105
+ shas
106
+ end
107
+
108
+ def [](sha1)
109
+ if obj = @cache[sha1]
110
+ return obj
111
+ end
112
+
113
+ offset = find_object(sha1)
114
+ return nil if !offset
115
+ @cache[sha1] = obj = parse_object(offset)
116
+ return obj
117
+ end
118
+
119
+ def init_pack
120
+ with_idx do |idx|
121
+ @offsets = [0]
122
+ FanOutCount.times do |i|
123
+ pos = idx[i * IdxOffsetSize,IdxOffsetSize].unpack('N')[0]
124
+ if pos < @offsets[i]
125
+ raise PackFormatError, "pack #@name has discontinuous index #{i}"
126
+ end
127
+ @offsets << pos
128
+ end
129
+ @size = @offsets[-1]
130
+ end
131
+ end
132
+
133
+ def each_entry
134
+ with_idx do |idx|
135
+ if @version == 2
136
+ data = read_data_v2(idx)
137
+ data.each do |sha1, crc, offset|
138
+ yield sha1, offset
139
+ end
140
+ else
141
+ pos = OffsetStart
142
+ @size.times do
143
+ offset = idx[pos,OffsetSize].unpack('N')[0]
144
+ sha1 = idx[pos+OffsetSize,SHA1Size]
145
+ pos += EntrySize
146
+ yield sha1, offset
147
+ end
148
+ end
149
+ end
150
+ end
151
+
152
+ def read_data_v2(idx)
153
+ data = []
154
+ pos = OffsetStart
155
+ @size.times do |i|
156
+ data[i] = [idx[pos,SHA1Size], 0, 0]
157
+ pos += SHA1Size
158
+ end
159
+ @size.times do |i|
160
+ crc = idx[pos,CrcSize]
161
+ data[i][1] = crc
162
+ pos += CrcSize
163
+ end
164
+ @size.times do |i|
165
+ offset = idx[pos,OffsetSize].unpack('N')[0]
166
+ data[i][2] = offset
167
+ pos += OffsetSize
168
+ end
169
+ data
170
+ end
171
+ private :read_data_v2
172
+
173
+ def each_sha1
174
+ with_idx do |idx|
175
+ if @version == 2
176
+ data = read_data_v2(idx)
177
+ data.each do |sha1, crc, offset|
178
+ yield sha1
179
+ end
180
+ else
181
+ pos = SHA1Start
182
+ @size.times do
183
+ sha1 = idx[pos,SHA1Size]
184
+ pos += EntrySize
185
+ yield sha1
186
+ end
187
+ end
188
+ end
189
+ end
190
+
191
+ def find_object_in_index(idx, sha1)
192
+ slot = sha1[0]
193
+ return nil if !slot
194
+ first, last = @offsets[slot,2]
195
+ while first < last
196
+ mid = (first + last) / 2
197
+ if @version == 2
198
+ midsha1 = idx[OffsetStart + (mid * SHA1Size), SHA1Size]
199
+ cmp = midsha1 <=> sha1
200
+
201
+ if cmp < 0
202
+ first = mid + 1
203
+ elsif cmp > 0
204
+ last = mid
205
+ else
206
+ pos = OffsetStart + (@size * (SHA1Size + CrcSize)) + (mid * OffsetSize)
207
+ offset = idx[pos, OffsetSize].unpack('N')[0]
208
+ return offset
209
+ end
210
+ else
211
+ midsha1 = idx[SHA1Start + mid * EntrySize,SHA1Size]
212
+ cmp = midsha1 <=> sha1
213
+
214
+ if cmp < 0
215
+ first = mid + 1
216
+ elsif cmp > 0
217
+ last = mid
218
+ else
219
+ pos = OffsetStart + mid * EntrySize
220
+ offset = idx[pos,OffsetSize].unpack('N')[0]
221
+ return offset
222
+ end
223
+ end
224
+ end
225
+ nil
226
+ end
227
+
228
+ def find_object(sha1)
229
+ obj = nil
230
+ with_idx do |idx|
231
+ obj = find_object_in_index(idx, sha1)
232
+ end
233
+ obj
234
+ end
235
+ private :find_object
236
+
237
+ def parse_object(offset)
238
+ obj = nil
239
+ with_packfile do |packfile|
240
+ data, type = unpack_object(packfile, offset)
241
+ obj = RawObject.new(OBJ_TYPES[type], data)
242
+ end
243
+ obj
244
+ end
245
+ protected :parse_object
246
+
247
+ def unpack_object(packfile, offset, options = {})
248
+ obj_offset = offset
249
+ packfile.seek(offset)
250
+
251
+ c = packfile.read(1)[0]
252
+ size = c & 0xf
253
+ type = (c >> 4) & 7
254
+ shift = 4
255
+ offset += 1
256
+ while c & 0x80 != 0
257
+ c = packfile.read(1)[0]
258
+ size |= ((c & 0x7f) << shift)
259
+ shift += 7
260
+ offset += 1
261
+ end
262
+
263
+ return [false, false] if !(type == OBJ_COMMIT || type == OBJ_TREE) && options[:caching]
264
+
265
+ case type
266
+ when OBJ_OFS_DELTA, OBJ_REF_DELTA
267
+ data, type = unpack_deltified(packfile, type, offset, obj_offset, size, options)
268
+ #puts type
269
+ when OBJ_COMMIT, OBJ_TREE, OBJ_BLOB, OBJ_TAG
270
+ data = unpack_compressed(offset, size)
271
+ else
272
+ raise PackFormatError, "invalid type #{type}"
273
+ end
274
+ [data, type]
275
+ end
276
+ private :unpack_object
277
+
278
+ def unpack_deltified(packfile, type, offset, obj_offset, size, options = {})
279
+ packfile.seek(offset)
280
+ data = packfile.read(SHA1Size)
281
+
282
+ if type == OBJ_OFS_DELTA
283
+ i = 0
284
+ c = data[i]
285
+ base_offset = c & 0x7f
286
+ while c & 0x80 != 0
287
+ c = data[i += 1]
288
+ base_offset += 1
289
+ base_offset <<= 7
290
+ base_offset |= c & 0x7f
291
+ end
292
+ base_offset = obj_offset - base_offset
293
+ offset += i + 1
294
+ else
295
+ base_offset = find_object(data)
296
+ offset += SHA1Size
297
+ end
298
+
299
+ base, type = unpack_object(packfile, base_offset)
300
+
301
+ return [false, false] if !(type == OBJ_COMMIT || type == OBJ_TREE) && options[:caching]
302
+
303
+ delta = unpack_compressed(offset, size)
304
+ [patch_delta(base, delta), type]
305
+ end
306
+ private :unpack_deltified
307
+
308
+ def unpack_compressed(offset, destsize)
309
+ outdata = ""
310
+ with_packfile do |packfile|
311
+ packfile.seek(offset)
312
+ zstr = Zlib::Inflate.new
313
+ while outdata.size < destsize
314
+ indata = packfile.read(4096)
315
+ if indata.size == 0
316
+ raise PackFormatError, 'error reading pack data'
317
+ end
318
+ outdata += zstr.inflate(indata)
319
+ end
320
+ if outdata.size > destsize
321
+ raise PackFormatError, 'error reading pack data'
322
+ end
323
+ zstr.close
324
+ end
325
+ outdata
326
+ end
327
+ private :unpack_compressed
328
+
329
+ def patch_delta(base, delta)
330
+ src_size, pos = patch_delta_header_size(delta, 0)
331
+ if src_size != base.size
332
+ raise PackFormatError, 'invalid delta data'
333
+ end
334
+
335
+ dest_size, pos = patch_delta_header_size(delta, pos)
336
+ dest = ""
337
+ while pos < delta.size
338
+ c = delta[pos]
339
+ pos += 1
340
+ if c & 0x80 != 0
341
+ pos -= 1
342
+ cp_off = cp_size = 0
343
+ cp_off = delta[pos += 1] if c & 0x01 != 0
344
+ cp_off |= delta[pos += 1] << 8 if c & 0x02 != 0
345
+ cp_off |= delta[pos += 1] << 16 if c & 0x04 != 0
346
+ cp_off |= delta[pos += 1] << 24 if c & 0x08 != 0
347
+ cp_size = delta[pos += 1] if c & 0x10 != 0
348
+ cp_size |= delta[pos += 1] << 8 if c & 0x20 != 0
349
+ cp_size |= delta[pos += 1] << 16 if c & 0x40 != 0
350
+ cp_size = 0x10000 if cp_size == 0
351
+ pos += 1
352
+ dest += base[cp_off,cp_size]
353
+ elsif c != 0
354
+ dest += delta[pos,c]
355
+ pos += c
356
+ else
357
+ raise PackFormatError, 'invalid delta data'
358
+ end
359
+ end
360
+ dest
361
+ end
362
+ private :patch_delta
363
+
364
+ def patch_delta_header_size(delta, pos)
365
+ size = 0
366
+ shift = 0
367
+ begin
368
+ c = delta[pos]
369
+ if c == nil
370
+ raise PackFormatError, 'invalid delta header'
371
+ end
372
+ pos += 1
373
+ size |= (c & 0x7f) << shift
374
+ shift += 7
375
+ end while c & 0x80 != 0
376
+ [size, pos]
377
+ end
378
+ private :patch_delta_header_size
379
+ end
380
+ end
381
+ end
382
+ end