smeagol 0.6.0 → 0.6.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 (37) hide show
  1. checksums.yaml +7 -0
  2. data/.index +82 -0
  3. data/HISTORY.md +14 -0
  4. data/lib/smeagol.rb +2 -1
  5. data/lib/smeagol/app.rb +1 -1
  6. data/lib/smeagol/gollum/file.rb +1 -1
  7. data/lib/smeagol/wiki.rb +1 -0
  8. data/vendor/grit/lib/grit.rb +75 -0
  9. data/vendor/grit/lib/grit/actor.rb +52 -0
  10. data/vendor/grit/lib/grit/blame.rb +70 -0
  11. data/vendor/grit/lib/grit/blob.rb +126 -0
  12. data/vendor/grit/lib/grit/commit.rb +313 -0
  13. data/vendor/grit/lib/grit/commit_stats.rb +128 -0
  14. data/vendor/grit/lib/grit/config.rb +44 -0
  15. data/vendor/grit/lib/grit/diff.rb +79 -0
  16. data/vendor/grit/lib/grit/errors.rb +10 -0
  17. data/vendor/grit/lib/grit/git-ruby.rb +262 -0
  18. data/vendor/grit/lib/grit/git-ruby/commit_db.rb +52 -0
  19. data/vendor/grit/lib/grit/git-ruby/git_object.rb +353 -0
  20. data/vendor/grit/lib/grit/git-ruby/internal/file_window.rb +58 -0
  21. data/vendor/grit/lib/grit/git-ruby/internal/loose.rb +137 -0
  22. data/vendor/grit/lib/grit/git-ruby/internal/pack.rb +397 -0
  23. data/vendor/grit/lib/grit/git-ruby/internal/raw_object.rb +44 -0
  24. data/vendor/grit/lib/grit/git-ruby/repository.rb +775 -0
  25. data/vendor/grit/lib/grit/git.rb +501 -0
  26. data/vendor/grit/lib/grit/index.rb +222 -0
  27. data/vendor/grit/lib/grit/lazy.rb +35 -0
  28. data/vendor/grit/lib/grit/merge.rb +45 -0
  29. data/vendor/grit/lib/grit/ref.rb +78 -0
  30. data/vendor/grit/lib/grit/repo.rb +709 -0
  31. data/vendor/grit/lib/grit/ruby1.9.rb +7 -0
  32. data/vendor/grit/lib/grit/status.rb +153 -0
  33. data/vendor/grit/lib/grit/submodule.rb +88 -0
  34. data/vendor/grit/lib/grit/tag.rb +102 -0
  35. data/vendor/grit/lib/grit/tree.rb +125 -0
  36. metadata +125 -56
  37. data/.ruby +0 -80
