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.
data/lib/grit/blame.rb ADDED
@@ -0,0 +1,61 @@
1
+ module Grit
2
+
3
+ class Blame
4
+
5
+ attr_reader :lines
6
+
7
+ def initialize(repo, file, commit)
8
+ @repo = repo
9
+ @file = file
10
+ @commit = commit
11
+ @lines = []
12
+ load_blame
13
+ end
14
+
15
+ def load_blame
16
+ output = @repo.git.blame({'p' => true}, @commit, '--', @file)
17
+ process_raw_blame(output)
18
+ end
19
+
20
+ def process_raw_blame(output)
21
+ lines, final = [], []
22
+ info, commits = {}, {}
23
+
24
+ # process the output
25
+ output.split("\n").each do |line|
26
+ if line[0, 1] == "\t"
27
+ lines << line[1, line.size]
28
+ elsif m = /^(\w{40}) (\d+) (\d+)/.match(line)
29
+ if !commits[m[1]]
30
+ commits[m[1]] = @repo.commit(m[1])
31
+ end
32
+ info[m[3].to_i] = [commits[m[1]], m[2].to_i]
33
+ end
34
+ end
35
+
36
+ # get it together
37
+ info.sort.each do |lineno, commit|
38
+ final << BlameLine.new(lineno, commit[1], commit[0], lines[lineno - 1])
39
+ end
40
+
41
+ @lines = final
42
+ end
43
+
44
+ # Pretty object inspection
45
+ def inspect
46
+ %Q{#<Grit::Blame "#{@file} <#{@commit}>">}
47
+ end
48
+
49
+ class BlameLine
50
+ attr_accessor :lineno, :oldlineno, :commit, :line
51
+ def initialize(lineno, oldlineno, commit, line)
52
+ @lineno = lineno
53
+ @oldlineno = oldlineno
54
+ @commit = commit
55
+ @line = line
56
+ end
57
+ end
58
+
59
+ end # Blame
60
+
61
+ end # Grit
data/lib/grit/blob.rb ADDED
@@ -0,0 +1,126 @@
1
+ module Grit
2
+
3
+ class Blob
4
+ DEFAULT_MIME_TYPE = "text/plain"
5
+
6
+ attr_reader :id
7
+ attr_reader :mode
8
+ attr_reader :name
9
+
10
+ # Create an unbaked Blob containing just the specified attributes
11
+ # +repo+ is the Repo
12
+ # +atts+ is a Hash of instance variable data
13
+ #
14
+ # Returns Grit::Blob (unbaked)
15
+ def self.create(repo, atts)
16
+ self.allocate.create_initialize(repo, atts)
17
+ end
18
+
19
+ # Initializer for Blob.create
20
+ # +repo+ is the Repo
21
+ # +atts+ is a Hash of instance variable data
22
+ #
23
+ # Returns Grit::Blob (unbaked)
24
+ def create_initialize(repo, atts)
25
+ @repo = repo
26
+ atts.each do |k, v|
27
+ instance_variable_set("@#{k}".to_sym, v)
28
+ end
29
+ self
30
+ end
31
+
32
+ # The size of this blob in bytes
33
+ #
34
+ # Returns Integer
35
+ def size
36
+ @size ||= @repo.git.cat_file({:s => true}, id).chomp.to_i
37
+ end
38
+
39
+ # The binary contents of this blob.
40
+ #
41
+ # Returns String
42
+ def data
43
+ @data ||= @repo.git.cat_file({:p => true}, id)
44
+ end
45
+
46
+ # The mime type of this file (based on the filename)
47
+ #
48
+ # Returns String
49
+ def mime_type
50
+ guesses = MIME::Types.type_for(self.name) rescue []
51
+ guesses.first ? guesses.first.simplified : DEFAULT_MIME_TYPE
52
+ end
53
+
54
+ # The blame information for the given file at the given commit
55
+ #
56
+ # Returns Array: [Grit::Commit, Array: [<line>]]
57
+ def self.blame(repo, commit, file)
58
+ data = repo.git.blame({:p => true}, commit, '--', file)
59
+
60
+ commits = {}
61
+ blames = []
62
+ info = nil
63
+
64
+ data.split("\n").each do |line|
65
+ parts = line.split(/\s+/, 2)
66
+ case parts.first
67
+ when /^[0-9A-Fa-f]{40}$/
68
+ case line
69
+ when /^([0-9A-Fa-f]{40}) (\d+) (\d+) (\d+)$/
70
+ _, id, origin_line, final_line, group_lines = *line.match(/^([0-9A-Fa-f]{40}) (\d+) (\d+) (\d+)$/)
71
+ info = {:id => id}
72
+ blames << [nil, []]
73
+ when /^([0-9A-Fa-f]{40}) (\d+) (\d+)$/
74
+ _, id, origin_line, final_line = *line.match(/^([0-9A-Fa-f]{40}) (\d+) (\d+)$/)
75
+ info = {:id => id}
76
+ end
77
+ when /^(author|committer)/
78
+ case parts.first
79
+ when /^(.+)-mail$/
80
+ info["#{$1}_email".intern] = parts.last
81
+ when /^(.+)-time$/
82
+ info["#{$1}_date".intern] = Time.at(parts.last.to_i)
83
+ when /^(author|committer)$/
84
+ info[$1.intern] = parts.last
85
+ end
86
+ when /^filename/
87
+ info[:filename] = parts.last
88
+ when /^summary/
89
+ info[:summary] = parts.last
90
+ when ''
91
+ c = commits[info[:id]]
92
+ unless c
93
+ c = Commit.create(repo, :id => info[:id],
94
+ :author => Actor.from_string(info[:author] + ' ' + info[:author_email]),
95
+ :authored_date => info[:author_date],
96
+ :committer => Actor.from_string(info[:committer] + ' ' + info[:committer_email]),
97
+ :committed_date => info[:committer_date],
98
+ :message => info[:summary])
99
+ commits[info[:id]] = c
100
+ end
101
+ _, text = *line.match(/^\t(.*)$/)
102
+ blames.last[0] = c
103
+ blames.last[1] << text
104
+ info = nil
105
+ end
106
+ end
107
+
108
+ blames
109
+ end
110
+
111
+ def basename
112
+ File.basename(name)
113
+ end
114
+
115
+ # Pretty object inspection
116
+ def inspect
117
+ %Q{#<Grit::Blob "#{@id}">}
118
+ end
119
+
120
+ # Compares blobs by name
121
+ def <=>(other)
122
+ name <=> other.name
123
+ end
124
+ end # Blob
125
+
126
+ end # Grit
@@ -0,0 +1,294 @@
1
+ module Grit
2
+
3
+ class Commit
4
+ extend Lazy
5
+
6
+ attr_reader :id
7
+ attr_reader :repo
8
+ lazy_reader :parents
9
+ lazy_reader :tree
10
+ lazy_reader :author
11
+ lazy_reader :authored_date
12
+ lazy_reader :committer
13
+ lazy_reader :committed_date
14
+ lazy_reader :message
15
+ lazy_reader :short_message
16
+ lazy_reader :author_string
17
+
18
+ # Parses output from the `git-cat-file --batch'.
19
+ #
20
+ # repo - Grit::Repo instance.
21
+ # sha - String SHA of the Commit.
22
+ # size - Fixnum size of the object.
23
+ # object - Parsed String output from `git cat-file --batch`.
24
+ #
25
+ # Returns an Array of Grit::Commit objects.
26
+ def self.parse_batch(repo, sha, size, object)
27
+ info, message = object.split("\n\n", 2)
28
+
29
+ lines = info.split("\n")
30
+ tree = lines.shift.split(' ', 2).last
31
+ parents = []
32
+ parents << lines.shift[7..-1] while lines.first[0, 6] == 'parent'
33
+ author, authored_date = Grit::Commit.actor(lines.shift)
34
+ committer, committed_date = Grit::Commit.actor(lines.shift)
35
+
36
+ Grit::Commit.new(
37
+ repo, sha, parents, tree,
38
+ author, authored_date,
39
+ committer, committed_date,
40
+ message.to_s.split("\n"))
41
+ end
42
+
43
+ # Instantiate a new Commit
44
+ # +id+ is the id of the commit
45
+ # +parents+ is an array of commit ids (will be converted into Commit instances)
46
+ # +tree+ is the correspdonding tree id (will be converted into a Tree object)
47
+ # +author+ is the author string
48
+ # +authored_date+ is the authored Time
49
+ # +committer+ is the committer string
50
+ # +committed_date+ is the committed Time
51
+ # +message+ is an array of commit message lines
52
+ #
53
+ # Returns Grit::Commit (baked)
54
+ def initialize(repo, id, parents, tree, author, authored_date, committer, committed_date, message)
55
+ @repo = repo
56
+ @id = id
57
+ @parents = parents.map { |p| Commit.create(repo, :id => p) }
58
+ @tree = Tree.create(repo, :id => tree)
59
+ @author = author
60
+ @authored_date = authored_date
61
+ @committer = committer
62
+ @committed_date = committed_date
63
+ @message = message.join("\n")
64
+ @short_message = message.select { |x| !x.strip.empty? }[0] || ''
65
+ end
66
+
67
+ def id_abbrev
68
+ @id_abbrev ||= @repo.git.rev_parse({}, self.id).chomp[0, 7]
69
+ end
70
+
71
+ # Create an unbaked Commit containing just the specified attributes
72
+ # +repo+ is the Repo
73
+ # +atts+ is a Hash of instance variable data
74
+ #
75
+ # Returns Grit::Commit (unbaked)
76
+ def self.create(repo, atts)
77
+ self.allocate.create_initialize(repo, atts)
78
+ end
79
+
80
+ # Initializer for Commit.create
81
+ # +repo+ is the Repo
82
+ # +atts+ is a Hash of instance variable data
83
+ #
84
+ # Returns Grit::Commit (unbaked)
85
+ def create_initialize(repo, atts)
86
+ @repo = repo
87
+ atts.each do |k, v|
88
+ instance_variable_set("@#{k}", v)
89
+ end
90
+ self
91
+ end
92
+
93
+ def lazy_source
94
+ self.class.find_all(@repo, @id, {:max_count => 1}).first
95
+ end
96
+
97
+ # Count the number of commits reachable from this ref
98
+ # +repo+ is the Repo
99
+ # +ref+ is the ref from which to begin (SHA1 or name)
100
+ #
101
+ # Returns Integer
102
+ def self.count(repo, ref)
103
+ repo.git.rev_list({}, ref).size / 41
104
+ end
105
+
106
+ # Find all commits matching the given criteria.
107
+ # +repo+ is the Repo
108
+ # +ref+ is the ref from which to begin (SHA1 or name) or nil for --all
109
+ # +options+ is a Hash of optional arguments to git
110
+ # :max_count is the maximum number of commits to fetch
111
+ # :skip is the number of commits to skip
112
+ #
113
+ # Returns Grit::Commit[] (baked)
114
+ def self.find_all(repo, ref, options = {})
115
+ allowed_options = [:max_count, :skip, :since]
116
+
117
+ default_options = {:pretty => "raw"}
118
+ actual_options = default_options.merge(options)
119
+
120
+ if ref
121
+ output = repo.git.rev_list(actual_options, ref)
122
+ else
123
+ output = repo.git.rev_list(actual_options.merge(:all => true))
124
+ end
125
+
126
+ self.list_from_string(repo, output)
127
+ rescue Grit::GitRuby::Repository::NoSuchShaFound
128
+ []
129
+ end
130
+
131
+ # Parse out commit information into an array of baked Commit objects
132
+ # +repo+ is the Repo
133
+ # +text+ is the text output from the git command (raw format)
134
+ #
135
+ # Returns Grit::Commit[] (baked)
136
+ #
137
+ # really should re-write this to be more accepting of non-standard commit messages
138
+ # - it broke when 'encoding' was introduced - not sure what else might show up
139
+ #
140
+ def self.list_from_string(repo, text)
141
+ lines = text.split("\n")
142
+
143
+ commits = []
144
+
145
+ while !lines.empty?
146
+ id = lines.shift.split.last
147
+ tree = lines.shift.split.last
148
+
149
+ parents = []
150
+ parents << lines.shift.split.last while lines.first =~ /^parent/
151
+
152
+ author, authored_date = self.actor(lines.shift)
153
+ committer, committed_date = self.actor(lines.shift)
154
+
155
+ # not doing anything with this yet, but it's sometimes there
156
+ encoding = lines.shift.split.last if lines.first =~ /^encoding/
157
+
158
+ lines.shift
159
+
160
+ message_lines = []
161
+ message_lines << lines.shift[4..-1] while lines.first =~ /^ {4}/
162
+
163
+ lines.shift while lines.first && lines.first.empty?
164
+
165
+ commits << Commit.new(repo, id, parents, tree, author, authored_date, committer, committed_date, message_lines)
166
+ end
167
+
168
+ commits
169
+ end
170
+
171
+ # Show diffs between two trees.
172
+ #
173
+ # repo - The current Grit::Repo instance.
174
+ # a - A String named commit.
175
+ # b - An optional String named commit. Passing an array assumes you
176
+ # wish to omit the second named commit and limit the diff to the
177
+ # given paths.
178
+ # paths - An optional Array of paths to limit the diff.
179
+ # options - An optional Hash of options. Merged into {:full_index => true}.
180
+ #
181
+ # Returns Grit::Diff[] (baked)
182
+ def self.diff(repo, a, b = nil, paths = [], options = {})
183
+ if b.is_a?(Array)
184
+ paths = b
185
+ b = nil
186
+ end
187
+ paths.unshift("--") unless paths.empty?
188
+ paths.unshift(b) unless b.nil?
189
+ paths.unshift(a)
190
+ options = {:full_index => true}.update(options)
191
+ text = repo.git.diff(options, *paths)
192
+ Diff.list_from_string(repo, text)
193
+ end
194
+
195
+ def show
196
+ if parents.size > 1
197
+ diff = @repo.git.native("diff #{parents[0].id}...#{parents[1].id}", {:full_index => true})
198
+ else
199
+ diff = @repo.git.show({:full_index => true, :pretty => 'raw'}, @id)
200
+ end
201
+
202
+ if diff =~ /diff --git a/
203
+ diff = diff.sub(/.+?(diff --git a)/m, '\1')
204
+ else
205
+ diff = ''
206
+ end
207
+ Diff.list_from_string(@repo, diff)
208
+ end
209
+
210
+ # Shows diffs between the commit's parent and the commit.
211
+ #
212
+ # options - An optional Hash of options, passed to Grit::Commit.diff.
213
+ #
214
+ # Returns Grit::Diff[] (baked)
215
+ def diffs(options = {})
216
+ if parents.empty?
217
+ show
218
+ else
219
+ self.class.diff(@repo, parents.first.id, @id, [], options)
220
+ end
221
+ end
222
+
223
+ def stats
224
+ stats = @repo.commit_stats(self.sha, 1)[0][-1]
225
+ end
226
+
227
+ # Convert this Commit to a String which is just the SHA1 id
228
+ def to_s
229
+ @id
230
+ end
231
+
232
+ def sha
233
+ @id
234
+ end
235
+
236
+ def date
237
+ @committed_date
238
+ end
239
+
240
+ def to_patch
241
+ @repo.git.format_patch({'1' => true, :stdout => true}, to_s)
242
+ end
243
+
244
+ def notes
245
+ ret = {}
246
+ notes = Note.find_all(@repo)
247
+ notes.each do |note|
248
+ if n = note.commit.tree/(self.id)
249
+ ret[note.name] = n.data
250
+ end
251
+ end
252
+ ret
253
+ end
254
+
255
+ # Pretty object inspection
256
+ def inspect
257
+ %Q{#<Grit::Commit "#{@id}">}
258
+ end
259
+
260
+ # private
261
+
262
+ # Parse out the actor (author or committer) info
263
+ #
264
+ # Returns [String (actor name and email), Time (acted at time)]
265
+ def self.actor(line)
266
+ m, actor, epoch = *line.match(/^.+? (.*) (\d+) .*$/)
267
+ [Actor.from_string(actor), Time.at(epoch.to_i)]
268
+ end
269
+
270
+ def author_string
271
+ "%s <%s> %s %+05d" % [author.name, author.email, authored_date.to_i, 800]
272
+ end
273
+
274
+ def to_hash
275
+ {
276
+ 'id' => id,
277
+ 'parents' => parents.map { |p| { 'id' => p.id } },
278
+ 'tree' => tree.id,
279
+ 'message' => message,
280
+ 'author' => {
281
+ 'name' => author.name,
282
+ 'email' => author.email
283
+ },
284
+ 'committer' => {
285
+ 'name' => committer.name,
286
+ 'email' => committer.email
287
+ },
288
+ 'authored_date' => authored_date.xmlschema,
289
+ 'committed_date' => committed_date.xmlschema,
290
+ }
291
+ end
292
+ end # Commit
293
+
294
+ end # Grit