rhomobile-grit 2.4.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/grit/actor.rb ADDED
@@ -0,0 +1,52 @@
1
+ module Grit
2
+
3
+ class Actor
4
+ attr_reader :name
5
+ attr_reader :email
6
+
7
+ def initialize(name, email)
8
+ @name = name
9
+ @email = email
10
+ end
11
+ alias_method :to_s, :name
12
+
13
+ # Create an Actor from a string.
14
+ #
15
+ # str - The String in this format: 'John Doe <jdoe@example.com>'
16
+ #
17
+ # Returns Git::Actor.
18
+ def self.from_string(str)
19
+ case str
20
+ when /<.+>/
21
+ m, name, email = *str.match(/(.*) <(.+?)>/)
22
+ return self.new(name, email)
23
+ else
24
+ return self.new(str, nil)
25
+ end
26
+ end
27
+
28
+ # Outputs an actor string for Git commits.
29
+ #
30
+ # actor = Actor.new('bob', 'bob@email.com')
31
+ # actor.output(time) # => "bob <bob@email.com> UNIX_TIME +0700"
32
+ #
33
+ # time - The Time the commit was authored or committed.
34
+ #
35
+ # Returns a String.
36
+ def output(time)
37
+ out = @name.to_s.dup
38
+ if @email
39
+ out << " <#{@email}>"
40
+ end
41
+ hours = (time.utc_offset.to_f / 3600).to_i # 60 * 60, seconds to hours
42
+ rem = time.utc_offset.abs % 3600
43
+ out << " #{time.to_i} #{hours >= 0 ? :+ : :-}#{hours.abs.to_s.rjust(2, '0')}#{rem.to_s.rjust(2, '0')}"
44
+ end
45
+
46
+ # Pretty object inspection
47
+ def inspect
48
+ %Q{#<Grit::Actor "#{@name} <#{@email}>">}
49
+ end
50
+ end # Actor
51
+
52
+ end # Grit
data/lib/grit/blame.rb ADDED
@@ -0,0 +1,66 @@
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
+ commit_id, old_lineno, lineno = m[1], m[2].to_i, m[3].to_i
30
+ commits[commit_id] = nil if !commits.key?(commit_id)
31
+ info[lineno] = [commit_id, old_lineno]
32
+ end
33
+ end
34
+
35
+ # load all commits in single call
36
+ @repo.batch(*commits.keys).each do |commit|
37
+ commits[commit.id] = commit
38
+ end
39
+
40
+ # get it together
41
+ info.sort.each do |lineno, (commit_id, old_lineno)|
42
+ commit = commits[commit_id]
43
+ final << BlameLine.new(lineno, old_lineno, commit, lines[lineno - 1])
44
+ end
45
+
46
+ @lines = final
47
+ end
48
+
49
+ # Pretty object inspection
50
+ def inspect
51
+ %Q{#<Grit::Blame "#{@file} <#{@commit}>">}
52
+ end
53
+
54
+ class BlameLine
55
+ attr_accessor :lineno, :oldlineno, :commit, :line
56
+ def initialize(lineno, oldlineno, commit, line)
57
+ @lineno = lineno
58
+ @oldlineno = oldlineno
59
+ @commit = commit
60
+ @line = line
61
+ end
62
+ end
63
+
64
+ end # Blame
65
+
66
+ 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, {:full_index => true}, "#{parents[0].id}...#{parents[1].id}")
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