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.
- checksums.yaml +7 -0
- data/.index +82 -0
- data/HISTORY.md +14 -0
- data/lib/smeagol.rb +2 -1
- data/lib/smeagol/app.rb +1 -1
- data/lib/smeagol/gollum/file.rb +1 -1
- data/lib/smeagol/wiki.rb +1 -0
- data/vendor/grit/lib/grit.rb +75 -0
- data/vendor/grit/lib/grit/actor.rb +52 -0
- data/vendor/grit/lib/grit/blame.rb +70 -0
- data/vendor/grit/lib/grit/blob.rb +126 -0
- data/vendor/grit/lib/grit/commit.rb +313 -0
- data/vendor/grit/lib/grit/commit_stats.rb +128 -0
- data/vendor/grit/lib/grit/config.rb +44 -0
- data/vendor/grit/lib/grit/diff.rb +79 -0
- data/vendor/grit/lib/grit/errors.rb +10 -0
- data/vendor/grit/lib/grit/git-ruby.rb +262 -0
- data/vendor/grit/lib/grit/git-ruby/commit_db.rb +52 -0
- data/vendor/grit/lib/grit/git-ruby/git_object.rb +353 -0
- data/vendor/grit/lib/grit/git-ruby/internal/file_window.rb +58 -0
- data/vendor/grit/lib/grit/git-ruby/internal/loose.rb +137 -0
- data/vendor/grit/lib/grit/git-ruby/internal/pack.rb +397 -0
- data/vendor/grit/lib/grit/git-ruby/internal/raw_object.rb +44 -0
- data/vendor/grit/lib/grit/git-ruby/repository.rb +775 -0
- data/vendor/grit/lib/grit/git.rb +501 -0
- data/vendor/grit/lib/grit/index.rb +222 -0
- data/vendor/grit/lib/grit/lazy.rb +35 -0
- data/vendor/grit/lib/grit/merge.rb +45 -0
- data/vendor/grit/lib/grit/ref.rb +78 -0
- data/vendor/grit/lib/grit/repo.rb +709 -0
- data/vendor/grit/lib/grit/ruby1.9.rb +7 -0
- data/vendor/grit/lib/grit/status.rb +153 -0
- data/vendor/grit/lib/grit/submodule.rb +88 -0
- data/vendor/grit/lib/grit/tag.rb +102 -0
- data/vendor/grit/lib/grit/tree.rb +125 -0
- metadata +125 -56
- 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,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
|