schacon-grit 0.9.1

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.
Files changed (47) hide show
  1. data/History.txt +13 -0
  2. data/Manifest.txt +57 -0
  3. data/README.txt +213 -0
  4. data/Rakefile +29 -0
  5. data/grit.gemspec +60 -0
  6. data/lib/grit.rb +53 -0
  7. data/lib/grit/actor.rb +36 -0
  8. data/lib/grit/blob.rb +117 -0
  9. data/lib/grit/commit.rb +221 -0
  10. data/lib/grit/commit_stats.rb +104 -0
  11. data/lib/grit/config.rb +44 -0
  12. data/lib/grit/diff.rb +70 -0
  13. data/lib/grit/errors.rb +7 -0
  14. data/lib/grit/git-ruby.rb +175 -0
  15. data/lib/grit/git-ruby/commit_db.rb +52 -0
  16. data/lib/grit/git-ruby/file_index.rb +186 -0
  17. data/lib/grit/git-ruby/git_object.rb +344 -0
  18. data/lib/grit/git-ruby/internal/loose.rb +136 -0
  19. data/lib/grit/git-ruby/internal/mmap.rb +59 -0
  20. data/lib/grit/git-ruby/internal/pack.rb +332 -0
  21. data/lib/grit/git-ruby/internal/raw_object.rb +37 -0
  22. data/lib/grit/git-ruby/object.rb +319 -0
  23. data/lib/grit/git-ruby/repository.rb +675 -0
  24. data/lib/grit/git.rb +130 -0
  25. data/lib/grit/head.rb +83 -0
  26. data/lib/grit/index.rb +102 -0
  27. data/lib/grit/lazy.rb +31 -0
  28. data/lib/grit/ref.rb +95 -0
  29. data/lib/grit/repo.rb +381 -0
  30. data/lib/grit/status.rb +149 -0
  31. data/lib/grit/tag.rb +71 -0
  32. data/lib/grit/tree.rb +100 -0
  33. data/test/test_actor.rb +35 -0
  34. data/test/test_blob.rb +79 -0
  35. data/test/test_commit.rb +184 -0
  36. data/test/test_config.rb +58 -0
  37. data/test/test_diff.rb +18 -0
  38. data/test/test_git.rb +70 -0
  39. data/test/test_grit.rb +32 -0
  40. data/test/test_head.rb +41 -0
  41. data/test/test_real.rb +19 -0
  42. data/test/test_reality.rb +17 -0
  43. data/test/test_remote.rb +14 -0
  44. data/test/test_repo.rb +277 -0
  45. data/test/test_tag.rb +25 -0
  46. data/test/test_tree.rb +96 -0
  47. metadata +110 -0
