schleyfox-grit 2.3.0

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.
@@ -0,0 +1,128 @@
1
+ module Grit
2
+
3
+ class CommitStats
4
+
5
+ attr_reader :id, :files, :additions, :deletions, :total
6
+
7
+ # Instantiate a new CommitStats
8
+ # +id+ is the id of the commit
9
+ # +files+ is an array of :
10
+ # [ [filename, adds, deletes, total],
11
+ # [filename, adds, deletes, total],
12
+ # [filename, adds, deletes, total] ]
13
+ #
14
+ # Returns Grit::CommitStats (baked)
15
+ def initialize(repo, id, files)
16
+ @repo = repo
17
+ @id = id
18
+ @files = files
19
+ @additions = files.inject(0) { |total, a| total += a[1] }
20
+ @deletions = files.inject(0) { |total, a| total += a[2] }
21
+ @total = files.inject(0) { |total, a| total += a[3] }
22
+ end
23
+
24
+ # Find all commit stats matching the given criteria.
25
+ # +repo+ is the Repo
26
+ # +ref+ is the ref from which to begin (SHA1 or name) or nil for --all
27
+ # +options+ is a Hash of optional arguments to git
28
+ # :max_count is the maximum number of commits to fetch
29
+ # :skip is the number of commits to skip
30
+ #
31
+ # Returns assoc array [sha, Grit::Commit[] (baked)]
32
+ def self.find_all(repo, ref, options = {})
33
+ allowed_options = [:max_count, :skip, :since]
34
+
35
+ default_options = {:numstat => true}
36
+ actual_options = default_options.merge(options)
37
+
38
+ if ref
39
+ output = repo.git.log(actual_options, ref)
40
+ else
41
+ output = repo.git.log(actual_options.merge(:all => true))
42
+ end
43
+
44
+ self.list_from_string(repo, output)
45
+ end
46
+
47
+ # Parse out commit information into an array of baked Commit objects
48
+ # +repo+ is the Repo
49
+ # +text+ is the text output from the git command (raw format)
50
+ #
51
+ # Returns assoc array [sha, Grit::Commit[] (baked)]
52
+ def self.list_from_string(repo, text)
53
+ lines = text.split("\n")
54
+
55
+ commits = []
56
+
57
+ while !lines.empty?
58
+ id = lines.shift.split.last
59
+
60
+ lines.shift
61
+ lines.shift
62
+ lines.shift
63
+
64
+ message_lines = []
65
+ message_lines << lines.shift[4..-1] while lines.first =~ /^ {4}/ || lines.first == ''
66
+
67
+ lines.shift while lines.first && lines.first.empty?
68
+
69
+ files = []
70
+ while lines.first =~ /^([-\d]+)\s+([-\d]+)\s+(.+)/
71
+ (additions, deletions, filename) = lines.shift.split
72
+ additions = additions.to_i
73
+ deletions = deletions.to_i
74
+ total = additions + deletions
75
+ files << [filename, additions, deletions, total]
76
+ end
77
+
78
+ lines.shift while lines.first && lines.first.empty?
79
+
80
+ commits << [id, CommitStats.new(repo, id, files)]
81
+ end
82
+
83
+ commits
84
+ end
85
+
86
+ # Pretty object inspection
87
+ def inspect
88
+ %Q{#<Grit::CommitStats "#{@id}">}
89
+ end
90
+
91
+ # Convert to an easy-to-traverse structure
92
+ def to_diffstat
93
+ files.map do |metadata|
94
+ DiffStat.new(*metadata)
95
+ end
96
+ end
97
+
98
+ # private
99
+
100
+ def to_hash
101
+ {
102
+ 'id' => id,
103
+ 'files' => files,
104
+ 'additions' => additions,
105
+ 'deletions' => deletions,
106
+ 'total' => total
107
+ }
108
+ end
109
+
110
+ end # CommitStats
111
+
112
+ class DiffStat
113
+ attr_reader :filename, :additions, :deletions
114
+
115
+ def initialize(filename, additions, deletions, total=nil)
116
+ @filename, @additions, @deletions = filename, additions, deletions
117
+ end
118
+
119
+ def net
120
+ additions - deletions
121
+ end
122
+
123
+ def inspect
124
+ "#{filename}: +#{additions} -#{deletions}"
125
+ end
126
+ end
127
+
128
+ end # Grit
@@ -0,0 +1,44 @@
1
+ module Grit
2
+
3
+ class Config
4
+ def initialize(repo)
5
+ @repo = repo
6
+ end
7
+
8
+ def []=(key, value)
9
+ @repo.git.config({}, key, value)
10
+ @data = nil
11
+ end
12
+
13
+ def [](key)
14
+ data[key]
15
+ end
16
+
17
+ def fetch(key, default = nil)
18
+ data[key] || default || raise(IndexError.new("key not found"))
19
+ end
20
+
21
+ def keys
22
+ data.keys
23
+ end
24
+
25
+ protected
26
+ def data
27
+ @data ||= load_config
28
+ end
29
+
30
+ def load_config
31
+ hash = {}
32
+ config_lines.map do |line|
33
+ key, value = line.split(/=/, 2)
34
+ hash[key] = value
35
+ end
36
+ hash
37
+ end
38
+
39
+ def config_lines
40
+ @repo.git.config(:list => true).split(/\n/)
41
+ end
42
+ end # Config
43
+
44
+ end # Grit
data/lib/grit/diff.rb ADDED
@@ -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,272 @@
1
+ require 'grit/git-ruby/repository'
2
+ require 'grit/git-ruby/file_index'
3
+
4
+ module Grit
5
+
6
+ # the functions in this module intercept the calls to git binary
7
+ # made buy the grit objects and attempts to run them in pure ruby
8
+ # if it will be faster, or if the git binary is not available (!!TODO!!)
9
+ module GitRuby
10
+
11
+ attr_accessor :ruby_git_repo, :git_file_index
12
+
13
+ def init(options, *args)
14
+ if options.size == 0
15
+ Grit::GitRuby::Repository.init(@git_dir)
16
+ else
17
+ method_missing('init', options, *args)
18
+ end
19
+ end
20
+
21
+ def cat_file(options, sha)
22
+ if options[:t]
23
+ file_type(sha)
24
+ elsif options[:s]
25
+ file_size(sha)
26
+ elsif options[:p]
27
+ try_run { ruby_git.cat_file(sha) }
28
+ end
29
+ rescue Grit::GitRuby::Repository::NoSuchShaFound
30
+ ''
31
+ end
32
+
33
+ def cat_ref(options, ref)
34
+ sha = rev_parse({}, ref)
35
+ cat_file(options, sha)
36
+ end
37
+
38
+ # lib/grit/tree.rb:16: output = repo.git.ls_tree({}, treeish, *paths)
39
+ def ls_tree(options, treeish, *paths)
40
+ sha = rev_parse({}, treeish)
41
+ ruby_git.ls_tree(sha, paths.flatten, options.delete(:r))
42
+ rescue Grit::GitRuby::Repository::NoSuchShaFound
43
+ ''
44
+ end
45
+
46
+ # git diff --full-index 'ec037431382e83c3e95d4f2b3d145afbac8ea55d' 'f1ec1aea10986159456846b8a05615b87828d6c6'
47
+ def diff(options, sha1, sha2 = nil)
48
+ try_run { ruby_git.diff(sha1, sha2, options) }
49
+ end
50
+
51
+ def rev_list(options, ref = 'master')
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)
55
+ return method_missing('rev-list', options, ref)
56
+ elsif (options.size == 0)
57
+ # pure rev-list
58
+ begin
59
+ return file_index.commits_from(rev_parse({}, ref)).join("\n") + "\n"
60
+ rescue
61
+ return method_missing('rev-list', options, ref)
62
+ end
63
+ else
64
+ aref = rev_parse({}, ref)
65
+ if aref.is_a? Array
66
+ return method_missing('rev-list', options, ref)
67
+ else
68
+ return try_run { ruby_git.rev_list(aref, options) }
69
+ end
70
+ end
71
+ end
72
+
73
+ def rev_parse(options, string)
74
+ raise RuntimeError, "invalid string: #{string}" unless string.is_a?(String)
75
+
76
+ if string =~ /\.\./
77
+ (sha1, sha2) = string.split('..')
78
+ return [rev_parse({}, sha1), rev_parse({}, sha2)]
79
+ end
80
+
81
+ if /^[0-9a-f]{40}$/.match(string) # passing in a sha - just no-op it
82
+ return string.chomp
83
+ end
84
+
85
+ head = File.join(@git_dir, 'refs', 'heads', string)
86
+ return File.read(head).chomp if File.file?(head)
87
+
88
+ head = File.join(@git_dir, 'refs', 'remotes', string)
89
+ return File.read(head).chomp if File.file?(head)
90
+
91
+ head = File.join(@git_dir, 'refs', 'tags', string)
92
+ return File.read(head).chomp if File.file?(head)
93
+
94
+ ## check packed-refs file, too
95
+ packref = File.join(@git_dir, 'packed-refs')
96
+ if File.file?(packref)
97
+ File.readlines(packref).each do |line|
98
+ if m = /^(\w{40}) refs\/.+?\/(.*?)$/.match(line)
99
+ next if !Regexp.new(Regexp.escape(string) + '$').match(m[3])
100
+ return m[1].chomp
101
+ end
102
+ end
103
+ end
104
+
105
+ ## !! more - partials and such !!
106
+
107
+ # revert to calling git - grr
108
+ return method_missing('rev-parse', options, string).chomp
109
+ end
110
+
111
+ def refs(options, prefix)
112
+ refs = []
113
+ already = {}
114
+ Dir.chdir(@git_dir) do
115
+ files = Dir.glob(prefix + '/**/*')
116
+ files.each do |ref|
117
+ next if !File.file?(ref)
118
+ id = File.read(ref).chomp
119
+ name = ref.sub("#{prefix}/", '')
120
+ if !already[name]
121
+ refs << "#{name} #{id}"
122
+ already[name] = true
123
+ end
124
+ end
125
+
126
+ if File.file?('packed-refs')
127
+ File.readlines('packed-refs').each do |line|
128
+ if m = /^(\w{40}) (.*?)$/.match(line)
129
+ next if !Regexp.new('^' + prefix).match(m[2])
130
+ name = m[2].sub("#{prefix}/", '')
131
+ if !already[name]
132
+ refs << "#{name} #{m[1]}"
133
+ already[name] = true
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ refs.join("\n")
141
+ end
142
+
143
+ def tags(options, prefix)
144
+ refs = []
145
+ already = {}
146
+
147
+ Dir.chdir(repo.path) do
148
+ files = Dir.glob(prefix + '/**/*')
149
+
150
+ files.each do |ref|
151
+ next if !File.file?(ref)
152
+
153
+ id = File.read(ref).chomp
154
+ name = ref.sub("#{prefix}/", '')
155
+
156
+ if !already[name]
157
+ refs << "#{name} #{id}"
158
+ already[name] = true
159
+ end
160
+ end
161
+
162
+ if File.file?('packed-refs')
163
+ lines = File.readlines('packed-refs')
164
+ lines.each_with_index do |line, i|
165
+ if m = /^(\w{40}) (.*?)$/.match(line)
166
+ next if !Regexp.new('^' + prefix).match(m[2])
167
+ name = m[2].sub("#{prefix}/", '')
168
+
169
+ # Annotated tags in packed-refs include a reference
170
+ # to the commit object on the following line.
171
+ next_line = lines[i + 1]
172
+
173
+ id =
174
+ if next_line && next_line[0] == ?^
175
+ next_line[1..-1].chomp
176
+ else
177
+ m[1]
178
+ end
179
+
180
+ if !already[name]
181
+ refs << "#{name} #{id}"
182
+ already[name] = true
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
188
+
189
+ refs.join("\n")
190
+ end
191
+
192
+ def file_size(ref)
193
+ try_run { ruby_git.cat_file_size(ref).to_s }
194
+ end
195
+
196
+ def file_type(ref)
197
+ try_run { ruby_git.cat_file_type(ref).to_s }
198
+ end
199
+
200
+ def blame_tree(commit, path = nil)
201
+ begin
202
+ path = [path].join('/').to_s + '/' if (path && path != '')
203
+ path = '' if !path.is_a? String
204
+ commits = file_index.last_commits(rev_parse({}, commit), looking_for(commit, path))
205
+ clean_paths(commits)
206
+ rescue FileIndex::IndexFileNotFound
207
+ {}
208
+ end
209
+ end
210
+
211
+ def file_index
212
+ @git_file_index ||= FileIndex.new(@git_dir)
213
+ end
214
+
215
+ def ruby_git
216
+ @ruby_git_repo ||= Repository.new(@git_dir)
217
+ end
218
+
219
+ private
220
+
221
+ def try_run
222
+ ret = ''
223
+ Timeout.timeout(self.class.git_timeout) do
224
+ ret = yield
225
+ end
226
+ @bytes_read += ret.size
227
+
228
+ #if @bytes_read > 5242880 # 5.megabytes
229
+ # bytes = @bytes_read
230
+ # @bytes_read = 0
231
+ # raise Grit::Git::GitTimeout.new(command, bytes)
232
+ #end
233
+
234
+ ret
235
+ rescue Timeout::Error => e
236
+ bytes = @bytes_read
237
+ @bytes_read = 0
238
+ raise Grit::Git::GitTimeout.new(command, bytes)
239
+ end
240
+
241
+ def looking_for(commit, path = nil)
242
+ tree_sha = ruby_git.get_subtree(rev_parse({}, commit), path)
243
+
244
+ looking_for = []
245
+ ruby_git.get_object_by_sha1(tree_sha).entry.each do |e|
246
+ if path && !(path == '' || path == '.' || path == './')
247
+ file = File.join(path, e.name)
248
+ else
249
+ file = e.name
250
+ end
251
+ file += '/' if e.type == :directory
252
+ looking_for << file
253
+ end
254
+ looking_for
255
+ end
256
+
257
+ def clean_paths(commit_array)
258
+ new_commits = {}
259
+ commit_array.each do |file, sha|
260
+ file = file.chop if file[file.size - 1 , 1] == '/'
261
+ new_commits[file] = sha
262
+ end
263
+ new_commits
264
+ end
265
+
266
+ # TODO
267
+ # git grep -n 'foo' 'master'
268
+ # git log --pretty='raw' --max-count='1' 'master' -- 'LICENSE'
269
+ # git log --pretty='raw' --max-count='1' 'master' -- 'test'
270
+
271
+ end
272
+ end