robinluckey-grit 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,350 @@
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'.getord(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
+ if RUBY_VERSION > '1.9'
183
+ while ((next_char = io.getc) != char) && !io.eof
184
+ string += next_char
185
+ end
186
+ else
187
+ while ((next_char = io.getc.chr) != char) && !io.eof
188
+ string += next_char
189
+ end
190
+ end
191
+ string
192
+ end
193
+
194
+
195
+ class Tree < GitObject
196
+ attr_accessor :entry
197
+
198
+ def self.from_raw(rawobject, repository=nil)
199
+ raw = StringIO.new(rawobject.content)
200
+
201
+ entries = []
202
+ while !raw.eof?
203
+ mode = Grit::GitRuby.read_bytes_until(raw, ' ')
204
+ file_name = Grit::GitRuby.read_bytes_until(raw, "\0")
205
+ raw_sha = raw.read(20)
206
+ sha = raw_sha.unpack("H*").first
207
+
208
+ entries << DirectoryEntry.new(mode, file_name, sha)
209
+ end
210
+ new(entries, repository)
211
+ end
212
+
213
+ def initialize(entries=[], repository = nil)
214
+ @entry = entries
215
+ @repository = repository
216
+ end
217
+
218
+ def type
219
+ :tree
220
+ end
221
+
222
+ def raw_content
223
+ # TODO: sort correctly
224
+ #@entry.sort { |a,b| a.name <=> b.name }.
225
+ @entry.collect { |e| [[e.format_mode, e.format_type, e.sha1].join(' '), e.name].join("\t") }.join("\n")
226
+ end
227
+
228
+ def actual_raw
229
+ #@entry.collect { |e| e.raw.join(' '), e.name].join("\t") }.join("\n")
230
+ end
231
+ end
232
+
233
+ class Commit < GitObject
234
+ attr_accessor :author, :committer, :tree, :parent, :message, :headers
235
+
236
+ def self.from_raw(rawobject, repository=nil)
237
+ parent = []
238
+ tree = author = committer = nil
239
+
240
+ headers, message = rawobject.content.split(/\n\n/, 2)
241
+ all_headers = headers.split(/\n/).map { |header| header.split(/ /, 2) }
242
+ all_headers.each do |key, value|
243
+ case key
244
+ when "tree"
245
+ tree = value
246
+ when "parent"
247
+ parent.push(value)
248
+ when "author"
249
+ author = UserInfo.new(value)
250
+ when "committer"
251
+ committer = UserInfo.new(value)
252
+ else
253
+ warn "unknown header '%s' in commit %s" % \
254
+ [key, rawobject.sha1.unpack("H*")[0]]
255
+ end
256
+ end
257
+ if not tree && author && committer
258
+ raise RuntimeError, "incomplete raw commit object"
259
+ end
260
+ new(tree, parent, author, committer, message, headers, repository)
261
+ end
262
+
263
+ def initialize(tree, parent, author, committer, message, headers, repository=nil)
264
+ @tree = tree
265
+ @author = author
266
+ @parent = parent
267
+ @committer = committer
268
+ @message = message
269
+ @headers = headers
270
+ @repository = repository
271
+ end
272
+
273
+ def type
274
+ :commit
275
+ end
276
+
277
+ def raw_content
278
+ "tree %s\n%sauthor %s\ncommitter %s\n\n" % [
279
+ @tree,
280
+ @parent.collect { |i| "parent %s\n" % i }.join,
281
+ @author, @committer] + @message
282
+ end
283
+
284
+ def raw_log(sha)
285
+ output = "commit #{sha}\n"
286
+ output += @headers + "\n\n"
287
+ output += @message.split("\n").map { |l| ' ' + l }.join("\n") + "\n\n"
288
+ end
289
+
290
+ end
291
+
292
+ class Tag < GitObject
293
+ attr_accessor :object, :type, :tag, :tagger, :message
294
+
295
+ def self.from_raw(rawobject, repository=nil)
296
+
297
+ headers, message = rawobject.content.split(/\n\n/, 2)
298
+ headers = headers.split(/\n/).map { |header| header.split(' ', 2) }
299
+
300
+ object = ''
301
+ type = ''
302
+ tag = ''
303
+ tagger = ''
304
+
305
+ headers.each do |key, value|
306
+ case key
307
+ when "object"
308
+ object = value
309
+ when "type"
310
+ if !["blob", "tree", "commit", "tag"].include?(value)
311
+ raise RuntimeError, "invalid type in tag"
312
+ end
313
+ type = value.to_sym
314
+ when "tag"
315
+ tag = value
316
+ when "tagger"
317
+ tagger = UserInfo.new(value)
318
+ else
319
+ warn "unknown header '%s' in tag" % \
320
+ [key, rawobject.sha1.unpack("H*")[0]]
321
+ end
322
+ end
323
+
324
+ if not object && type && tag && tagger
325
+ raise RuntimeError, "incomplete raw tag object"
326
+ end
327
+ new(object, type, tag, tagger, message, repository)
328
+ end
329
+
330
+ def initialize(object, type, tag, tagger, message, repository=nil)
331
+ @object = object
332
+ @type = type
333
+ @tag = tag
334
+ @tagger = tagger
335
+ @repository = repository
336
+ @message = message
337
+ end
338
+
339
+ def raw_content
340
+ "object %s\ntype %s\ntag %s\ntagger %s\n\n" % \
341
+ [@object, @type, @tag, @tagger] + @message
342
+ end
343
+
344
+ def type
345
+ :tag
346
+ end
347
+ end
348
+
349
+ end
350
+ 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 FileWindow
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).getord(0)
50
+ else
51
+ @file.read(len)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+
@@ -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(File.open(path, 'rb').read)
33
+ rescue Errno::ENOENT
34
+ nil
35
+ end
36
+ end
37
+
38
+ def get_raw_object(buf)
39
+ if buf.length < 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.length != 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.length.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.length.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.length <= 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