@@ -0,0 +1,344 @@
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
+ # These classes translate the raw binary data kept in the sha encoded files
13
+ # into parsed data that can then be used in another fashion
14
+ require 'stringio'
15
+
16
+ module Grit
17
+ module GitRuby
18
+
19
+ # class for author/committer/tagger lines
20
+ class UserInfo
21
+ attr_accessor :name, :email, :date, :offset
22
+
23
+ def initialize(str)
24
+ @email = ''
25
+ @date = Time.now
26
+ @offset = 0
27
+
28
+ m = /^(.*?) <(.*)> (\d+) ([+-])0*(\d+?)$/.match(str)
29
+ if !m
30
+ case str
31
+ when /<.+>/
32
+ m, @name, @email = *str.match(/(.*) <(.+?)>/)
33
+ else
34
+ @name = str
35
+ end
36
+ else
37
+ @name = m[1]
38
+ @email = m[2]
39
+ @date = Time.at(Integer(m[3]))
40
+ @offset = (m[4] == "-" ? -1 : 1)*Integer(m[5])
41
+ end
42
+ end
43
+
44
+ def to_s
45
+ "%s <%s> %s %+05d" % [@name, @email, @date.to_i, @offset]
46
+ end
47
+ end
48
+
49
+ # base class for all git objects (blob, tree, commit, tag)
50
+ class GitObject
51
+ attr_accessor :repository
52
+
53
+ def GitObject.from_raw(rawobject, repository = nil)
54
+ case rawobject.type
55
+ when :blob
56
+ return Blob.from_raw(rawobject, repository)
57
+ when :tree
58
+ return Tree.from_raw(rawobject, repository)
59
+ when :commit
60
+ return Commit.from_raw(rawobject, repository)
61
+ when :tag
62
+ return Tag.from_raw(rawobject, repository)
63
+ else
64
+ raise RuntimeError, "got invalid object-type"
65
+ end
66
+ end
67
+
68
+ def initialize
69
+ raise NotImplemented, "abstract class"
70
+ end
71
+
72
+ def type
73
+ raise NotImplemented, "abstract class"
74
+ end
75
+
76
+ def raw_content
77
+ raise NotImplemented, "abstract class"
78
+ end
79
+
80
+ def sha1
81
+ Digest::SHA1.hexdigest("%s %d\0" % \
82
+ [self.type, self.raw_content.length] + \
83
+ self.raw_content)
84
+ end
85
+ end
86
+
87
+ class Blob < GitObject
88
+ attr_accessor :content
89
+
90
+ def self.from_raw(rawobject, repository)
91
+ new(rawobject.content)
92
+ end
93
+
94
+ def initialize(content, repository=nil)
95
+ @content = content
96
+ @repository = repository
97
+ end
98
+
99
+ def type
100
+ :blob
101
+ end
102
+
103
+ def raw_content
104
+ @content
105
+ end
106
+ end
107
+
108
+ class DirectoryEntry
109
+ S_IFMT = 00170000
110
+ S_IFLNK = 0120000
111
+ S_IFREG = 0100000
112
+ S_IFDIR = 0040000
113
+ S_IFGITLINK = 0160000
114
+ attr_accessor :mode, :name, :sha1
115
+ def initialize(mode, filename, sha1o)
116
+ @mode = 0
117
+ mode.each_byte do |i|
118
+ @mode = (@mode << 3) | (i-'0'[0])
119
+ end
120
+ @name = filename
121
+ @sha1 = sha1o
122
+ if ![S_IFLNK, S_IFDIR, S_IFREG, S_IFGITLINK].include?(@mode & S_IFMT)
123
+ raise RuntimeError, "unknown type for directory entry"
124
+ end
125
+ end
126
+
127
+ def type
128
+ case @mode & S_IFMT
129
+ when S_IFGITLINK
130
+ @type = :submodule
131
+ when S_IFLNK
132
+ @type = :link
133
+ when S_IFDIR
134
+ @type = :directory
135
+ when S_IFREG
136
+ @type = :file
137
+ else
138
+ raise RuntimeError, "unknown type for directory entry"
139
+ end
140
+ end
141
+
142
+ def type=(type)
143
+ case @type
144
+ when :link
145
+ @mode = (@mode & ~S_IFMT) | S_IFLNK
146
+ when :directory
147
+ @mode = (@mode & ~S_IFMT) | S_IFDIR
148
+ when :file
149
+ @mode = (@mode & ~S_IFMT) | S_IFREG
150
+ when :submodule
151
+ @mode = (@mode & ~S_IFMT) | S_IFGITLINK
152
+ else
153
+ raise RuntimeError, "invalid type"
154
+ end
155
+ end
156
+
157
+ def format_type
158
+ case type
159
+ when :link
160
+ 'link'
161
+ when :directory
162
+ 'tree'
163
+ when :file
164
+ 'blob'
165
+ when :submodule
166
+ 'commit'
167
+ end
168
+ end
169
+
170
+ def format_mode
171
+ "%06o" % @mode
172
+ end
173
+
174
+ def raw
175
+ "%o %s\0%s" % [@mode, @name, [@sha1].pack("H*")]
176
+ end
177
+ end
178
+
179
+
180
+ def self.read_bytes_until(io, char)
181
+ string = ''
182
+ while ((next_char = io.getc.chr) != char) && !io.eof
183
+ string += next_char
184
+ end
185
+ string
186
+ end
187
+
188
+
189
+ class Tree < GitObject
190
+ attr_accessor :entry
191
+
192
+ def self.from_raw(rawobject, repository=nil)
193
+ raw = StringIO.new(rawobject.content)
194
+
195
+ entries = []
196
+ while !raw.eof?
197
+ mode = Grit::GitRuby.read_bytes_until(raw, ' ')
198
+ file_name = Grit::GitRuby.read_bytes_until(raw, "\0")
199
+ raw_sha = raw.read(20)
200
+ sha = raw_sha.unpack("H*").first
201
+
202
+ entries << DirectoryEntry.new(mode, file_name, sha)
203
+ end
204
+ new(entries, repository)
205
+ end
206
+
207
+ def initialize(entries=[], repository = nil)
208
+ @entry = entries
209
+ @repository = repository
210
+ end
211
+
212
+ def type
213
+ :tree
214
+ end
215
+
216
+ def raw_content
217
+ # TODO: sort correctly
218
+ #@entry.sort { |a,b| a.name <=> b.name }.
219
+ @entry.collect { |e| [[e.format_mode, e.format_type, e.sha1].join(' '), e.name].join("\t") }.join("\n")
220
+ end
221
+
222
+ def actual_raw
223
+ #@entry.collect { |e| e.raw.join(' '), e.name].join("\t") }.join("\n")
224
+ end
225
+ end
226
+
227
+ class Commit < GitObject
228
+ attr_accessor :author, :committer, :tree, :parent, :message, :headers
229
+
230
+ def self.from_raw(rawobject, repository=nil)
231
+ parent = []
232
+ tree = author = committer = nil
233
+
234
+ headers, message = rawobject.content.split(/\n\n/, 2)
235
+ all_headers = headers.split(/\n/).map { |header| header.split(/ /, 2) }
236
+ all_headers.each do |key, value|
237
+ case key
238
+ when "tree"
239
+ tree = value
240
+ when "parent"
241
+ parent.push(value)
242
+ when "author"
243
+ author = UserInfo.new(value)
244
+ when "committer"
245
+ committer = UserInfo.new(value)
246
+ else
247
+ warn "unknown header '%s' in commit %s" % \
248
+ [key, rawobject.sha1.unpack("H*")[0]]
249
+ end
250
+ end
251
+ if not tree && author && committer
252
+ raise RuntimeError, "incomplete raw commit object"
253
+ end
254
+ new(tree, parent, author, committer, message, headers, repository)
255
+ end
256
+
257
+ def initialize(tree, parent, author, committer, message, headers, repository=nil)
258
+ @tree = tree
259
+ @author = author
260
+ @parent = parent
261
+ @committer = committer
262
+ @message = message
263
+ @headers = headers
264
+ @repository = repository
265
+ end
266
+
267
+ def type
268
+ :commit
269
+ end
270
+
271
+ def raw_content
272
+ "tree %s\n%sauthor %s\ncommitter %s\n\n" % [
273
+ @tree,
274
+ @parent.collect { |i| "parent %s\n" % i }.join,
275
+ @author, @committer] + @message
276
+ end
277
+
278
+ def raw_log(sha)
279
+ output = "commit #{sha}\n"
280
+ output += @headers + "\n\n"
281
+ output += @message.split("\n").map { |l| ' ' + l }.join("\n") + "\n\n"
282
+ end
283
+
284
+ end
285
+
286
+ class Tag < GitObject
287
+ attr_accessor :object, :type, :tag, :tagger, :message
288
+
289
+ def self.from_raw(rawobject, repository=nil)
290
+
291
+ headers, message = rawobject.content.split(/\n\n/, 2)
292
+ headers = headers.split(/\n/).map { |header| header.split(' ', 2) }
293
+
294
+ object = ''
295
+ type = ''
296
+ tag = ''
297
+ tagger = ''
298
+
299
+ headers.each do |key, value|
300
+ case key
301
+ when "object"
302
+ object = value
303
+ when "type"
304
+ if !["blob", "tree", "commit", "tag"].include?(value)
305
+ raise RuntimeError, "invalid type in tag"
306
+ end
307
+ type = value.to_sym
308
+ when "tag"
309
+ tag = value
310
+ when "tagger"
311
+ tagger = UserInfo.new(value)
312
+ else
313
+ warn "unknown header '%s' in tag" % \
314
+ [key, rawobject.sha1.unpack("H*")[0]]
315
+ end
316
+ end
317
+
318
+ if not object && type && tag && tagger
319
+ raise RuntimeError, "incomplete raw tag object"
320
+ end
321
+ new(object, type, tag, tagger, message, repository)
322
+ end
323
+
324
+ def initialize(object, type, tag, tagger, message, repository=nil)
325
+ @object = object
326
+ @type = type
327
+ @tag = tag
328
+ @tagger = tagger
329
+ @repository = repository
330
+ @message = message
331
+ end
332
+
333
+ def raw_content
334
+ "object %s\ntype %s\ntag %s\ntagger %s\n\n" % \
335
+ [@object, @type, @tag, @tagger] + @message
336
+ end
337
+
338
+ def type
339
+ :tag
340
+ end
341
+ end
342
+
343
+ end
344
+ end
@@ -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