steveh-grit 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,325 @@
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
+ m = /^(.*?) <(.*)> (\d+) ([+-])0*(\d+?)$/.match(str)
25
+ if !m
26
+ raise RuntimeError, "invalid header '%s' in commit" % str
27
+ end
28
+ @name = m[1]
29
+ @email = m[2]
30
+ @date = Time.at(Integer(m[3]))
31
+ @offset = (m[4] == "-" ? -1 : 1)*Integer(m[5])
32
+ end
33
+
34
+ def to_s
35
+ "%s <%s> %s %+05d" % [@name, @email, @date.to_i, @offset]
36
+ end
37
+ end
38
+
39
+ # base class for all git objects (blob, tree, commit, tag)
40
+ class Object
41
+ attr_accessor :repository
42
+
43
+ def Object.from_raw(rawobject, repository = nil)
44
+ case rawobject.type
45
+ when :blob
46
+ return Blob.from_raw(rawobject, repository)
47
+ when :tree
48
+ return Tree.from_raw(rawobject, repository)
49
+ when :commit
50
+ return Commit.from_raw(rawobject, repository)
51
+ when :tag
52
+ return Tag.from_raw(rawobject, repository)
53
+ else
54
+ raise RuntimeError, "got invalid object-type"
55
+ end
56
+ end
57
+
58
+ def initialize
59
+ raise NotImplemented, "abstract class"
60
+ end
61
+
62
+ def type
63
+ raise NotImplemented, "abstract class"
64
+ end
65
+
66
+ def raw_content
67
+ raise NotImplemented, "abstract class"
68
+ end
69
+
70
+ def sha1
71
+ Digest::SHA1.hexdigest("%s %d\0" % \
72
+ [self.type, self.raw_content.length] + \
73
+ self.raw_content)
74
+ end
75
+ end
76
+
77
+ class Blob < Object
78
+ attr_accessor :content
79
+
80
+ def self.from_raw(rawobject, repository)
81
+ new(rawobject.content)
82
+ end
83
+
84
+ def initialize(content, repository=nil)
85
+ @content = content
86
+ @repository = repository
87
+ end
88
+
89
+ def type
90
+ :blob
91
+ end
92
+
93
+ def raw_content
94
+ @content
95
+ end
96
+ end
97
+
98
+ class DirectoryEntry
99
+ S_IFMT = 00170000
100
+ S_IFLNK = 0120000
101
+ S_IFREG = 0100000
102
+ S_IFDIR = 0040000
103
+
104
+ attr_accessor :mode, :name, :sha1
105
+ def initialize(mode, filename, sha1o)
106
+ @mode = 0
107
+ mode.each_byte do |i|
108
+ @mode = (@mode << 3) | (i-'0'[0])
109
+ end
110
+ @name = filename
111
+ @sha1 = sha1o
112
+ if ![S_IFLNK, S_IFDIR, S_IFREG].include?(@mode & S_IFMT)
113
+ raise RuntimeError, "unknown type for directory entry"
114
+ end
115
+ end
116
+
117
+ def type
118
+ case @mode & S_IFMT
119
+ when S_IFLNK
120
+ @type = :link
121
+ when S_IFDIR
122
+ @type = :directory
123
+ when S_IFREG
124
+ @type = :file
125
+ else
126
+ raise RuntimeError, "unknown type for directory entry"
127
+ end
128
+ end
129
+
130
+ def type=(type)
131
+ case @type
132
+ when :link
133
+ @mode = (@mode & ~S_IFMT) | S_IFLNK
134
+ when :directory
135
+ @mode = (@mode & ~S_IFMT) | S_IFDIR
136
+ when :file
137
+ @mode = (@mode & ~S_IFMT) | S_IFREG
138
+ else
139
+ raise RuntimeError, "invalid type"
140
+ end
141
+ end
142
+
143
+ def format_type
144
+ case type
145
+ when :link
146
+ 'link'
147
+ when :directory
148
+ 'tree'
149
+ when :file
150
+ 'blob'
151
+ end
152
+ end
153
+
154
+ def format_mode
155
+ "%06o" % @mode
156
+ end
157
+
158
+ def raw
159
+ "%o %s\0%s" % [@mode, @name, [@sha1].pack("H*")]
160
+ end
161
+ end
162
+
163
+
164
+ def self.read_bytes_until(io, char)
165
+ string = ''
166
+ if RUBY_VERSION > '1.9'
167
+ while ((next_char = io.getc) != char) && !io.eof
168
+ string += next_char
169
+ end
170
+ else
171
+ while ((next_char = io.getc.chr) != char) && !io.eof
172
+ string += next_char
173
+ end
174
+ end
175
+ string
176
+ end
177
+
178
+
179
+ class Tree < Object
180
+ attr_accessor :entry
181
+
182
+ def self.from_raw(rawobject, repository=nil)
183
+ raw = StringIO.new(rawobject.content)
184
+
185
+ entries = []
186
+ while !raw.eof?
187
+ mode = Grit::GitRuby.read_bytes_until(raw, ' ')
188
+ file_name = Grit::GitRuby.read_bytes_until(raw, "\0")
189
+ raw_sha = raw.read(20)
190
+ sha = raw_sha.unpack("H*").first
191
+
192
+ entries << DirectoryEntry.new(mode, file_name, sha)
193
+ end
194
+ new(entries, repository)
195
+ end
196
+
197
+ def initialize(entries=[], repository = nil)
198
+ @entry = entries
199
+ @repository = repository
200
+ end
201
+
202
+ def type
203
+ :tree
204
+ end
205
+
206
+ def raw_content
207
+ # TODO: sort correctly
208
+ #@entry.sort { |a,b| a.name <=> b.name }.
209
+ @entry.collect { |e| [[e.format_mode, e.format_type, e.sha1].join(' '), e.name].join("\t") }.join("\n")
210
+ end
211
+
212
+ def actual_raw
213
+ #@entry.collect { |e| e.raw.join(' '), e.name].join("\t") }.join("\n")
214
+ end
215
+ end
216
+
217
+ class Commit < Object
218
+ attr_accessor :author, :committer, :tree, :parent, :message, :headers
219
+
220
+ def self.from_raw(rawobject, repository=nil)
221
+ parent = []
222
+ tree = author = committer = nil
223
+
224
+ headers, message = rawobject.content.split(/\n\n/, 2)
225
+ all_headers = headers.split(/\n/).map { |header| header.split(/ /, 2) }
226
+ all_headers.each do |key, value|
227
+ case key
228
+ when "tree"
229
+ tree = value
230
+ when "parent"
231
+ parent.push(value)
232
+ when "author"
233
+ author = UserInfo.new(value)
234
+ when "committer"
235
+ committer = UserInfo.new(value)
236
+ else
237
+ warn "unknown header '%s' in commit %s" % \
238
+ [key, rawobject.sha1.unpack("H*")[0]]
239
+ end
240
+ end
241
+ if not tree && author && committer
242
+ raise RuntimeError, "incomplete raw commit object"
243
+ end
244
+ new(tree, parent, author, committer, message, headers, repository)
245
+ end
246
+
247
+ def initialize(tree, parent, author, committer, message, headers, repository=nil)
248
+ @tree = tree
249
+ @author = author
250
+ @parent = parent
251
+ @committer = committer
252
+ @message = message
253
+ @headers = headers
254
+ @repository = repository
255
+ end
256
+
257
+ def type
258
+ :commit
259
+ end
260
+
261
+ def raw_content
262
+ "tree %s\n%sauthor %s\ncommitter %s\n\n" % [
263
+ @tree,
264
+ @parent.collect { |i| "parent %s\n" % i }.join,
265
+ @author, @committer] + @message
266
+ end
267
+
268
+ def raw_log(sha)
269
+ output = "commit #{sha}\n"
270
+ output += @headers + "\n\n"
271
+ output += @message.split("\n").map { |l| ' ' + l }.join("\n") + "\n\n"
272
+ end
273
+
274
+ end
275
+
276
+ class Tag < Object
277
+ attr_accessor :object, :type, :tag, :tagger, :message
278
+
279
+ def self.from_raw(rawobject, repository=nil)
280
+ headers, message = rawobject.content.split(/\n\n/, 2)
281
+ headers = headers.split(/\n/).map { |header| header.split(/ /, 2) }
282
+ headers.each do |key, value|
283
+ case key
284
+ when "object"
285
+ object = value
286
+ when "type"
287
+ if !["blob", "tree", "commit", "tag"].include?(value)
288
+ raise RuntimeError, "invalid type in tag"
289
+ end
290
+ type = value.to_sym
291
+ when "tag"
292
+ tag = value
293
+ when "tagger"
294
+ tagger = UserInfo.new(value)
295
+ else
296
+ warn "unknown header '%s' in tag" % \
297
+ [key, rawobject.sha1.unpack("H*")[0]]
298
+ end
299
+ if not object && type && tag && tagger
300
+ raise RuntimeError, "incomplete raw tag object"
301
+ end
302
+ end
303
+ new(object, type, tag, tagger, repository)
304
+ end
305
+
306
+ def initialize(object, type, tag, tagger, repository=nil)
307
+ @object = object
308
+ @type = type
309
+ @tag = tag
310
+ @tagger = tagger
311
+ @repository = repository
312
+ end
313
+
314
+ def raw_content
315
+ "object %s\ntype %s\ntag %s\ntagger %s\n\n" % \
316
+ [@object, @type, @tag, @tagger] + @message
317
+ end
318
+
319
+ def type
320
+ :tag
321
+ end
322
+ end
323
+
324
+ end
325
+ end