git_fame 1.6.0 → 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rspec +2 -2
- data/README.md +4 -1
- data/bin/git-fame +0 -1
- data/git_fame.gemspec +3 -5
- data/lib/git_fame/author.rb +28 -6
- data/lib/git_fame/base.rb +196 -164
- data/lib/git_fame/file.rb +13 -0
- data/lib/git_fame/result.rb +6 -0
- data/lib/git_fame/version.rb +1 -1
- data/spec/git_fame_spec.rb +13 -32
- data/spec/spec_helper.rb +10 -5
- data/spec/support/startup.rb +7 -0
- metadata +38 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f884e24c83b7a966af59ab96a045ea3ece06b6ff
|
4
|
+
data.tar.gz: fa4e2967c967867b568ace60dc7bd62bf1a191d4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a3f5a220b5e3a6211c3d8aad13d881bcb500e1e0bff4ad8b16bf984e0038c3acd1f4d1673ee6577a495d7be8387fe5c24d33b6f6f612010cee8d0e5084781d79
|
7
|
+
data.tar.gz: 53101b66a4a44b3eabf739c43bbabf33664e5ccbcee1225566e2dcf41aeb47217eac3225b945f2870de0549c7e89854c8fca6ddf29c31c82d4fb1846b441dab3
|
data/.gitignore
CHANGED
data/.rspec
CHANGED
data/README.md
CHANGED
@@ -131,7 +131,10 @@ The list of authors may include duplicate people. If a git user's configured nam
|
|
131
131
|
## Testing
|
132
132
|
|
133
133
|
1. Download fixtures (`spec/fixtures`) using `git submodule update --init`.
|
134
|
-
2. Run rspec using `rspec
|
134
|
+
2. Run rspec using `bundle exec rspec`.
|
135
|
+
|
136
|
+
Note that `puts` has been disabled to avoid unnecessary output during testing.
|
137
|
+
Visit `spec/spec_helper.rb` to enable it again.
|
135
138
|
|
136
139
|
## Requirements
|
137
140
|
|
data/bin/git-fame
CHANGED
@@ -18,7 +18,6 @@ opts = Trollop::options do
|
|
18
18
|
opt :format, "Format (pretty/csv)", default: "pretty", type: String
|
19
19
|
end
|
20
20
|
|
21
|
-
Trollop::die :repository, "is not a git repository" unless GitFame::Base.git_repository?(opts[:repository])
|
22
21
|
Trollop::die :sort, "must be one of the following; #{sort.join(", ")}" unless sort.include?(opts[:sort])
|
23
22
|
fame = GitFame::Base.new({
|
24
23
|
repository: opts[:repository],
|
data/git_fame.gemspec
CHANGED
@@ -28,12 +28,10 @@ Generates data like:
|
|
28
28
|
gem.add_dependency("trollop")
|
29
29
|
gem.add_dependency("hirb")
|
30
30
|
gem.add_dependency("mimer_plus")
|
31
|
+
gem.add_dependency("scrub_rb")
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
gem.add_development_dependency("rspec", "2.10.0")
|
33
|
+
gem.add_development_dependency("rspec", "~> 3.0")
|
34
|
+
gem.add_development_dependency("rspec-collection_matchers")
|
37
35
|
gem.add_development_dependency("rake")
|
38
36
|
gem.add_development_dependency("coveralls")
|
39
37
|
|
data/lib/git_fame/author.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
module GitFame
|
2
2
|
class Author
|
3
3
|
include GitFame::Helper
|
4
|
-
attr_accessor :name, :raw_files, :raw_commits,
|
4
|
+
attr_accessor :name, :raw_files, :raw_commits,
|
5
5
|
:raw_loc, :files_list, :file_type_counts
|
6
6
|
|
7
|
+
FIELDS = [:loc, :commits, :files]
|
8
|
+
|
7
9
|
#
|
8
10
|
# @args Hash
|
9
11
|
#
|
@@ -12,7 +14,7 @@ module GitFame
|
|
12
14
|
@raw_commits = 0
|
13
15
|
@raw_files = 0
|
14
16
|
@file_type_counts = Hash.new(0)
|
15
|
-
args.keys.each do |name|
|
17
|
+
args.keys.each do |name|
|
16
18
|
instance_variable_set "@" + name.to_s, args[name]
|
17
19
|
end
|
18
20
|
end
|
@@ -22,13 +24,15 @@ module GitFame
|
|
22
24
|
# @return String Distribution (in %) between users
|
23
25
|
#
|
24
26
|
def distribution
|
25
|
-
"
|
26
|
-
|
27
|
+
"%s / %s / %s" % FIELDS.map do |field|
|
28
|
+
("%.1f" % (percent_for_field(field) * 100)).rjust(4, " ")
|
29
|
+
end
|
27
30
|
end
|
31
|
+
alias_method :"distribution (%)", :distribution
|
28
32
|
|
29
|
-
|
33
|
+
FIELDS.each do |method|
|
30
34
|
define_method(method) do
|
31
|
-
number_with_delimiter(
|
35
|
+
number_with_delimiter(raw(method))
|
32
36
|
end
|
33
37
|
end
|
34
38
|
|
@@ -38,5 +42,23 @@ module GitFame
|
|
38
42
|
def method_missing(m, *args, &block)
|
39
43
|
file_type_counts[m.to_s]
|
40
44
|
end
|
45
|
+
|
46
|
+
def raw(method)
|
47
|
+
unless FIELDS.include?(method.to_sym)
|
48
|
+
raise "can't access raw '#{method}' on author"
|
49
|
+
end
|
50
|
+
|
51
|
+
send("raw_#{method}")
|
52
|
+
end
|
53
|
+
|
54
|
+
def inc(method, amount)
|
55
|
+
send("raw_#{method}=", raw(method) + amount)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def percent_for_field(field)
|
61
|
+
raw(field) / @parent.send(field).to_f
|
62
|
+
end
|
41
63
|
end
|
42
64
|
end
|
data/lib/git_fame/base.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
require "csv"
|
2
2
|
require_relative "./errors"
|
3
|
+
require_relative "./result"
|
4
|
+
require_relative "./file"
|
5
|
+
require "open3"
|
6
|
+
|
3
7
|
if RUBY_VERSION.to_f < 2.1
|
4
8
|
require "scrub_rb"
|
5
9
|
end
|
@@ -7,6 +11,7 @@ end
|
|
7
11
|
module GitFame
|
8
12
|
class Base
|
9
13
|
include GitFame::Helper
|
14
|
+
attr_accessor :file_extensions
|
10
15
|
|
11
16
|
#
|
12
17
|
# @args[:repository] String Absolute path to git repository
|
@@ -17,25 +22,31 @@ module GitFame
|
|
17
22
|
# @args[:branch] String Branch to run from
|
18
23
|
#
|
19
24
|
def initialize(args)
|
20
|
-
@
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
@
|
25
|
-
@exclude = ""
|
26
|
-
@include = ""
|
27
|
-
@authors = {}
|
25
|
+
@default_settings = {
|
26
|
+
branch: "master",
|
27
|
+
sorting: "loc"
|
28
|
+
}
|
29
|
+
@progressbar = args.fetch(:progressbar, false)
|
28
30
|
@file_authors = Hash.new { |h,k| h[k] = {} }
|
29
|
-
|
30
|
-
|
31
|
+
# Create array out of comma separated list
|
32
|
+
@exclude = args.fetch(:exclude, "").split(",").
|
33
|
+
map{ |path| path.strip.sub(/\A\//, "") }
|
34
|
+
@extensions = args.fetch(:extensions, "").split(",")
|
35
|
+
# Default sorting option is by loc
|
36
|
+
@include = args.fetch(:include, "")
|
37
|
+
@sort = args.fetch(:sort, @default_settings.fetch(:sorting))
|
38
|
+
@repository = args.fetch(:repository)
|
39
|
+
@bytype = args.fetch(:bytype, false)
|
40
|
+
@branch = args.fetch(:branch, default_branch)
|
41
|
+
|
42
|
+
# User defined branch must exist
|
43
|
+
if not blank?(@branch) and not default_branch_exists?
|
44
|
+
raise GitFame::BranchNotFound, "Branch '#{@branch}' does not exist"
|
31
45
|
end
|
32
|
-
@exclude = convert_exclude_paths_to_array
|
33
|
-
@extensions = convert_extensions_to_array
|
34
|
-
@branch = (@branch.nil? or @branch.empty?) ? "master" : @branch
|
35
46
|
|
36
47
|
# Fields that should be visible in the final table
|
37
48
|
# Used by #csv_puts, #to_csv and #pretty_puts
|
38
|
-
# Format: [ [:method_on_author, "custom column name"] ]
|
49
|
+
# Format: [ [ :method_on_author, "custom column name" ] ]
|
39
50
|
@visible_fields = [
|
40
51
|
:name,
|
41
52
|
:loc,
|
@@ -43,17 +54,10 @@ module GitFame
|
|
43
54
|
:files,
|
44
55
|
[:distribution, "distribution (%)"]
|
45
56
|
]
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
# @dir Path (relative or absolute) to git repository
|
51
|
-
#
|
52
|
-
def self.git_repository?(dir)
|
53
|
-
return false unless File.directory?(dir)
|
54
|
-
Dir.chdir(dir) do
|
55
|
-
system "git rev-parse --git-dir > /dev/null 2>&1"
|
56
|
-
end
|
57
|
+
@cache = {}
|
58
|
+
@file_extensions = []
|
59
|
+
@wopt = args.fetch(:whitespace, false) ? "-w" : ""
|
60
|
+
@authors = {}
|
57
61
|
end
|
58
62
|
|
59
63
|
#
|
@@ -66,7 +70,7 @@ module GitFame
|
|
66
70
|
puts "Total number of lines: #{number_with_delimiter(loc)}"
|
67
71
|
puts "Total number of commits: #{number_with_delimiter(commits)}\n"
|
68
72
|
|
69
|
-
table(authors, fields:
|
73
|
+
table(authors, fields: printable_fields)
|
70
74
|
end
|
71
75
|
|
72
76
|
#
|
@@ -81,7 +85,7 @@ module GitFame
|
|
81
85
|
#
|
82
86
|
def to_csv
|
83
87
|
CSV.generate do |csv|
|
84
|
-
csv <<
|
88
|
+
csv << fields
|
85
89
|
authors.each do |author|
|
86
90
|
csv << fields.map do |f|
|
87
91
|
author.send(f)
|
@@ -92,95 +96,195 @@ module GitFame
|
|
92
96
|
|
93
97
|
#
|
94
98
|
# @return Fixnum Total number of files
|
99
|
+
# TODO: Rename this
|
95
100
|
#
|
96
101
|
def files
|
97
|
-
|
102
|
+
file_list.count
|
98
103
|
end
|
99
104
|
|
100
105
|
#
|
101
106
|
# @return Array list of repo files processed
|
102
107
|
#
|
103
108
|
def file_list
|
104
|
-
populate
|
109
|
+
populate { current_files }
|
105
110
|
end
|
106
111
|
|
107
112
|
#
|
108
113
|
# @return Fixnum Total number of commits
|
109
114
|
#
|
110
115
|
def commits
|
111
|
-
authors.inject(0){ |result, author| author.
|
116
|
+
authors.inject(0) { |result, author| author.raw(:commits) + result }
|
112
117
|
end
|
113
118
|
|
114
119
|
#
|
115
120
|
# @return Fixnum Total number of lines
|
116
121
|
#
|
117
122
|
def loc
|
118
|
-
|
119
|
-
inject(0){ |result, author| author.raw_loc + result }
|
123
|
+
authors.inject(0) { |result, author| author.raw(:loc) + result }
|
120
124
|
end
|
121
125
|
|
122
126
|
#
|
123
127
|
# @return Array<Author> A list of authors
|
124
128
|
#
|
125
129
|
def authors
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
if @sort == "name"
|
131
|
-
author.send(@sort)
|
132
|
-
else
|
133
|
-
-1 * author.send("raw_#{@sort}")
|
134
|
-
end
|
130
|
+
cache(:authors) do
|
131
|
+
populate do
|
132
|
+
@authors.values.sort_by do |author|
|
133
|
+
@sort == "name" ? author.send(@sort) : -1 * author.raw(@sort)
|
135
134
|
end
|
136
|
-
else
|
137
|
-
authors
|
138
135
|
end
|
139
136
|
end
|
140
137
|
end
|
141
138
|
|
142
|
-
|
143
|
-
|
144
|
-
#
|
145
|
-
|
146
|
-
|
147
|
-
|
139
|
+
private
|
140
|
+
|
141
|
+
# Populates @authors and @file_extensions with data
|
142
|
+
# Block is called on every call to populate, but
|
143
|
+
# the data is only calculated once
|
144
|
+
def populate(&block)
|
145
|
+
cache(:populate) do
|
146
|
+
# Display progressbar with the number of files as countdown
|
147
|
+
progressbar = init_progressbar(current_files.count)
|
148
|
+
|
149
|
+
# Extract the blame history from all checked in files
|
150
|
+
current_files.each do |file|
|
151
|
+
progressbar.inc
|
152
|
+
|
153
|
+
# Skip if mimetype can't be decided
|
154
|
+
next unless type = Mimer.identify(File.join(@repository, file.path))
|
155
|
+
# Binary types isn't very usefull to run git-blame on
|
156
|
+
next if type.binary?
|
157
|
+
|
158
|
+
@file_extensions << file.extname
|
159
|
+
|
160
|
+
execute("git blame #{@wopt} --line-porcelain #{@branch} -- '#{file}'") do |result|
|
161
|
+
# Authors from git blame has to be parsed
|
162
|
+
result.to_s.scan(/^author (.+)$/).each do |raw_author, _|
|
163
|
+
# Create or find already existing user
|
164
|
+
author = fetch(raw_author)
|
165
|
+
|
166
|
+
# Get author by name and increase the number of loc by 1
|
167
|
+
author.inc(:loc, 1)
|
168
|
+
|
169
|
+
# Store the files and authors together
|
170
|
+
@file_authors[raw_author][file] ||= 1
|
171
|
+
|
172
|
+
@bytype && author.file_type_counts[file.extname] += 1
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Get repository summery and update each author accordingly
|
178
|
+
execute("git shortlog #{@branch} -se") do |result|
|
179
|
+
result.to_s.split("\n").map do |line|
|
180
|
+
_, commits, raw_author = line.match(%r{^\s*(\d+)\s+(.+?)\s+<.+?>}).to_a
|
181
|
+
author = fetch(raw_author)
|
182
|
+
# There might be duplicate authors using git shortlog
|
183
|
+
# (same name, different emails). Update already existing authors
|
184
|
+
if author.raw(:commits).zero?
|
185
|
+
update(raw_author, {
|
186
|
+
raw_commits: commits.to_i,
|
187
|
+
raw_files: @file_authors[raw_author].keys.count,
|
188
|
+
files_list: @file_authors[raw_author].keys
|
189
|
+
})
|
190
|
+
else
|
191
|
+
# Calculate the number of files edited by users
|
192
|
+
files = (author.files_list + @file_authors[raw_author].keys).uniq
|
193
|
+
update(raw_author, {
|
194
|
+
raw_commits: commits.to_i + author.raw(:commits),
|
195
|
+
raw_files: files.count,
|
196
|
+
files_list: files
|
197
|
+
})
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
progressbar.finish
|
148
203
|
end
|
149
|
-
end
|
150
204
|
|
151
|
-
|
205
|
+
block.call
|
206
|
+
end
|
152
207
|
|
208
|
+
# Uses the more printable names in @visible_fields
|
153
209
|
def printable_fields
|
154
|
-
|
155
|
-
|
210
|
+
cache(:printable_fields) do
|
211
|
+
raw_fields.map do |field|
|
212
|
+
field.is_a?(Array) ? field.last : field
|
213
|
+
end
|
156
214
|
end
|
157
215
|
end
|
158
216
|
|
217
|
+
# Check to see if a string is empty (nil or "")
|
218
|
+
def blank?(value)
|
219
|
+
value.nil? or value.empty?
|
220
|
+
end
|
221
|
+
|
222
|
+
# Includes fields from file extensions
|
159
223
|
def raw_fields
|
160
224
|
return @visible_fields unless @bytype
|
161
|
-
|
162
|
-
|
163
|
-
|
225
|
+
cache(:raw_fields) do
|
226
|
+
populate do
|
227
|
+
(@visible_fields + file_extensions).uniq
|
228
|
+
end
|
229
|
+
end
|
164
230
|
end
|
165
231
|
|
232
|
+
# Method fields used by #to_csv and #pretty_puts
|
166
233
|
def fields
|
167
|
-
|
168
|
-
|
234
|
+
cache(:fields) do
|
235
|
+
raw_fields.map do |field|
|
236
|
+
field.is_a?(Array) ? field.first : field
|
237
|
+
end
|
169
238
|
end
|
170
239
|
end
|
171
240
|
|
172
|
-
#
|
173
|
-
# @
|
174
|
-
|
175
|
-
|
176
|
-
|
241
|
+
# Command to be executed at @repository
|
242
|
+
# @silent = true wont raise an error on exit code =! 0
|
243
|
+
def execute(command, silent = false, &block)
|
244
|
+
result = Open3.popen2e(command, chdir: @repository) do |_, out, thread|
|
245
|
+
Result.new(out.read, thread.value.success?)
|
246
|
+
end
|
247
|
+
|
248
|
+
return block.call(result) if result.success? or silent
|
249
|
+
raise cmd_error_message(command, result.data)
|
250
|
+
rescue Errno::ENOENT
|
251
|
+
raise cmd_error_message(command, $!.message)
|
177
252
|
end
|
178
253
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
#
|
254
|
+
def cmd_error_message(command, message)
|
255
|
+
"Could not run '#{command}' => #{message}"
|
256
|
+
end
|
257
|
+
|
258
|
+
# Boolean Does the branch exist?
|
259
|
+
def default_branch_exists?
|
260
|
+
branch_exists?(@branch)
|
261
|
+
end
|
262
|
+
|
263
|
+
# Does @branch exist in the current git repo?
|
264
|
+
def branch_exists?(branch)
|
265
|
+
execute("git show-ref '#{branch}'", true) do |result|
|
266
|
+
result.success?
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
# In those cases the users havent defined a branch
|
271
|
+
# We try to define it for him/her by
|
272
|
+
# 1. check if { @default_settings.fetch(:branch) } exists
|
273
|
+
# 1. look at .git/HEAD (basically)
|
274
|
+
def default_branch
|
275
|
+
if branch_exists?(@default_settings.fetch(:branch))
|
276
|
+
return @default_settings.fetch(:branch)
|
277
|
+
end
|
278
|
+
|
279
|
+
execute("git rev-parse HEAD") do |result|
|
280
|
+
return result.data if result.success?
|
281
|
+
end
|
282
|
+
|
283
|
+
raise BranchNotFound.new("No branch found")
|
284
|
+
end
|
285
|
+
|
286
|
+
# Tries to create an author, unless it already exists in cache
|
287
|
+
# User is always updated with the passed @args
|
184
288
|
def update(author, args)
|
185
289
|
fetch(author).tap do |found|
|
186
290
|
args.keys.each do |key|
|
@@ -189,115 +293,43 @@ module GitFame
|
|
189
293
|
end
|
190
294
|
end
|
191
295
|
|
192
|
-
#
|
193
|
-
# @return Author
|
194
|
-
# @author String
|
195
|
-
#
|
296
|
+
# Fetches user from cache
|
196
297
|
def fetch(author)
|
197
|
-
@authors[author] ||= Author.new({name: author, parent: self})
|
298
|
+
@authors[author] ||= Author.new({ name: author, parent: self })
|
198
299
|
end
|
199
300
|
|
200
|
-
#
|
201
|
-
# @
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
end
|
208
|
-
|
209
|
-
command = "git ls-tree -r #{@branch} --name-only #{@include}"
|
210
|
-
command += " | grep \"\\.\\(#{@extensions.join("\\|")}\\)$\"" unless @extensions.empty?
|
211
|
-
@files = execute(command).split("\n")
|
212
|
-
@file_extensions = []
|
213
|
-
remove_excluded_files
|
214
|
-
progressbar = SilentProgressbar.new(
|
215
|
-
"Blame",
|
216
|
-
@files.count,
|
217
|
-
@progressbar
|
218
|
-
)
|
219
|
-
blame_opts = @whitespace ? "-w" : ""
|
220
|
-
@files.each do |file|
|
221
|
-
progressbar.inc
|
222
|
-
if @bytype
|
223
|
-
file_extension = File.extname(file).gsub(/^\./, "")
|
224
|
-
file_extension = "unknown" if file_extension.empty?
|
225
|
-
end
|
226
|
-
|
227
|
-
unless type = Mimer.identify(File.join(@repository, file))
|
228
|
-
next
|
229
|
-
end
|
230
|
-
|
231
|
-
if type.binary?
|
232
|
-
next
|
301
|
+
# List all files in current git directory, excluding
|
302
|
+
# extensions in @extensions defined by the user
|
303
|
+
def current_files
|
304
|
+
cache(:current_files) do
|
305
|
+
execute("git ls-tree -r #{@branch} --name-only #{@include}") do |result|
|
306
|
+
files = remove_excluded_files(result.to_s.split("\n")).map do |path|
|
307
|
+
GitFame::FileUnit.new(path)
|
233
308
|
end
|
234
309
|
|
235
|
-
|
236
|
-
@
|
237
|
-
|
238
|
-
output = execute(
|
239
|
-
"git blame #{blame_opts} --line-porcelain #{@branch} -- '#{file}'"
|
240
|
-
)
|
241
|
-
output.scan(/^author (.+)$/).each do |author|
|
242
|
-
fetch(author.first).raw_loc += 1
|
243
|
-
@file_authors[author.first][file] ||= 1
|
244
|
-
if @bytype
|
245
|
-
fetch(author.first).
|
246
|
-
file_type_counts[file_extension] += 1
|
247
|
-
end
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
execute("git shortlog #{@branch} -se").split("\n").map do |l|
|
252
|
-
_, commits, u = l.match(%r{^\s*(\d+)\s+(.+?)\s+<.+?>}).to_a
|
253
|
-
user = fetch(u)
|
254
|
-
# Has this user been updated before?
|
255
|
-
if user.raw_commits.zero?
|
256
|
-
update(u, {
|
257
|
-
raw_commits: commits.to_i,
|
258
|
-
raw_files: @file_authors[u].keys.count,
|
259
|
-
files_list: @file_authors[u].keys
|
260
|
-
})
|
261
|
-
else
|
262
|
-
# Calculate the number of files edited by users
|
263
|
-
files = (user.files_list + @file_authors[u].keys).uniq
|
264
|
-
update(u, {
|
265
|
-
raw_commits: commits.to_i + user.raw_commits,
|
266
|
-
raw_files: files.count,
|
267
|
-
files_list: files
|
268
|
-
})
|
269
|
-
end
|
310
|
+
return files if @extensions.empty?
|
311
|
+
files.select { |file| @extensions.include?(file.extname) }
|
270
312
|
end
|
271
|
-
|
272
|
-
progressbar.finish
|
273
|
-
|
274
313
|
end
|
275
|
-
return self
|
276
314
|
end
|
277
315
|
|
278
|
-
#
|
279
|
-
#
|
280
|
-
|
281
|
-
|
282
|
-
@exclude.split(",").map{|path| path.strip.sub(/\A\//, "") }
|
316
|
+
# The block is only called once for every unique key
|
317
|
+
# Used to ensure methods are only called once
|
318
|
+
def cache(key, &block)
|
319
|
+
@cache[key] ||= block.call
|
283
320
|
end
|
284
321
|
|
285
|
-
#
|
286
|
-
#
|
287
|
-
|
288
|
-
|
289
|
-
|
322
|
+
# Removes files excluded by the user
|
323
|
+
# Defined using --exclude
|
324
|
+
def remove_excluded_files(files)
|
325
|
+
return files if @exclude.empty?
|
326
|
+
files.reject do |file|
|
327
|
+
@exclude.any? { |exclude| file.match(exclude) }
|
328
|
+
end
|
290
329
|
end
|
291
330
|
|
292
|
-
|
293
|
-
|
294
|
-
#
|
295
|
-
def remove_excluded_files
|
296
|
-
return if @exclude.empty?
|
297
|
-
@files = @files.map do |path|
|
298
|
-
next if path =~ /\A(#{@exclude.join("|")})/
|
299
|
-
path
|
300
|
-
end.compact
|
331
|
+
def init_progressbar(files_count)
|
332
|
+
SilentProgressbar.new("GitBlame", files_count, @progressbar)
|
301
333
|
end
|
302
334
|
end
|
303
335
|
end
|
data/lib/git_fame/version.rb
CHANGED
data/spec/git_fame_spec.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
describe GitFame::Base do
|
2
|
-
let(:subject) { GitFame::Base.new({repository:
|
2
|
+
let(:subject) { GitFame::Base.new({repository: repository}) }
|
3
3
|
describe "#authors" do
|
4
4
|
it "should have a list of authors" do
|
5
5
|
subject.should have(3).authors
|
6
6
|
end
|
7
7
|
|
8
8
|
describe "author" do
|
9
|
+
require "pp"
|
9
10
|
let(:author) { subject.authors.last }
|
10
11
|
it "should have a bunch of commits" do
|
11
12
|
author.raw_commits.should eq(23)
|
@@ -61,7 +62,7 @@ describe GitFame::Base do
|
|
61
62
|
describe "sort" do
|
62
63
|
it "should be able to sort #authors by name" do
|
63
64
|
authors = GitFame::Base.new({
|
64
|
-
repository:
|
65
|
+
repository: repository,
|
65
66
|
sort: "name"
|
66
67
|
}).authors
|
67
68
|
authors.map(&:name).
|
@@ -70,7 +71,7 @@ describe GitFame::Base do
|
|
70
71
|
|
71
72
|
it "should be able to sort #authors by commits" do
|
72
73
|
authors = GitFame::Base.new({
|
73
|
-
repository:
|
74
|
+
repository: repository,
|
74
75
|
sort: "commits"
|
75
76
|
}).authors
|
76
77
|
authors.map(&:name).
|
@@ -79,7 +80,7 @@ describe GitFame::Base do
|
|
79
80
|
|
80
81
|
it "should be able to sort #authors by files" do
|
81
82
|
authors = GitFame::Base.new({
|
82
|
-
repository:
|
83
|
+
repository: repository,
|
83
84
|
sort: "files"
|
84
85
|
}).authors
|
85
86
|
authors.map(&:name).
|
@@ -90,7 +91,7 @@ describe GitFame::Base do
|
|
90
91
|
describe "#command_line_arguments" do
|
91
92
|
let(:subject) do
|
92
93
|
GitFame::Base.new({
|
93
|
-
repository:
|
94
|
+
repository: repository,
|
94
95
|
exclude: "lib",
|
95
96
|
bytype: true,
|
96
97
|
extensions: "rb,rdoc"
|
@@ -98,11 +99,11 @@ describe GitFame::Base do
|
|
98
99
|
end
|
99
100
|
|
100
101
|
it "should exclude the lib folder" do
|
101
|
-
subject.file_list.include?("lib/gash.rb").should
|
102
|
+
subject.file_list.include?("lib/gash.rb").should be_falsey
|
102
103
|
end
|
103
104
|
|
104
105
|
it "should exclude non rb or rdoc files" do
|
105
|
-
subject.file_list.include?("HISTORY").should
|
106
|
+
subject.file_list.include?("HISTORY").should be_falsey
|
106
107
|
end
|
107
108
|
|
108
109
|
let(:author) { subject.authors.find { |author| author.name == "7rans" } }
|
@@ -131,37 +132,17 @@ describe GitFame::Base do
|
|
131
132
|
end
|
132
133
|
|
133
134
|
it "should be equal to" do
|
134
|
-
subject.to_csv.should eq("name,loc,commits,files,distribution
|
135
|
+
subject.to_csv.should eq("name,loc,commits,files,distribution\n" \
|
135
136
|
"Magnus Holm,586,41,4,54.2 / 58.6 / 25.0\n" \
|
136
|
-
"7rans,360,6,10,33.3 /
|
137
|
+
"7rans,360,6,10,33.3 / 8.6 / 62.5\n" \
|
137
138
|
"Linus Oleander,136,23,7,12.6 / 32.9 / 43.8\n")
|
138
139
|
end
|
139
140
|
end
|
140
141
|
|
141
|
-
describe ".git_repository?" do
|
142
|
-
it "should know if a folder is a git repository [absolute path]" do
|
143
|
-
GitFame::Base.git_repository?(@repository).should eq(true)
|
144
|
-
end
|
145
|
-
|
146
|
-
it "should know if a folder exists or not [absolute path]" do
|
147
|
-
GitFame::Base.git_repository?("/f67c2bcbfcfa30fccb36f72dca22a817").
|
148
|
-
should eq(false)
|
149
|
-
end
|
150
|
-
|
151
|
-
it "should know if a folder is a git repository [relative path]" do
|
152
|
-
GitFame::Base.git_repository?("spec/fixtures/gash").should eq(true)
|
153
|
-
end
|
154
|
-
|
155
|
-
it "should know if a folder exists or not [relative path]" do
|
156
|
-
GitFame::Base.git_repository?("f67c2bcbfcfa30fccb36f72dca22a817").
|
157
|
-
should eq(false)
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
142
|
describe "branches" do
|
162
143
|
it "should handle existing branches" do
|
163
144
|
authors = GitFame::Base.new({
|
164
|
-
repository:
|
145
|
+
repository: repository,
|
165
146
|
branch: "0.1.0"
|
166
147
|
}).authors
|
167
148
|
|
@@ -172,8 +153,8 @@ describe GitFame::Base do
|
|
172
153
|
it "should raise an error if branch doesn't exist" do
|
173
154
|
expect {
|
174
155
|
GitFame::Base.new({
|
175
|
-
repository:
|
176
|
-
branch: "
|
156
|
+
repository: repository,
|
157
|
+
branch: "-----"
|
177
158
|
}).authors
|
178
159
|
}.to raise_error(GitFame::BranchNotFound)
|
179
160
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,16 +1,21 @@
|
|
1
1
|
require "rspec"
|
2
2
|
require "git_fame"
|
3
3
|
require "coveralls"
|
4
|
+
require "rspec/collection_matchers"
|
5
|
+
require_relative "./support/startup"
|
4
6
|
|
5
7
|
Coveralls.wear!
|
6
8
|
|
7
9
|
RSpec.configure do |config|
|
10
|
+
config.include GitFame::Startup
|
8
11
|
config.mock_with :rspec
|
9
12
|
config.order = "random"
|
10
|
-
config.
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
end
|
13
|
+
config.expect_with(:rspec) { |c| c.syntax = [:should, :expect] }
|
14
|
+
config.fail_fast = false
|
15
|
+
config.before(:all) do
|
16
|
+
Dir.chdir(repository) { `git checkout 7ab01bc5a720` }
|
15
17
|
end
|
18
|
+
|
19
|
+
# Remove this line to allow Kernel#puts
|
20
|
+
config.before { allow($stdout).to receive(:puts) }
|
16
21
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: git_fame
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.7.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Linus Oleander
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-05-
|
11
|
+
date: 2016-05-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: progressbar
|
@@ -66,20 +66,48 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: scrub_rb
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: rspec
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
72
86
|
requirements:
|
73
|
-
- -
|
87
|
+
- - "~>"
|
74
88
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
89
|
+
version: '3.0'
|
76
90
|
type: :development
|
77
91
|
prerelease: false
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
80
|
-
- -
|
94
|
+
- - "~>"
|
81
95
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
96
|
+
version: '3.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec-collection_matchers
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
83
111
|
- !ruby/object:Gem::Dependency
|
84
112
|
name: rake
|
85
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -130,11 +158,14 @@ files:
|
|
130
158
|
- lib/git_fame/author.rb
|
131
159
|
- lib/git_fame/base.rb
|
132
160
|
- lib/git_fame/errors.rb
|
161
|
+
- lib/git_fame/file.rb
|
133
162
|
- lib/git_fame/helper.rb
|
163
|
+
- lib/git_fame/result.rb
|
134
164
|
- lib/git_fame/silent_progressbar.rb
|
135
165
|
- lib/git_fame/version.rb
|
136
166
|
- spec/git_fame_spec.rb
|
137
167
|
- spec/spec_helper.rb
|
168
|
+
- spec/support/startup.rb
|
138
169
|
homepage: https://github.com/oleander/git-fame-rb
|
139
170
|
licenses: []
|
140
171
|
metadata: {}
|
@@ -163,3 +194,4 @@ summary: 'Generates awesome stats from git-blame A Ruby wrapper for git-blame.
|
|
163
194
|
test_files:
|
164
195
|
- spec/git_fame_spec.rb
|
165
196
|
- spec/spec_helper.rb
|
197
|
+
- spec/support/startup.rb
|