schacon-grit 0.9.4 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
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 CHANGED
@@ -104,14 +104,23 @@ module Grit
104
104
  info = nil
105
105
  end
106
106
  end
107
-
107
+
108
108
  blames
109
109
  end
110
110
 
111
+ def basename
112
+ File.basename(name)
113
+ end
114
+
111
115
  # Pretty object inspection
112
116
  def inspect
113
117
  %Q{#<Grit::Blob "#{@id}">}
114
118
  end
119
+
120
+ # Compares blobs by name
121
+ def <=>(other)
122
+ name <=> other.name
123
+ end
115
124
  end # Blob
116
-
125
+
117
126
  end # Grit
data/lib/grit/commit.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Grit
2
-
2
+
3
3
  class Commit
4
4
  attr_reader :id
5
5
  lazy_reader :parents
@@ -10,7 +10,8 @@ module Grit
10
10
  lazy_reader :committed_date
11
11
  lazy_reader :message
12
12
  lazy_reader :short_message
13
-
13
+ lazy_reader :author_string
14
+
14
15
  # Instantiate a new Commit
15
16
  # +id+ is the id of the commit
16
17
  # +parents+ is an array of commit ids (will be converted into Commit instances)
@@ -34,11 +35,11 @@ module Grit
34
35
  @message = message.join("\n")
35
36
  @short_message = message[0] || ''
36
37
  end
37
-
38
+
38
39
  def id_abbrev
39
40
  @id_abbrev ||= @repo.git.rev_parse({}, self.id).chomp[0, 7]
40
41
  end
41
-
42
+
42
43
  # Create an unbaked Commit containing just the specified attributes
43
44
  # +repo+ is the Repo
44
45
  # +atts+ is a Hash of instance variable data
@@ -47,7 +48,7 @@ module Grit
47
48
  def self.create(repo, atts)
48
49
  self.allocate.create_initialize(repo, atts)
49
50
  end
50
-
51
+
51
52
  # Initializer for Commit.create
52
53
  # +repo+ is the Repo
53
54
  # +atts+ is a Hash of instance variable data
@@ -60,11 +61,11 @@ module Grit
60
61
  end
61
62
  self
62
63
  end
63
-
64
+
64
65
  def lazy_source
65
66
  self.class.find_all(@repo, @id, {:max_count => 1}).first
66
67
  end
67
-
68
+
68
69
  # Count the number of commits reachable from this ref
69
70
  # +repo+ is the Repo
70
71
  # +ref+ is the ref from which to begin (SHA1 or name)
@@ -73,7 +74,7 @@ module Grit
73
74
  def self.count(repo, ref)
74
75
  repo.git.rev_list({}, ref).size / 41
75
76
  end
76
-
77
+
77
78
  # Find all commits matching the given criteria.
78
79
  # +repo+ is the Repo
79
80
  # +ref+ is the ref from which to begin (SHA1 or name) or nil for --all
@@ -84,21 +85,21 @@ module Grit
84
85
  # Returns Grit::Commit[] (baked)
85
86
  def self.find_all(repo, ref, options = {})
86
87
  allowed_options = [:max_count, :skip, :since]
87
-
88
+
88
89
  default_options = {:pretty => "raw"}
89
90
  actual_options = default_options.merge(options)
90
-
91
+
91
92
  if ref
92
93
  output = repo.git.rev_list(actual_options, ref)
93
94
  else
94
95
  output = repo.git.rev_list(actual_options.merge(:all => true))
95
96
  end
96
-
97
+
97
98
  self.list_from_string(repo, output)
98
99
  rescue Grit::GitRuby::Repository::NoSuchShaFound
99
100
  []
100
101
  end
101
-
102
+
102
103
  # Parse out commit information into an array of baked Commit objects
103
104
  # +repo+ is the Repo
104
105
  # +text+ is the text output from the git command (raw format)
@@ -110,40 +111,40 @@ module Grit
110
111
  #
111
112
  def self.list_from_string(repo, text)
112
113
  lines = text.split("\n")
113
-
114
+
114
115
  commits = []
115
-
116
+
116
117
  while !lines.empty?
117
118
  id = lines.shift.split.last
118
119
  tree = lines.shift.split.last
119
-
120
+
120
121
  parents = []
121
122
  parents << lines.shift.split.last while lines.first =~ /^parent/
122
-
123
+
123
124
  author, authored_date = self.actor(lines.shift)
124
125
  committer, committed_date = self.actor(lines.shift)
125
-
126
+
126
127
  # not doing anything with this yet, but it's sometimes there
127
128
  encoding = lines.shift.split.last if lines.first =~ /^encoding/
128
-
129
+
129
130
  lines.shift
130
-
131
+
131
132
  message_lines = []
132
133
  message_lines << lines.shift[4..-1] while lines.first =~ /^ {4}/
133
-
134
+
134
135
  lines.shift while lines.first && lines.first.empty?
135
-
136
+
136
137
  commits << Commit.new(repo, id, parents, tree, author, authored_date, committer, committed_date, message_lines)
137
138
  end
138
-
139
+
139
140
  commits
140
141
  end
141
-
142
+
142
143
  # Show diffs between two trees:
143
144
  # +repo+ is the Repo
144
145
  # +a+ is a named commit
145
- # +b+ is an optional named commit. Passing an array assumes you
146
- # wish to omit the second named commit and limit the diff to the
146
+ # +b+ is an optional named commit. Passing an array assumes you
147
+ # wish to omit the second named commit and limit the diff to the
147
148
  # given paths.
148
149
  # +paths* is an array of paths to limit the diff.
149
150
  #
@@ -174,10 +175,14 @@ module Grit
174
175
  if parents.empty?
175
176
  show
176
177
  else
177
- self.class.diff(@repo, parents.first.id, @id)
178
+ self.class.diff(@repo, parents.first.id, @id)
178
179
  end
179
180
  end
180
181
 
182
+ def stats
183
+ stats = @repo.commit_stats(self.sha, 1)[0][-1]
184
+ end
185
+
181
186
  # Convert this Commit to a String which is just the SHA1 id
182
187
  def to_s
183
188
  @id
@@ -186,18 +191,22 @@ module Grit
186
191
  def sha
187
192
  @id
188
193
  end
189
-
194
+
190
195
  def date
191
196
  @committed_date
192
197
  end
193
-
198
+
199
+ def to_patch
200
+ @repo.git.format_patch({'1' => true, :stdout => true}, to_s)
201
+ end
202
+
194
203
  # Pretty object inspection
195
204
  def inspect
196
205
  %Q{#<Grit::Commit "#{@id}">}
197
206
  end
198
-
207
+
199
208
  # private
200
-
209
+
201
210
  # Parse out the actor (author or committer) info
202
211
  #
203
212
  # Returns [String (actor name and email), Time (acted at time)]
@@ -206,6 +215,10 @@ module Grit
206
215
  [Actor.from_string(actor), Time.at(epoch.to_i)]
207
216
  end
208
217
 
218
+ def author_string
219
+ "%s <%s> %s %+05d" % [author.name, author.email, authored_date.to_i, 800]
220
+ end
221
+
209
222
  def to_hash
210
223
  {
211
224
  'id' => id,
@@ -225,5 +238,5 @@ module Grit
225
238
  }
226
239
  end
227
240
  end # Commit
228
-
241
+
229
242
  end # Grit
@@ -62,12 +62,12 @@ module Grit
62
62
  lines.shift
63
63
 
64
64
  message_lines = []
65
- message_lines << lines.shift[4..-1] while lines.first =~ /^ {4}/
65
+ message_lines << lines.shift[4..-1] while lines.first =~ /^ {4}/ || lines.first == ''
66
66
 
67
67
  lines.shift while lines.first && lines.first.empty?
68
68
 
69
69
  files = []
70
- while lines.first =~ /^(\d+)\s+(\d+)\s+(.+)/
70
+ while lines.first =~ /^([-\d]+)\s+([-\d]+)\s+(.+)/
71
71
  (additions, deletions, filename) = lines.shift.split
72
72
  additions = additions.to_i
73
73
  deletions = deletions.to_i
@@ -88,6 +88,13 @@ module Grit
88
88
  %Q{#<Grit::CommitStats "#{@id}">}
89
89
  end
90
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
+
91
98
  # private
92
99
 
93
100
  def to_hash
@@ -99,6 +106,23 @@ module Grit
99
106
  'total' => total
100
107
  }
101
108
  end
109
+
102
110
  end # CommitStats
103
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
+
104
128
  end # Grit
data/lib/grit/diff.rb CHANGED
@@ -2,21 +2,21 @@ module Grit
2
2
 
3
3
  class Diff
4
4
  attr_reader :a_path, :b_path
5
- attr_reader :a_commit, :b_commit
5
+ attr_reader :a_blob, :b_blob
6
6
  attr_reader :a_mode, :b_mode
7
7
  attr_reader :new_file, :deleted_file
8
8
  attr_reader :diff
9
9
 
10
- def initialize(repo, a_path, b_path, a_commit, b_commit, a_mode, b_mode, new_file, deleted_file, diff)
10
+ def initialize(repo, a_path, b_path, a_blob, b_blob, a_mode, b_mode, new_file, deleted_file, diff)
11
11
  @repo = repo
12
12
  @a_path = a_path
13
13
  @b_path = b_path
14
- @a_commit = a_commit =~ /^0{40}$/ ? nil : Commit.create(repo, :id => a_commit)
15
- @b_commit = b_commit =~ /^0{40}$/ ? nil : Commit.create(repo, :id => b_commit)
14
+ @a_blob = a_blob =~ /^0{40}$/ ? nil : Blob.create(repo, :id => a_blob)
15
+ @b_blob = b_blob =~ /^0{40}$/ ? nil : Blob.create(repo, :id => b_blob)
16
16
  @a_mode = a_mode
17
17
  @b_mode = b_mode
18
- @new_file = new_file
19
- @deleted_file = deleted_file
18
+ @new_file = new_file || @a_blob.nil?
19
+ @deleted_file = deleted_file || @b_blob.nil?
20
20
  @diff = diff
21
21
  end
22
22
 
@@ -51,7 +51,7 @@ module Grit
51
51
  deleted_file = true
52
52
  end
53
53
 
54
- m, a_commit, b_commit, b_mode = *lines.shift.match(%r{^index ([0-9A-Fa-f]+)\.\.([0-9A-Fa-f]+) ?(.+)?$})
54
+ m, a_blob, b_blob, b_mode = *lines.shift.match(%r{^index ([0-9A-Fa-f]+)\.\.([0-9A-Fa-f]+) ?(.+)?$})
55
55
  b_mode.strip! if b_mode
56
56
 
57
57
  diff_lines = []
@@ -60,7 +60,7 @@ module Grit
60
60
  end
61
61
  diff = diff_lines.join("\n")
62
62
 
63
- diffs << Diff.new(repo, a_path, b_path, a_commit, b_commit, a_mode, b_mode, new_file, deleted_file, diff)
63
+ diffs << Diff.new(repo, a_path, b_path, a_blob, b_blob, a_mode, b_mode, new_file, deleted_file, diff)
64
64
  end
65
65
 
66
66
  diffs
data/lib/grit/git-ruby.rb CHANGED
@@ -39,9 +39,9 @@ module Grit
39
39
  end
40
40
 
41
41
  # git diff --full-index 'ec037431382e83c3e95d4f2b3d145afbac8ea55d' 'f1ec1aea10986159456846b8a05615b87828d6c6'
42
- def diff(options, sha1, sha2)
43
- try_run { ruby_git.diff(sha1, sha2, options) }
44
- end
42
+ #def diff(options, sha1, sha2)
43
+ # try_run { ruby_git.diff(sha1, sha2, options) }
44
+ #end
45
45
 
46
46
  def rev_list(options, ref = 'master')
47
47
  options.delete(:skip) if options[:skip].to_i == 0
@@ -72,8 +72,8 @@ module Grit
72
72
  (sha1, sha2) = string.split('..')
73
73
  return [rev_parse({}, sha1), rev_parse({}, sha2)]
74
74
  end
75
-
76
- if /\w{40}/.match(string) # passing in a sha - just no-op it
75
+
76
+ if /^[0-9a-f]{40}$/.match(string) # passing in a sha - just no-op it
77
77
  return string.chomp
78
78
  end
79
79
 
@@ -103,6 +103,87 @@ module Grit
103
103
  return method_missing('rev-parse', {}, string).chomp
104
104
  end
105
105
 
106
+ def refs(options, prefix)
107
+ refs = []
108
+ already = {}
109
+ Dir.chdir(@git_dir) do
110
+ files = Dir.glob(prefix + '/**/*')
111
+ files.each do |ref|
112
+ next if !File.file?(ref)
113
+ id = File.read(ref).chomp
114
+ name = ref.sub("#{prefix}/", '')
115
+ if !already[name]
116
+ refs << "#{name} #{id}"
117
+ already[name] = true
118
+ end
119
+ end
120
+
121
+ if File.file?('packed-refs')
122
+ File.readlines('packed-refs').each do |line|
123
+ if m = /^(\w{40}) (.*?)$/.match(line)
124
+ next if !Regexp.new('^' + prefix).match(m[2])
125
+ name = m[2].sub("#{prefix}/", '')
126
+ if !already[name]
127
+ refs << "#{name} #{m[1]}"
128
+ already[name] = true
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ refs.join("\n")
136
+ end
137
+
138
+ def tags(options, prefix)
139
+ refs = []
140
+ already = {}
141
+
142
+ Dir.chdir(repo.path) do
143
+ files = Dir.glob(prefix + '/**/*')
144
+
145
+ files.each do |ref|
146
+ next if !File.file?(ref)
147
+
148
+ id = File.read(ref).chomp
149
+ name = ref.sub("#{prefix}/", '')
150
+
151
+ if !already[name]
152
+ refs << "#{name} #{id}"
153
+ already[name] = true
154
+ end
155
+ end
156
+
157
+ if File.file?('packed-refs')
158
+ lines = File.readlines('packed-refs')
159
+ lines.each_with_index do |line, i|
160
+ if m = /^(\w{40}) (.*?)$/.match(line)
161
+ next if !Regexp.new('^' + prefix).match(m[2])
162
+ name = m[2].sub("#{prefix}/", '')
163
+
164
+ # Annotated tags in packed-refs include a reference
165
+ # to the commit object on the following line.
166
+ next_line = lines[i + 1]
167
+
168
+ id =
169
+ if next_line && next_line[0] == ?^
170
+ next_line[1..-1].chomp
171
+ else
172
+ m[1]
173
+ end
174
+
175
+ if !already[name]
176
+ refs << "#{name} #{id}"
177
+ already[name] = true
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end
183
+
184
+ refs.join("\n")
185
+ end
186
+
106
187
  def file_size(ref)
107
188
  try_run { ruby_git.cat_file_size(ref).to_s }
108
189
  end
@@ -113,7 +194,7 @@ module Grit
113
194
 
114
195
  def blame_tree(commit, path = nil)
115
196
  begin
116
- path = path.to_a.join('/').to_s + '/' if (path && path != '')
197
+ path = [path].join('/').to_s + '/' if (path && path != '')
117
198
  path = '' if !path.is_a? String
118
199
  commits = file_index.last_commits(rev_parse({}, commit), looking_for(commit, path))
119
200
  clean_paths(commits)