@@ -0,0 +1,79 @@
1
+ module Grit
2
+
3
+ class Diff
4
+ attr_reader :a_path, :b_path
5
+ attr_reader :a_blob, :b_blob
6
+ attr_reader :a_mode, :b_mode
7
+ attr_reader :new_file, :deleted_file, :renamed_file
8
+ attr_reader :similarity_index
9
+ attr_accessor :diff
10
+
11
+ def initialize(repo, a_path, b_path, a_blob, b_blob, a_mode, b_mode, new_file, deleted_file, diff, renamed_file = false, similarity_index = 0)
12
+ @repo = repo
13
+ @a_path = a_path
14
+ @b_path = b_path
15
+ @a_blob = a_blob =~ /^0{40}$/ ? nil : Blob.create(repo, :id => a_blob)
16
+ @b_blob = b_blob =~ /^0{40}$/ ? nil : Blob.create(repo, :id => b_blob)
17
+ @a_mode = a_mode
18
+ @b_mode = b_mode
19
+ @new_file = new_file || @a_blob.nil?
20
+ @deleted_file = deleted_file || @b_blob.nil?
21
+ @renamed_file = renamed_file
22
+ @similarity_index = similarity_index.to_i
23
+ @diff = diff
24
+ end
25
+
26
+ def self.list_from_string(repo, text)
27
+ lines = text.split("\n")
28
+
29
+ diffs = []
30
+
31
+ while !lines.empty?
32
+ m, a_path, b_path = *lines.shift.match(%r{^diff --git a/(.+?) b/(.+)$})
33
+
34
+ if lines.first =~ /^old mode/
35
+ m, a_mode = *lines.shift.match(/^old mode (\d+)/)
36
+ m, b_mode = *lines.shift.match(/^new mode (\d+)/)
37
+ end
38
+
39
+ if lines.empty? || lines.first =~ /^diff --git/
40
+ diffs << Diff.new(repo, a_path, b_path, nil, nil, a_mode, b_mode, false, false, nil)
41
+ next
42
+ end
43
+
44
+ sim_index = 0
45
+ new_file = false
46
+ deleted_file = false
47
+ renamed_file = false
48
+
49
+ if lines.first =~ /^new file/
50
+ m, b_mode = lines.shift.match(/^new file mode (.+)$/)
51
+ a_mode = nil
52
+ new_file = true
53
+ elsif lines.first =~ /^deleted file/
54
+ m, a_mode = lines.shift.match(/^deleted file mode (.+)$/)
55
+ b_mode = nil
56
+ deleted_file = true
57
+ elsif lines.first =~ /^similarity index (\d+)\%/
58
+ sim_index = $1.to_i
59
+ renamed_file = true
60
+ 2.times { lines.shift } # shift away the 2 `rename from/to ...` lines
61
+ end
62
+
63
+ m, a_blob, b_blob, b_mode = *lines.shift.match(%r{^index ([0-9A-Fa-f]+)\.\.([0-9A-Fa-f]+) ?(.+)?$})
64
+ b_mode.strip! if b_mode
65
+
66
+ diff_lines = []
67
+ while lines.first && lines.first !~ /^diff/
68
+ diff_lines << lines.shift
69
+ end
70
+ diff = diff_lines.join("\n")
71
+
72
+ diffs << Diff.new(repo, a_path, b_path, a_blob, b_blob, a_mode, b_mode, new_file, deleted_file, diff, renamed_file, sim_index)
73
+ end
74
+
75
+ diffs
76
+ end
77
+ end # Diff
78
+
79
+ end # Grit
@@ -0,0 +1,10 @@
1
+ module Grit
2
+ class InvalidGitRepositoryError < StandardError
3
+ end
4
+
5
+ class NoSuchPathError < StandardError
6
+ end
7
+
8
+ class InvalidObjectType < StandardError
9
+ end
10
+ end
@@ -0,0 +1,262 @@
1
+ require 'grit/git-ruby/repository'
2
+
3
+ module Grit
4
+
5
+ # the functions in this module intercept the calls to git binary
6
+ # made by the grit objects and attempts to run them in pure ruby
7
+ # if it will be faster, or if the git binary is not available (!!TODO!!)
8
+ module GitRuby
9
+
10
+ attr_accessor :ruby_git_repo, :git_file_index
11
+
12
+ def init(options, *args)
13
+ if options.size == 0
14
+ Grit::GitRuby::Repository.init(@git_dir)
15
+ else
16
+ method_missing('init', options, *args)
17
+ end
18
+ end
19
+
20
+ def cat_file(options, sha)
21
+ if options[:t]
22
+ file_type(sha)
23
+ elsif options[:s]
24
+ file_size(sha)
25
+ elsif options[:p]
26
+ try_run { ruby_git.cat_file(sha) }
27
+ end
28
+ rescue Grit::GitRuby::Repository::NoSuchShaFound
29
+ ''
30
+ end
31
+
32
+ def cat_ref(options, ref)
33
+ sha = rev_parse({}, ref)
34
+ cat_file(options, sha)
35
+ end
36
+
37
+ # lib/grit/tree.rb:16: output = repo.git.ls_tree({}, treeish, *paths)
38
+ def ls_tree(options, treeish, *paths)
39
+ sha = rev_parse({}, treeish)
40
+ ruby_git.ls_tree(sha, paths.flatten, options.delete(:r))
41
+ rescue Grit::GitRuby::Repository::NoSuchShaFound
42
+ ''
43
+ end
44
+
45
+ # git diff --full-index 'ec037431382e83c3e95d4f2b3d145afbac8ea55d' 'f1ec1aea10986159456846b8a05615b87828d6c6'
46
+ def diff(options, sha1, sha2 = nil)
47
+ try_run { ruby_git.diff(sha1, sha2, options) }
48
+ end
49
+
50
+ def rev_list(options, *refs)
51
+ refs = ['master'] if refs.empty?
52
+ options.delete(:skip) if options[:skip].to_i == 0
53
+ allowed_options = [:max_count, :since, :until, :pretty] # this is all I can do right now
54
+ if ((options.keys - allowed_options).size > 0) || refs.size > 1
55
+ method_missing('rev-list', options, *refs)
56
+ elsif (options.size == 0)
57
+ # pure rev-list
58
+ ref = refs.first
59
+ begin
60
+ file_index.commits_from(rev_parse({}, ref)).join("\n") + "\n"
61
+ rescue
62
+ method_missing('rev-list', options, *refs)
63
+ end
64
+ else
65
+ ref = refs.first
66
+ aref = rev_parse({:verify => true}, ref)
67
+ if aref.is_a? Array
68
+ method_missing('rev-list', options, *refs)
69
+ else
70
+ try_run { ruby_git.rev_list(aref, options) }
71
+ end
72
+ end
73
+ end
74
+
75
+ def rev_parse(options, string)
76
+ raise RuntimeError, "invalid string: #{string.inspect}" unless string.is_a?(String)
77
+
78
+ # Split ranges, but don't split when specifying a ref:path.
79
+ # Don't split HEAD:some/path/in/repo..txt
80
+ # Do split sha1..sha2
81
+ if string !~ /:/ && string =~ /\.\./
82
+ (sha1, sha2) = string.split('..')
83
+ return [rev_parse({}, sha1), rev_parse({}, sha2)]
84
+ end
85
+
86
+ if /^[0-9a-f]{40}$/.match(string) # passing in a sha - just no-op it
87
+ return string.chomp
88
+ end
89
+
90
+ head = File.join(@git_dir, 'refs', 'heads', string)
91
+ return File.read(head).chomp if File.file?(head)
92
+
93
+ head = File.join(@git_dir, 'refs', 'remotes', string)
94
+ return File.read(head).chomp if File.file?(head)
95
+
96
+ head = File.join(@git_dir, 'refs', 'tags', string)
97
+ return File.read(head).chomp if File.file?(head)
98
+
99
+ ## check packed-refs file, too
100
+ packref = File.join(@git_dir, 'packed-refs')
101
+ if File.file?(packref)
102
+ File.readlines(packref).each do |line|
103
+ if m = /^(\w{40}) refs\/.+?\/(.*?)$/.match(line)
104
+ next if !Regexp.new(Regexp.escape(string) + '$').match(m[3])
105
+ return m[1].chomp
106
+ end
107
+ end
108
+ end
109
+
110
+ ## !! more - partials and such !!
111
+
112
+ # revert to calling git - grr
113
+ return method_missing('rev-parse', options, string).chomp
114
+ end
115
+
116
+ def refs(options, prefix)
117
+ refs = []
118
+ already = {}
119
+ Dir.chdir(@git_dir) do
120
+ files = Dir.glob(prefix + '/**/*')
121
+ files.each do |ref|
122
+ next if !File.file?(ref)
123
+ id = File.read(ref).chomp
124
+ name = ref.sub("#{prefix}/", '')
125
+ if !already[name]
126
+ refs << "#{name} #{id}"
127
+ already[name] = true
128
+ end
129
+ end
130
+
131
+ if File.file?('packed-refs')
132
+ File.readlines('packed-refs').each do |line|
133
+ if m = /^(\w{40}) (.*?)$/.match(line)
134
+ next if !Regexp.new('^' + prefix).match(m[2])
135
+ name = m[2].sub("#{prefix}/", '')
136
+ if !already[name]
137
+ refs << "#{name} #{m[1]}"
138
+ already[name] = true
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ refs.join("\n")
146
+ end
147
+
148
+ def tags(options, prefix)
149
+ refs = []
150
+ already = {}
151
+
152
+ Dir.chdir(repo.path) do
153
+ files = Dir.glob(prefix + '/**/*')
154
+
155
+ files.each do |ref|
156
+ next if !File.file?(ref)
157
+
158
+ id = File.read(ref).chomp
159
+ name = ref.sub("#{prefix}/", '')
160
+
161
+ if !already[name]
162
+ refs << "#{name} #{id}"
163
+ already[name] = true
164
+ end
165
+ end
166
+
167
+ if File.file?('packed-refs')
168
+ lines = File.readlines('packed-refs')
169
+ lines.each_with_index do |line, i|
170
+ if m = /^(\w{40}) (.*?)$/.match(line)
171
+ next if !Regexp.new('^' + prefix).match(m[2])
172
+ name = m[2].sub("#{prefix}/", '')
173
+
174
+ # Annotated tags in packed-refs include a reference
175
+ # to the commit object on the following line.
176
+ next_line = lines[i + 1]
177
+
178
+ id =
179
+ if next_line && next_line[0] == ?^
180
+ next_line[1..-1].chomp
181
+ else
182
+ m[1]
183
+ end
184
+
185
+ if !already[name]
186
+ refs << "#{name} #{id}"
187
+ already[name] = true
188
+ end
189
+ end
190
+ end
191
+ end
192
+ end
193
+
194
+ refs.join("\n")
195
+ end
196
+
197
+ def file_size(ref)
198
+ try_run { ruby_git.cat_file_size(ref).to_s }
199
+ end
200
+
201
+ def file_type(ref)
202
+ try_run { ruby_git.cat_file_type(ref).to_s }
203
+ end
204
+
205
+ def ruby_git
206
+ @ruby_git_repo ||= Repository.new(@git_dir)
207
+ end
208
+
209
+ private
210
+
211
+ def try_run
212
+ ret = ''
213
+ Timeout.timeout(self.class.git_timeout) do
214
+ ret = yield
215
+ end
216
+ @bytes_read += ret.size
217
+
218
+ #if @bytes_read > 5242880 # 5.megabytes
219
+ # bytes = @bytes_read
220
+ # @bytes_read = 0
221
+ # raise Grit::Git::GitTimeout.new(command, bytes)
222
+ #end
223
+
224
+ ret
225
+ rescue Timeout::Error => e
226
+ bytes = @bytes_read
227
+ @bytes_read = 0
228
+ raise Grit::Git::GitTimeout.new(command, bytes)
229
+ end
230
+
231
+ def looking_for(commit, path = nil)
232
+ tree_sha = ruby_git.get_subtree(rev_parse({}, commit), path)
233
+
234
+ looking_for = []
235
+ ruby_git.get_object_by_sha1(tree_sha).entry.each do |e|
236
+ if path && !(path == '' || path == '.' || path == './')
237
+ file = File.join(path, e.name)
238
+ else
239
+ file = e.name
240
+ end
241
+ file += '/' if e.type == :directory
242
+ looking_for << file
243
+ end
244
+ looking_for
245
+ end
246
+
247
+ def clean_paths(commit_array)
248
+ new_commits = {}
249
+ commit_array.each do |file, sha|
250
+ file = file.chop if file[file.size - 1 , 1] == '/'
251
+ new_commits[file] = sha
252
+ end
253
+ new_commits
254
+ end
255
+
256
+ # TODO
257
+ # git grep -n 'foo' 'master'
258
+ # git log --pretty='raw' --max-count='1' 'master' -- 'LICENSE'
259
+ # git log --pretty='raw' --max-count='1' 'master' -- 'test'
260
+
261
+ end
262
+ end
@@ -0,0 +1,52 @@
1
+ begin
2
+ require 'sequel'
3
+
4
+ module Grit
5
+
6
+ class CommitDb
7
+
8
+ SCHEMA_VERSION = 1
9
+
10
+ attr_accessor :db, :git
11
+
12
+ def initialize(git_obj, index_location = nil)
13
+ @git = git_obj
14
+ db_file = File.join(index_location || @git.git_dir, 'commit_db')
15
+ if !File.exists?(db_file)
16
+ @db = Sequel.open "sqlite:///#{db_file}"
17
+ setup_tables
18
+ else
19
+ @db = Sequel.open "sqlite:///#{db_file}"
20
+ end
21
+ end
22
+
23
+ def rev_list(branch, options)
24
+ end
25
+
26
+ def update_db(branch = nil)
27
+ # find all refs/heads, for each
28
+ # add branch if not there
29
+ # go though all commits in branch
30
+ # add new commit_branches a
31
+ # and commit_nodes for each new one
32
+ # stop if reach commit that already has branch and node links
33
+ end
34
+
35
+ def setup_tables
36
+ @db << "create table meta (meta_key text, meta_value text)"
37
+ @db[:meta] << {:meta_key => 'schema', :meta_value => SCHEMA_VERSION}
38
+
39
+ @db << "create table commits (id integer, sha text, author_date integer)"
40
+ @db << "create table nodes (id integer, path text, type text)"
41
+ @db << "create table branches (id integer, ref text, commit_id integer)"
42
+
43
+ @db << "create table commit_branches (commit_id integer, branch_id integer)"
44
+ @db << "create table commit_nodes (commit_id integer, node_id integer, node_sha string)"
45
+ end
46
+
47
+ end
48
+ end
49
+
50
+ rescue LoadError
51
+ # no commit db
52
+ end
@@ -0,0 +1,353 @@
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
+ attr_accessor :sha
53
+
54
+ def GitObject.from_raw(rawobject, repository = nil)
55
+ case rawobject.type
56
+ when :blob
57
+ return Blob.from_raw(rawobject, repository)
58
+ when :tree
59
+ return Tree.from_raw(rawobject, repository)
60
+ when :commit
61
+ return Commit.from_raw(rawobject, repository)
62
+ when :tag
63
+ return Tag.from_raw(rawobject, repository)
64
+ else
65
+ raise RuntimeError, "got invalid object-type"
66
+ end
67
+ end
68
+
69
+ def initialize
70
+ raise NotImplemented, "abstract class"
71
+ end
72
+
73
+ def type
74
+ raise NotImplemented, "abstract class"
75
+ end
76
+
77
+ def raw_content
78
+ raise NotImplemented, "abstract class"
79
+ end
80
+
81
+ def sha1
82
+ Digest::SHA1.hexdigest("%s %d\0" % \
83
+ [self.type, self.raw_content.length] + \
84
+ self.raw_content)
85
+ end
86
+ end
87
+
88
+ class Blob < GitObject
89
+ attr_accessor :content
90
+
91
+ def self.from_raw(rawobject, repository)
92
+ new(rawobject.content)
93
+ end
94
+
95
+ def initialize(content, repository=nil)
96
+ @content = content
97
+ @repository = repository
98
+ end
99
+
100
+ def type
101
+ :blob
102
+ end
103
+
104
+ def raw_content
105
+ @content
106
+ end
107
+ end
108
+
109
+ class DirectoryEntry
110
+ S_IFMT = 00170000
111
+ S_IFLNK = 0120000
112
+ S_IFREG = 0100000
113
+ S_IFDIR = 0040000
114
+ S_IFGITLINK = 0160000
115
+ attr_accessor :mode, :name, :sha1
116
+ def initialize(mode, filename, sha1o)
117
+ @mode = 0
118
+ mode.each_byte do |i|
119
+ @mode = (@mode << 3) | (i-'0'.getord(0))
120
+ end
121
+ @name = filename
122
+ @sha1 = sha1o
123
+ if ![S_IFLNK, S_IFDIR, S_IFREG, S_IFGITLINK].include?(@mode & S_IFMT)
124
+ raise RuntimeError, "unknown type for directory entry"
125
+ end
126
+ end
127
+
128
+ def type
129
+ case @mode & S_IFMT
130
+ when S_IFGITLINK
131
+ @type = :submodule
132
+ when S_IFLNK
133
+ @type = :link
134
+ when S_IFDIR
135
+ @type = :directory
136
+ when S_IFREG
137
+ @type = :file
138
+ else
139
+ raise RuntimeError, "unknown type for directory entry"
140
+ end
141
+ end
142
+
143
+ def type=(type)
144
+ case @type
145
+ when :link
146
+ @mode = (@mode & ~S_IFMT) | S_IFLNK
147
+ when :directory
148
+ @mode = (@mode & ~S_IFMT) | S_IFDIR
149
+ when :file
150
+ @mode = (@mode & ~S_IFMT) | S_IFREG
151
+ when :submodule
152
+ @mode = (@mode & ~S_IFMT) | S_IFGITLINK
153
+ else
154
+ raise RuntimeError, "invalid type"
155
+ end
156
+ end
157
+
158
+ def format_type
159
+ case type
160
+ when :link
161
+ 'link'
162
+ when :directory
163
+ 'tree'
164
+ when :file
165
+ 'blob'
166
+ when :submodule
167
+ 'commit'
168
+ end
169
+ end
170
+
171
+ def format_mode
172
+ "%06o" % @mode
173
+ end
174
+
175
+ def raw
176
+ "%o %s\0%s" % [@mode, @name, [@sha1].pack("H*")]
177
+ end
178
+ end
179
+
180
+
181
+ def self.read_bytes_until(io, char)
182
+ string = ''
183
+ if RUBY_VERSION > '1.9'
184
+ while ((next_char = io.getc) != char) && !io.eof
185
+ string += next_char
186
+ end
187
+ else
188
+ while ((next_char = io.getc.chr) != char) && !io.eof
189
+ string += next_char
190
+ end
191
+ end
192
+ string
193
+ end
194
+
195
+
196
+ class Tree < GitObject
197
+ attr_accessor :entry
198
+
199
+ def self.from_raw(rawobject, repository=nil)
200
+ raw = StringIO.new(rawobject.content)
201
+
202
+ entries = []
203
+ while !raw.eof?
204
+ mode = Grit::GitRuby.read_bytes_until(raw, ' ')
205
+ file_name = Grit::GitRuby.read_bytes_until(raw, "\0")
206
+ raw_sha = raw.read(20)
207
+ sha = raw_sha.unpack("H*").first
208
+
209
+ entries << DirectoryEntry.new(mode, file_name, sha)
210
+ end
211
+ new(entries, repository)
212
+ end
213
+
214
+ def initialize(entries=[], repository = nil)
215
+ @entry = entries
216
+ @repository = repository
217
+ end
218
+
219
+ def type
220
+ :tree
221
+ end
222
+
223
+ def raw_content
224
+ # TODO: sort correctly
225
+ #@entry.sort { |a,b| a.name <=> b.name }.
226
+ @entry.collect { |e| [[e.format_mode, e.format_type, e.sha1].join(' '), e.name].join("\t") }.join("\n")
227
+ end
228
+
229
+ def actual_raw
230
+ #@entry.collect { |e| e.raw.join(' '), e.name].join("\t") }.join("\n")
231
+ end
232
+ end
233
+
234
+ class Commit < GitObject
235
+ attr_accessor :author, :committer, :tree, :parent, :message, :headers
236
+
237
+ def self.from_raw(rawobject, repository=nil)
238
+ parent = []
239
+ tree = author = committer = nil
240
+
241
+ headers, message = rawobject.content.split(/\n\n/, 2)
242
+ all_headers = headers.split(/\n/).map { |header| header.split(/ /, 2) }
243
+ all_headers.each do |key, value|
244
+ case key
245
+ when "tree"
246
+ tree = value
247
+ when "parent"
248
+ parent.push(value)
249
+ when "author"
250
+ author = UserInfo.new(value)
251
+ when "committer"
252
+ committer = UserInfo.new(value)
253
+ else
254
+ warn "unknown header '%s' in commit %s" % \
255
+ [key, rawobject.sha1.unpack("H*")[0]]
256
+ end
257
+ end
258
+ if not tree && author && committer
259
+ raise RuntimeError, "incomplete raw commit object"
260
+ end
261
+ new(tree, parent, author, committer, message, headers, repository)
262
+ end
263
+
264
+ def initialize(tree, parent, author, committer, message, headers, repository=nil)
265
+ @tree = tree
266
+ @author = author
267
+ @parent = parent
268
+ @committer = committer
269
+ @message = message
270
+ @headers = headers
271
+ @repository = repository
272
+ end
273
+
274
+ def type
275
+ :commit
276
+ end
277
+
278
+ def raw_content
279
+ "tree %s\n%sauthor %s\ncommitter %s\n\n" % [
280
+ @tree,
281
+ @parent.collect { |i| "parent %s\n" % i }.join,
282
+ @author, @committer] + @message
283
+ end
284
+
285
+ def raw_log(sha)
286
+ output = "commit #{sha}\n"
287
+ output += @headers + "\n\n"
288
+ output += @message.split("\n").map { |l| ' ' + l }.join("\n") + "\n\n"
289
+ end
290
+
291
+ end
292
+
293
+ class Tag < GitObject
294
+ attr_accessor :object, :tag, :tagger, :message, :object_type
295
+ attr_writer :type
296
+
297
+ def self.from_raw(rawobject, repository=nil)
298
+
299
+ headers, message = rawobject.content.split(/\n\n/, 2)
300
+ headers = headers.split(/\n/).map { |header| header.split(' ', 2) }
301
+
302
+ object = ''
303
+ type = ''
304
+ tag = ''
305
+ tagger = ''
306
+
307
+ headers.each do |key, value|
308
+ case key
309
+ when "object"
310
+ object = value
311
+ when "type"
312
+ if !["blob", "tree", "commit", "tag"].include?(value)
313
+ raise RuntimeError, "invalid type in tag"
314
+ end
315
+ type = value.to_sym
316
+ when "tag"
317
+ tag = value
318
+ when "tagger"
319
+ tagger = UserInfo.new(value)
320
+ else
321
+ warn "unknown header '%s' in tag" % \
322
+ [key, rawobject.sha1.unpack("H*")[0]]
323
+ end
324
+ end
325
+
326
+ if not object && type && tag && tagger
327
+ raise RuntimeError, "incomplete raw tag object"
328
+ end
329
+ new(object, type, tag, tagger, message, repository)
330
+ end
331
+
332
+ def initialize(object, type, tag, tagger, message, repository=nil)
333
+ @object = object
334
+ @type = type
335
+ @object_type = type
336
+ @tag = tag
337
+ @tagger = tagger
338
+ @repository = repository
339
+ @message = message
340
+ end
341
+
342
+ def raw_content
343
+ ("object %s\ntype %s\ntag %s\ntagger %s\n\n" % \
344
+ [@object, @type, @tag, @tagger]) + @message.to_s
345
+ end
346
+
347
+ def type
348
+ :tag
349
+ end
350
+ end
351
+
352
+ end
353
+ end