git_statistics 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +1 -0
- data/Guardfile +2 -2
- data/lib/git_statistics.rb +3 -3
- data/lib/git_statistics/collector.rb +38 -94
- data/lib/git_statistics/commit_line_extractor.rb +64 -0
- data/lib/git_statistics/commits.rb +51 -59
- data/lib/git_statistics/results.rb +5 -7
- data/lib/git_statistics/utilities.rb +27 -39
- data/lib/git_statistics/version.rb +1 -1
- data/spec/collector_spec.rb +11 -76
- data/spec/commit_line_extractor_spec.rb +70 -0
- data/spec/commits_spec.rb +2 -2
- data/spec/spec_helper.rb +1 -1
- data/spec/utilities_spec.rb +32 -38
- metadata +5 -2
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
* [0.5.0 - January 16, 2013](https://github.com/kevinjalbert/git_statistics/compare/v0.4.1...v0.5.0)
|
3
4
|
* [0.4.1 - October 9, 2012](https://github.com/kevinjalbert/git_statistics/compare/v0.4.0...v0.4.1)
|
4
5
|
* [0.4.0 - September 25, 2012](https://github.com/kevinjalbert/git_statistics/compare/v0.3.0...v0.4.0)
|
5
6
|
* [0.3.0 - September 12, 2012](https://github.com/kevinjalbert/git_statistics/compare/v0.2.0...v0.3.0)
|
data/Guardfile
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# A sample Guardfile
|
2
2
|
# More info at https://github.com/guard/guard#readme
|
3
|
-
guard :rspec, :version => 2, :all_on_start => false do
|
3
|
+
guard :rspec, :version => 2, :notifications => false, :all_on_start => false do
|
4
4
|
watch(%r{^spec/.+_spec\.rb$})
|
5
|
-
watch(%r{^lib/(.+)\.rb$}) { |m| "spec
|
5
|
+
watch(%r{^lib/git_statistics/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
6
6
|
watch('spec/spec_helper.rb') { "spec" }
|
7
7
|
end
|
data/lib/git_statistics.rb
CHANGED
@@ -2,7 +2,7 @@ require 'git_statistics/initialize'
|
|
2
2
|
|
3
3
|
module GitStatistics
|
4
4
|
class GitStatistics
|
5
|
-
def initialize(args=nil)
|
5
|
+
def initialize(args = nil)
|
6
6
|
@opts = Trollop::options do
|
7
7
|
opt :email, "Use author's email instead of name", :default => false
|
8
8
|
opt :merges, "Factor in merges when calculating statistics", :default => false
|
@@ -23,7 +23,7 @@ module GitStatistics
|
|
23
23
|
collector = Collector.new(@opts[:verbose], @opts[:limit], false, @opts[:pretty])
|
24
24
|
commits_directory = collector.repo_path + ".git_statistics" + File::Separator
|
25
25
|
FileUtils.mkdir_p(commits_directory)
|
26
|
-
file_count = Utilities.
|
26
|
+
file_count = Utilities.number_of_matching_files(commits_directory, /\d+\.json/) - 1
|
27
27
|
|
28
28
|
# Only use --since if there is data present
|
29
29
|
if file_count >= 0
|
@@ -34,7 +34,7 @@ module GitStatistics
|
|
34
34
|
end
|
35
35
|
|
36
36
|
# If no data was collected as there was no present data then start fresh
|
37
|
-
|
37
|
+
unless collected
|
38
38
|
collector = Collector.new(@opts[:verbose], @opts[:limit], true, @opts[:pretty])
|
39
39
|
collector.collect(@opts[:branch])
|
40
40
|
end
|
@@ -7,29 +7,25 @@ module GitStatistics
|
|
7
7
|
@verbose = verbose
|
8
8
|
@repo = Utilities.get_repository
|
9
9
|
|
10
|
-
|
11
|
-
if @repo == nil
|
12
|
-
raise ("No git Repository Found")
|
13
|
-
end
|
10
|
+
raise "No Git repository found" if @repo.nil?
|
14
11
|
|
15
12
|
@repo_path = File.expand_path("..", @repo.path) + File::Separator
|
16
13
|
@commits_path = @repo_path + ".git_statistics" + File::Separator
|
17
14
|
@commits = Commits.new(@commits_path, fresh, limit, pretty)
|
18
15
|
end
|
19
16
|
|
20
|
-
def collect(branch, time_since="", time_until="")
|
17
|
+
def collect(branch, time_since = "", time_until = "")
|
21
18
|
# Create pipe for git log to acquire branches
|
22
19
|
pipe = open("|git --no-pager branch --no-color")
|
23
20
|
|
24
21
|
# Collect branches to use for git log
|
25
|
-
branches = collect_branches(pipe)
|
26
|
-
branches = ["", ""] if branch
|
22
|
+
branches = branch ? ["", ""] : collect_branches(pipe)
|
27
23
|
|
28
24
|
# Create pipe for the git log to acquire commits
|
29
25
|
pipe = open("|git --no-pager log #{branches.join(' ')} --date=iso --reverse"\
|
30
|
-
" --no-color --find-copies-harder --numstat --encoding=utf-8
|
31
|
-
"--summary #{time_since} #{time_until}
|
32
|
-
"--format=\"%H,%an,%ae,%ad,%p\"")
|
26
|
+
" --no-color --find-copies-harder --numstat --encoding=utf-8"\
|
27
|
+
" --summary #{time_since} #{time_until}"\
|
28
|
+
" --format=\"%H,%an,%ae,%ad,%p\"")
|
33
29
|
|
34
30
|
# Use a buffer approach to queue up lines from the log for each commit
|
35
31
|
buffer = []
|
@@ -41,9 +37,9 @@ module GitStatistics
|
|
41
37
|
if line.split(',').size == 5
|
42
38
|
|
43
39
|
# Sometimes 'git log' doesn't populate the buffer (i.e., merges), try fallback option if so
|
44
|
-
buffer = fall_back_collect_commit(buffer[0].split(',').first) if buffer.
|
40
|
+
buffer = fall_back_collect_commit(buffer[0].split(',').first) if buffer.one?
|
45
41
|
|
46
|
-
extract_commit(buffer)
|
42
|
+
extract_commit(buffer) unless buffer.empty?
|
47
43
|
buffer = []
|
48
44
|
|
49
45
|
# Save commits to file if size exceeds limit or forced
|
@@ -55,7 +51,7 @@ module GitStatistics
|
|
55
51
|
end
|
56
52
|
|
57
53
|
# Extract the last commit
|
58
|
-
extract_commit(buffer)
|
54
|
+
extract_commit(buffer) unless buffer.empty?
|
59
55
|
@commits.flush_commits(true)
|
60
56
|
end
|
61
57
|
|
@@ -65,18 +61,13 @@ module GitStatistics
|
|
65
61
|
" --no-color --find-copies-harder --numstat --encoding=utf-8 "\
|
66
62
|
"--summary --format=\"%H,%an,%ae,%ad,%p\"")
|
67
63
|
|
68
|
-
buffer =
|
69
|
-
pipe.each do |line|
|
70
|
-
buffer << Utilities.clean_string(line)
|
71
|
-
end
|
64
|
+
buffer = pipe.map { |line| Utilities.clean_string(line) }
|
72
65
|
|
73
66
|
# Check that the buffer has valid information (i.e., sha was valid)
|
74
|
-
if buffer.empty?
|
75
|
-
|
76
|
-
elsif buffer.first.split(',').first == sha
|
77
|
-
return buffer
|
67
|
+
if !buffer.empty? && buffer.first.split(',').first == sha
|
68
|
+
buffer
|
78
69
|
else
|
79
|
-
|
70
|
+
nil
|
80
71
|
end
|
81
72
|
end
|
82
73
|
|
@@ -105,7 +96,7 @@ module GitStatistics
|
|
105
96
|
data[:files] = []
|
106
97
|
|
107
98
|
# Flag commit as merge if necessary (determined if two parents)
|
108
|
-
if commit_info[4]
|
99
|
+
if commit_info[4].nil? || commit_info[4].split(' ').one?
|
109
100
|
data[:merge] = false
|
110
101
|
else
|
111
102
|
data[:merge] = true
|
@@ -121,7 +112,7 @@ module GitStatistics
|
|
121
112
|
puts "Extracting #{commit_data[:sha]}" if @verbose
|
122
113
|
|
123
114
|
# Abort if the commit sha extracted form the buffer is invalid
|
124
|
-
if commit_data[:sha].scan(/[\d|a-f]{40}/)[0]
|
115
|
+
if commit_data[:sha].scan(/[\d|a-f]{40}/)[0].nil?
|
125
116
|
puts "Invalid buffer containing commit information"
|
126
117
|
return
|
127
118
|
end
|
@@ -130,7 +121,7 @@ module GitStatistics
|
|
130
121
|
files = identify_changed_files(buffer[2..-1])
|
131
122
|
|
132
123
|
# No files were changed in this commit, abort commit
|
133
|
-
if files
|
124
|
+
if files.nil?
|
134
125
|
puts "No files were changed"
|
135
126
|
return
|
136
127
|
end
|
@@ -159,9 +150,9 @@ module GitStatistics
|
|
159
150
|
blob = Utilities.find_blob_in_tree(@repo.tree(sha), file)
|
160
151
|
|
161
152
|
# If we cannot find blob in current commit (deleted file), check previous commit
|
162
|
-
if blob
|
153
|
+
if blob.nil? || blob.instance_of?(Grit::Tree)
|
163
154
|
prev_commit = @repo.commits(sha).first.parents[0]
|
164
|
-
return nil if prev_commit
|
155
|
+
return nil if prev_commit.nil?
|
165
156
|
|
166
157
|
prev_tree = @repo.tree(prev_commit.id)
|
167
158
|
blob = Utilities.find_blob_in_tree(prev_tree, file)
|
@@ -170,54 +161,56 @@ module GitStatistics
|
|
170
161
|
end
|
171
162
|
|
172
163
|
def identify_changed_files(buffer)
|
173
|
-
return buffer if buffer
|
164
|
+
return buffer if buffer.nil?
|
174
165
|
|
175
166
|
# For each modification extract the details
|
176
167
|
changed_files = []
|
177
168
|
buffer.each do |line|
|
169
|
+
extracted_line = CommitLineExtractor.new(line)
|
178
170
|
|
179
171
|
# Extract changed file information if it exists
|
180
|
-
|
181
|
-
if
|
182
|
-
changed_files <<
|
172
|
+
changed_file_information = extracted_line.changed
|
173
|
+
if changed_file_information.any?
|
174
|
+
changed_files << changed_file_information
|
183
175
|
next # This line is processed, skip to next
|
184
176
|
end
|
185
177
|
|
186
178
|
# Extract details of create/delete files if it exists
|
187
|
-
|
188
|
-
if
|
179
|
+
created_or_deleted = extracted_line.created_or_deleted
|
180
|
+
if created_or_deleted.any?
|
189
181
|
augmented = false
|
190
182
|
# Augment changed file with create/delete information if possible
|
191
183
|
changed_files.each do |file|
|
192
|
-
if file[:file] ==
|
193
|
-
file[:status] =
|
184
|
+
if file[:file] == created_or_deleted[:file]
|
185
|
+
file[:status] = created_or_deleted[:status]
|
194
186
|
augmented = true
|
195
187
|
break
|
196
188
|
end
|
197
189
|
end
|
198
|
-
changed_files <<
|
190
|
+
changed_files << created_or_deleted unless augmented
|
199
191
|
next # This line is processed, skip to next
|
200
192
|
end
|
201
193
|
|
202
194
|
# Extract details of rename/copy files if it exists
|
203
|
-
|
204
|
-
if
|
195
|
+
renamed_or_copied = extracted_line.renamed_or_copied
|
196
|
+
if renamed_or_copied.any?
|
205
197
|
augmented = false
|
206
198
|
# Augment changed file with rename/copy information if possible
|
207
199
|
changed_files.each do |file|
|
208
|
-
if file[:file] ==
|
209
|
-
file[:status] =
|
210
|
-
file[:old_file] =
|
211
|
-
file[:similar] =
|
200
|
+
if file[:file] == renamed_or_copied[:new_file]
|
201
|
+
file[:status] = renamed_or_copied[:status]
|
202
|
+
file[:old_file] = renamed_or_copied[:old_file]
|
203
|
+
file[:similar] = renamed_or_copied[:similar]
|
212
204
|
augmented = true
|
213
205
|
break
|
214
206
|
end
|
215
207
|
end
|
216
|
-
changed_files <<
|
208
|
+
changed_files << renamed_or_copied unless augmented
|
217
209
|
next # This line is processed, skip to next
|
218
210
|
end
|
219
211
|
end
|
220
|
-
|
212
|
+
|
213
|
+
changed_files
|
221
214
|
end
|
222
215
|
|
223
216
|
def process_blob(data, blob, file)
|
@@ -240,60 +233,11 @@ module GitStatistics
|
|
240
233
|
file_hash[:generated] = blob.generated?
|
241
234
|
|
242
235
|
# Identify the language of the blob if possible
|
243
|
-
|
244
|
-
file_hash[:language] = "Unknown"
|
245
|
-
else
|
246
|
-
file_hash[:language] = blob.language.name
|
247
|
-
end
|
236
|
+
file_hash[:language] = blob.language.nil? ? "Unknown" : blob.language.name
|
248
237
|
data[:files] << file_hash
|
249
238
|
|
250
239
|
return data
|
251
240
|
end
|
252
241
|
|
253
|
-
def extract_change_file(line)
|
254
|
-
# Use regex to detect a rename/copy changed file | 1 2 /path/{test => new}/file.txt
|
255
|
-
changes = line.scan(/^([-|\d]+)\s+([-|\d]+)\s+(.+)\s+=>\s+(.+)/)[0]
|
256
|
-
if changes != nil and changes.size == 4
|
257
|
-
# Split up the file into the old and new file
|
258
|
-
split_file = Utilities.split_old_new_file(changes[2], changes[3])
|
259
|
-
return {:additions => changes[0].to_i,
|
260
|
-
:deletions => changes[1].to_i,
|
261
|
-
:file => Utilities.clean_string(split_file[:new_file]),
|
262
|
-
:old_file => Utilities.clean_string(split_file[:old_file])}
|
263
|
-
end
|
264
|
-
|
265
|
-
# Use regex to detect a changed file | 1 2 /path/test/file.txt
|
266
|
-
changes = line.scan(/^([-|\d]+)\s+([-|\d]+)\s+(.+)/)[0]
|
267
|
-
if changes != nil and changes.size == 3
|
268
|
-
return {:additions => changes[0].to_i,
|
269
|
-
:deletions => changes[1].to_i,
|
270
|
-
:file => Utilities.clean_string(changes[2])}
|
271
|
-
end
|
272
|
-
return nil
|
273
|
-
end
|
274
|
-
|
275
|
-
def extract_create_delete_file(line)
|
276
|
-
# Use regex to detect a create/delete file | create mode 100644 /path/test/file.txt
|
277
|
-
changes = line.scan(/^(create|delete) mode \d+ ([^\\\n]*)/)[0]
|
278
|
-
if changes != nil and changes.size == 2
|
279
|
-
return {:status => Utilities.clean_string(changes[0]),
|
280
|
-
:file => Utilities.clean_string(changes[1])}
|
281
|
-
end
|
282
|
-
return nil
|
283
|
-
end
|
284
|
-
|
285
|
-
def extract_rename_copy_file(line)
|
286
|
-
# Use regex to detect a rename/copy file | copy /path/{test => new}/file.txt
|
287
|
-
changes = line.scan(/^(rename|copy)\s+(.+)\s+=>\s+(.+)\s+\((\d+)/)[0]
|
288
|
-
if changes != nil and changes.size == 4
|
289
|
-
# Split up the file into the old and new file
|
290
|
-
split_file = Utilities.split_old_new_file(changes[1], changes[2])
|
291
|
-
return {:status => Utilities.clean_string(changes[0]),
|
292
|
-
:old_file => Utilities.clean_string(split_file[:old_file]),
|
293
|
-
:new_file => Utilities.clean_string(split_file[:new_file]),
|
294
|
-
:similar => changes[3].to_i}
|
295
|
-
end
|
296
|
-
return nil
|
297
|
-
end
|
298
242
|
end
|
299
243
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module GitStatistics
|
2
|
+
class CommitLineExtractor
|
3
|
+
|
4
|
+
RENAMED_OR_COPIED = /^(rename|copy)\s+(.+)\s+=>\s+(.+)\s+\((\d+)/i
|
5
|
+
CREATED_OR_DELETED = /^(create|delete) mode \d+ ([^\\\n]*)/i
|
6
|
+
MODIFIED_OR_RENAMED = /^([-|\d]+)\s+([-|\d]+)\s+(.+)\s+=>\s+(.+)/i
|
7
|
+
ADDITIONS_OR_DELETIONS = /^([-|\d]+)\s+([-|\d]+)\s+(.+)/i
|
8
|
+
|
9
|
+
attr_reader :line
|
10
|
+
|
11
|
+
def initialize(line)
|
12
|
+
@line = line
|
13
|
+
end
|
14
|
+
|
15
|
+
def changed
|
16
|
+
modified_or_renamed = line.scan(MODIFIED_OR_RENAMED).first
|
17
|
+
modified_or_renamed = changes_are_right_size(modified_or_renamed, 4) do |changes|
|
18
|
+
split_file = Utilities.split_old_new_file(changes[2], changes[3])
|
19
|
+
{:additions => changes[0].to_i,
|
20
|
+
:deletions => changes[1].to_i,
|
21
|
+
:file => Utilities.clean_string(split_file[:new_file]),
|
22
|
+
:old_file => Utilities.clean_string(split_file[:old_file])}
|
23
|
+
end
|
24
|
+
return modified_or_renamed unless modified_or_renamed.empty?
|
25
|
+
|
26
|
+
addition_or_deletion = line.scan(ADDITIONS_OR_DELETIONS).first
|
27
|
+
changes_are_right_size(addition_or_deletion, 3) do |changes|
|
28
|
+
{:additions => changes[0].to_i,
|
29
|
+
:deletions => changes[1].to_i,
|
30
|
+
:file => Utilities.clean_string(changes[2])}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def created_or_deleted
|
35
|
+
changes = line.scan(CREATED_OR_DELETED).first
|
36
|
+
changes_are_right_size(changes, 2) do |changes|
|
37
|
+
{:status => Utilities.clean_string(changes[0]),
|
38
|
+
:file => Utilities.clean_string(changes[1])}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def renamed_or_copied
|
43
|
+
changes = line.scan(RENAMED_OR_COPIED).first
|
44
|
+
changes_are_right_size(changes, 4) do |changes|
|
45
|
+
split_file = Utilities.split_old_new_file(changes[1], changes[2])
|
46
|
+
{:status => Utilities.clean_string(changes[0]),
|
47
|
+
:old_file => Utilities.clean_string(split_file[:old_file]),
|
48
|
+
:new_file => Utilities.clean_string(split_file[:new_file]),
|
49
|
+
:similar => changes[3].to_i}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def changes_are_right_size(changes, size = 4)
|
56
|
+
if !changes.nil? && changes.size == size
|
57
|
+
yield changes
|
58
|
+
else
|
59
|
+
{}
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -19,8 +19,8 @@ module GitStatistics
|
|
19
19
|
# Remove all files within path if saving
|
20
20
|
if @fresh
|
21
21
|
Dir.entries(@path).each do |file|
|
22
|
-
next if
|
23
|
-
File.delete(path
|
22
|
+
next if %w[. ..].include? file
|
23
|
+
File.delete(File.join(path, file))
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -30,85 +30,79 @@ module GitStatistics
|
|
30
30
|
@totals[:languages] = {}
|
31
31
|
end
|
32
32
|
|
33
|
-
def flush_commits(force=false)
|
34
|
-
if
|
35
|
-
file_count = Utilities.
|
36
|
-
save(
|
37
|
-
|
33
|
+
def flush_commits(force = false)
|
34
|
+
if size >= limit || force
|
35
|
+
file_count = Utilities.number_of_matching_files(path, /\d+\.json/)
|
36
|
+
save(File.join(path, "#{file_count}.json"), @pretty)
|
37
|
+
clear
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
def author_top_n_type(type, top_n=0)
|
41
|
+
def author_top_n_type(type, top_n = 0)
|
42
42
|
top_n = 0 if top_n < 0
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
if @stats.empty? || !@stats.first[1].has_key?(type)
|
44
|
+
nil
|
45
|
+
else
|
46
|
+
Hash[*@stats.sorted_hash {|a,b| b[1][type.to_sym] <=> a[1][type]}.to_a[0..top_n-1].flatten]
|
47
|
+
end
|
46
48
|
end
|
47
49
|
|
48
50
|
def calculate_statistics(email, merge)
|
49
51
|
# Identify authors and author type
|
50
|
-
|
51
|
-
type = :author_email
|
52
|
-
else
|
53
|
-
type = :author
|
54
|
-
end
|
52
|
+
type = email ? :author_email : :author
|
55
53
|
|
56
54
|
# For all the commit files created
|
57
55
|
Dir.entries(path).each do |file|
|
58
56
|
# Load commit file and extract the commits
|
59
57
|
if file =~ /\d+\.json/
|
60
|
-
load(path
|
58
|
+
load(File.join(path, file))
|
61
59
|
process_commits(type, merge)
|
62
|
-
|
60
|
+
clear
|
63
61
|
end
|
64
62
|
end
|
65
63
|
end
|
66
64
|
|
67
65
|
def process_commits(type, merge)
|
68
66
|
# Collect the stats from each commit
|
69
|
-
|
70
|
-
if !merge && value[:merge]
|
71
|
-
|
72
|
-
|
67
|
+
each do |key,value|
|
68
|
+
next if !merge && value[:merge]
|
69
|
+
# If there are no changed files move to next commit
|
70
|
+
next if value[:files].empty?
|
73
71
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
if author == nil
|
78
|
-
@stats[value[type]] = Hash.new(0)
|
79
|
-
author = @stats[value[type]]
|
80
|
-
author[:languages] = {}
|
81
|
-
end
|
72
|
+
# Acquire author (make if not seen before)
|
73
|
+
author = @stats[value[type]]
|
82
74
|
|
83
|
-
|
84
|
-
|
75
|
+
if author.nil?
|
76
|
+
@stats[value[type]] = Hash.new(0)
|
77
|
+
author = @stats[value[type]]
|
78
|
+
author[:languages] = {}
|
79
|
+
end
|
85
80
|
|
86
|
-
|
87
|
-
|
81
|
+
# Collect language stats
|
82
|
+
value[:files].each do |file|
|
88
83
|
|
89
|
-
|
90
|
-
|
84
|
+
# Add to author's languages
|
85
|
+
add_language_stats(author, file)
|
91
86
|
|
92
|
-
|
93
|
-
|
94
|
-
|
87
|
+
# Add to repository's languages
|
88
|
+
add_language_stats(@totals, file)
|
89
|
+
end
|
95
90
|
|
96
|
-
|
97
|
-
|
91
|
+
# Add commit stats to author
|
92
|
+
add_commit_stats(author, value)
|
98
93
|
|
99
|
-
|
100
|
-
|
94
|
+
# Add commit stats to repository
|
95
|
+
add_commit_stats(@totals, value)
|
101
96
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
end
|
97
|
+
# Save new changes back to stats
|
98
|
+
@stats[value[type]] = author
|
99
|
+
author = nil
|
106
100
|
end
|
107
101
|
end
|
108
102
|
|
109
103
|
def add_language_stats(data, file)
|
110
104
|
# Add stats to data's languages
|
111
|
-
if data[:languages][file[:language].to_sym]
|
105
|
+
if data[:languages][file[:language].to_sym].nil?
|
112
106
|
data[:languages][file[:language].to_sym] = Hash.new(0)
|
113
107
|
end
|
114
108
|
|
@@ -136,21 +130,19 @@ module GitStatistics
|
|
136
130
|
end
|
137
131
|
|
138
132
|
def load(file)
|
139
|
-
|
133
|
+
merge!(JSON.parse(File.read(file), :symbolize_names => true))
|
140
134
|
end
|
141
135
|
|
142
136
|
def save(file, pretty)
|
143
137
|
# Don't save if there is no information (i.e., using updates)
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
else
|
153
|
-
File.open(file, 'w') {|file| file.write(self.to_json)}
|
138
|
+
unless empty?
|
139
|
+
# Ensure the path to the file exists
|
140
|
+
FileUtils.mkdir_p(File.dirname(file))
|
141
|
+
# Save file in a simple or pretty format
|
142
|
+
File.open(file, 'w') do |file|
|
143
|
+
json_content = pretty ? JSON.pretty_generate(self) : self.to_json
|
144
|
+
file.write(json_content)
|
145
|
+
end
|
154
146
|
end
|
155
147
|
end
|
156
148
|
end
|
@@ -7,20 +7,18 @@ module GitStatistics
|
|
7
7
|
@commits = commits
|
8
8
|
end
|
9
9
|
|
10
|
-
def prepare_result_summary(sort, email, top_n=0)
|
10
|
+
def prepare_result_summary(sort, email, top_n = 0)
|
11
11
|
# Default to a 0 if given a negative number to display
|
12
12
|
top_n = 0 if top_n < 0
|
13
13
|
|
14
14
|
# Acquire data based on sort type and top # to show
|
15
15
|
data = @commits.author_top_n_type(sort.to_sym, top_n)
|
16
|
-
if data
|
17
|
-
raise "Parameter for --sort is not valid"
|
18
|
-
end
|
16
|
+
raise "Parameter for --sort is not valid" if data.nil?
|
19
17
|
|
20
18
|
# Create config
|
21
19
|
config = {:data => data,
|
22
|
-
:author_length => Utilities.
|
23
|
-
:language_length => Utilities.
|
20
|
+
:author_length => Utilities.max_length_in_list(data.keys, 17),
|
21
|
+
:language_length => Utilities.max_length_in_list(@commits.totals[:languages].keys, 8),
|
24
22
|
:sort => sort,
|
25
23
|
:email => email,
|
26
24
|
:top_n => top_n}
|
@@ -31,7 +29,7 @@ module GitStatistics
|
|
31
29
|
return config
|
32
30
|
end
|
33
31
|
|
34
|
-
def print_summary(sort, email, top_n=0)
|
32
|
+
def print_summary(sort, email, top_n = 0)
|
35
33
|
# Prepare and determine the config for the result summary based on parameters
|
36
34
|
config = prepare_result_summary(sort, email, top_n)
|
37
35
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module GitStatistics
|
2
2
|
module Utilities
|
3
|
-
def self.get_repository(path=Dir.pwd)
|
3
|
+
def self.get_repository(path = Dir.pwd)
|
4
4
|
# Connect to git repository if it exists
|
5
5
|
directory = Pathname.new(path)
|
6
6
|
repo = nil
|
@@ -14,26 +14,16 @@ module GitStatistics
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
def self.
|
18
|
-
return
|
17
|
+
def self.max_length_in_list(list, max = nil)
|
18
|
+
return nil if list.nil?
|
19
19
|
list.each do |key,value|
|
20
|
-
max = key.length if max
|
20
|
+
max = key.length if max.nil? || key.length > max
|
21
21
|
end
|
22
|
-
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.unique_data_in_hash(data, type)
|
26
|
-
list = []
|
27
|
-
data.each do |key,value|
|
28
|
-
if not list.include?(value[type])
|
29
|
-
list << value[type]
|
30
|
-
end
|
31
|
-
end
|
32
|
-
return list
|
22
|
+
max
|
33
23
|
end
|
34
24
|
|
35
25
|
def self.clean_string(string)
|
36
|
-
|
26
|
+
string.strip.force_encoding("iso-8859-1").encode("utf-8")
|
37
27
|
end
|
38
28
|
|
39
29
|
def self.split_old_new_file(old, new)
|
@@ -42,13 +32,13 @@ module GitStatistics
|
|
42
32
|
split_new = new.split('}')
|
43
33
|
|
44
34
|
# Handle recombine the file splits into their whole paths)
|
45
|
-
if split_old.
|
35
|
+
if split_old.one? && split_new.one?
|
46
36
|
old_file = split_old[0]
|
47
37
|
new_file = split_new[0]
|
48
|
-
elsif split_new.
|
38
|
+
elsif split_new.one?
|
49
39
|
old_file = split_old[0] + split_old[1]
|
50
40
|
new_file = split_old[0] + split_new[0]
|
51
|
-
elsif split_old.
|
41
|
+
elsif split_old.one?
|
52
42
|
old_file = split_old[0] + split_new[1]
|
53
43
|
new_file = split_old[0] + split_new[0] + split_new[1]
|
54
44
|
else
|
@@ -63,20 +53,18 @@ module GitStatistics
|
|
63
53
|
|
64
54
|
def self.find_blob_in_tree(tree, file)
|
65
55
|
# Check If cannot find tree in commit or if we found a submodule as the changed file
|
66
|
-
if tree
|
67
|
-
return nil
|
68
|
-
elsif file == nil
|
56
|
+
if tree.nil? || file.nil?
|
69
57
|
return nil
|
70
58
|
elsif tree.instance_of?(Grit::Submodule)
|
71
59
|
return tree
|
72
60
|
end
|
73
61
|
|
74
62
|
# If the blob is within the current directory (tree)
|
75
|
-
if file.
|
63
|
+
if file.one?
|
76
64
|
blob = tree / file.first
|
77
65
|
|
78
66
|
# Check if blob is nil (could not find changed file in tree)
|
79
|
-
if blob
|
67
|
+
if blob.nil?
|
80
68
|
|
81
69
|
# Try looking for submodules as they cannot be found using tree / file notation
|
82
70
|
tree.contents.each do |content|
|
@@ -96,24 +84,24 @@ module GitStatistics
|
|
96
84
|
end
|
97
85
|
|
98
86
|
def self.get_modified_time(file)
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
raise "Update on the Windows operating system is not supported"
|
105
|
-
end
|
87
|
+
command = case
|
88
|
+
when OS.mac? then "stat -f %m #{file}"
|
89
|
+
when OS.linux? then "stat -c %Y #{file}"
|
90
|
+
else raise "Update on the Windows operating system is not supported"; end
|
91
|
+
time_at(command)
|
106
92
|
end
|
107
93
|
|
108
|
-
def self.
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
files.each do |file|
|
113
|
-
count += 1 if file =~ pattern
|
114
|
-
end
|
94
|
+
def self.time_at(cmd)
|
95
|
+
Time.at(%x{#{cmd}}.to_i)
|
96
|
+
end
|
115
97
|
|
116
|
-
|
98
|
+
def self.number_of_matching_files(directory, pattern)
|
99
|
+
Dir.entries(directory)
|
100
|
+
.select { |file| file =~ pattern }
|
101
|
+
.size
|
102
|
+
rescue SystemCallError
|
103
|
+
::Kernel.warn "No such directory #{File.expand_path(directory)}"
|
104
|
+
0
|
117
105
|
end
|
118
106
|
end
|
119
107
|
end
|
data/spec/collector_spec.rb
CHANGED
@@ -10,11 +10,9 @@ describe Collector do
|
|
10
10
|
|
11
11
|
# Create buffer which is an array of cleaned lines
|
12
12
|
let(:buffer) {
|
13
|
-
|
14
|
-
|
15
|
-
buffer << Utilities.clean_string(line)
|
13
|
+
fixture(fixture_file).readlines.collect do |line|
|
14
|
+
Utilities.clean_string(line)
|
16
15
|
end
|
17
|
-
buffer
|
18
16
|
}
|
19
17
|
|
20
18
|
describe "#collect" do
|
@@ -102,69 +100,6 @@ describe Collector do
|
|
102
100
|
end
|
103
101
|
end
|
104
102
|
|
105
|
-
describe "#extract_change_file" do
|
106
|
-
let(:file) {collector.extract_change_file(line)}
|
107
|
-
|
108
|
-
context "with a simple changed file" do
|
109
|
-
let(:line) {"37 30 lib/file.rb"}
|
110
|
-
it {file[:additions].should == 37}
|
111
|
-
it {file[:deletions].should == 30}
|
112
|
-
it {file[:file].should == "lib/file.rb"}
|
113
|
-
end
|
114
|
-
|
115
|
-
context "with a simple rename/copy changed file" do
|
116
|
-
let(:line) {"11 3 old_file.rb => lib/file.rb"}
|
117
|
-
it {file[:additions].should == 11}
|
118
|
-
it {file[:deletions].should == 3}
|
119
|
-
it {file[:file].should == "lib/file.rb"}
|
120
|
-
it {file[:old_file].should == "old_file.rb"}
|
121
|
-
end
|
122
|
-
|
123
|
-
context "with a complex rename/copy changed file" do
|
124
|
-
let(:line) {"- - lib/{old_dir => new_dir}/file.rb"}
|
125
|
-
it {file[:additions].should == 0}
|
126
|
-
it {file[:deletions].should == 0}
|
127
|
-
it {file[:file].should == "lib/new_dir/file.rb"}
|
128
|
-
it {file[:old_file].should == "lib/old_dir/file.rb"}
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
describe "#extract_create_delete_file" do
|
133
|
-
let(:file) {collector.extract_create_delete_file(line)}
|
134
|
-
|
135
|
-
context "with a create changed file" do
|
136
|
-
let(:line) {"create mode 100644 lib/dir/file.rb"}
|
137
|
-
it {file[:status].should == "create"}
|
138
|
-
it {file[:file].should == "lib/dir/file.rb"}
|
139
|
-
end
|
140
|
-
|
141
|
-
context "with a delete changed file" do
|
142
|
-
let(:line) {"delete mode 100644 lib/file.rb"}
|
143
|
-
it {file[:status].should == "delete"}
|
144
|
-
it {file[:file].should == "lib/file.rb"}
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
describe "#extract_rename_copy_file" do
|
149
|
-
let(:file) {collector.extract_rename_copy_file(line)}
|
150
|
-
|
151
|
-
context "with a rename changed file" do
|
152
|
-
let(:line) {"rename lib/{old_dir => new_dir}/file.rb (100%)"}
|
153
|
-
it {file[:status].should == "rename"}
|
154
|
-
it {file[:old_file].should == "lib/old_dir/file.rb"}
|
155
|
-
it {file[:new_file].should == "lib/new_dir/file.rb"}
|
156
|
-
it {file[:similar].should == 100}
|
157
|
-
end
|
158
|
-
|
159
|
-
context "with a copy changed file" do
|
160
|
-
let(:line) {"copy lib/dir/{old_file.rb => new_file.rb} (75%)"}
|
161
|
-
it {file[:status].should == "copy"}
|
162
|
-
it {file[:old_file].should == "lib/dir/old_file.rb"}
|
163
|
-
it {file[:new_file].should == "lib/dir/new_file.rb"}
|
164
|
-
it {file[:similar].should == 75}
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
103
|
describe "#acquire_commit_data" do
|
169
104
|
let(:input) {fixture(fixture_file).read}
|
170
105
|
let(:data) {collector.acquire_commit_data(input)}
|
@@ -204,7 +139,7 @@ describe Collector do
|
|
204
139
|
context "with no changes" do
|
205
140
|
let(:buffer) {[]}
|
206
141
|
it {files.size.should == 0}
|
207
|
-
it {files[0].should
|
142
|
+
it {files[0].should.nil?}
|
208
143
|
end
|
209
144
|
|
210
145
|
context "with all types (create,delete,rename,copy) of files" do
|
@@ -256,7 +191,7 @@ describe Collector do
|
|
256
191
|
it {data[:files][0][:name].should == "Gemfile"}
|
257
192
|
it {data[:files][0][:additions].should == 0}
|
258
193
|
it {data[:files][0][:deletions].should == 1}
|
259
|
-
it {data[:files][0][:status].should
|
194
|
+
it {data[:files][0][:status].should.nil?}
|
260
195
|
it {data[:files][0][:binary].should == false}
|
261
196
|
it {data[:files][0][:image].should == false}
|
262
197
|
it {data[:files][0][:vendored].should == false}
|
@@ -276,7 +211,7 @@ describe Collector do
|
|
276
211
|
it {data[:files][2][:name].should == "lib/git_statistics/initialize.rb"}
|
277
212
|
it {data[:files][2][:additions].should == 0}
|
278
213
|
it {data[:files][2][:deletions].should == 1}
|
279
|
-
it {data[:files][2][:status].should
|
214
|
+
it {data[:files][2][:status].should.nil?}
|
280
215
|
it {data[:files][2][:binary].should == false}
|
281
216
|
it {data[:files][2][:image].should == false}
|
282
217
|
it {data[:files][2][:vendored].should == false}
|
@@ -286,12 +221,12 @@ describe Collector do
|
|
286
221
|
|
287
222
|
context "with buffer that has no file changes" do
|
288
223
|
let(:fixture_file) {"commit_buffer_information.txt"}
|
289
|
-
it {data.should
|
224
|
+
it {data.should.nil?}
|
290
225
|
end
|
291
226
|
|
292
227
|
context "with invalid buffer" do
|
293
228
|
let(:buffer) {"invalid input"}
|
294
|
-
it {data.should
|
229
|
+
it {data.should.nil?}
|
295
230
|
end
|
296
231
|
end
|
297
232
|
|
@@ -305,7 +240,7 @@ describe Collector do
|
|
305
240
|
|
306
241
|
context "with invalid sha" do
|
307
242
|
let(:sha) {"111111aa111a11111a11aa11aaaa11a111111a11"}
|
308
|
-
it {results.should
|
243
|
+
it {results.should.nil?}
|
309
244
|
end
|
310
245
|
end
|
311
246
|
|
@@ -321,7 +256,7 @@ describe Collector do
|
|
321
256
|
|
322
257
|
context "with invalid blob" do
|
323
258
|
let(:file) {{:file => "dir/nothing.rb"}}
|
324
|
-
it {blob.should
|
259
|
+
it {blob.should.nil?}
|
325
260
|
end
|
326
261
|
|
327
262
|
context "with deleted file" do
|
@@ -373,7 +308,7 @@ describe Collector do
|
|
373
308
|
it {data_file[:name].should == file[:file]}
|
374
309
|
it {data_file[:additions].should == file[:additions]}
|
375
310
|
it {data_file[:deletions].should == file[:deletions]}
|
376
|
-
it {data_file[:status].should
|
311
|
+
it {data_file[:status].should.nil?}
|
377
312
|
it {data_file[:binary].should == false}
|
378
313
|
it {data_file[:image].should == false}
|
379
314
|
it {data_file[:vendored].should == false}
|
@@ -393,7 +328,7 @@ describe Collector do
|
|
393
328
|
it {data_file[:name].should == file[:file]}
|
394
329
|
it {data_file[:additions].should == file[:additions]}
|
395
330
|
it {data_file[:deletions].should == file[:deletions]}
|
396
|
-
it {data_file[:status].should
|
331
|
+
it {data_file[:status].should.nil?}
|
397
332
|
it {data_file[:binary].should == false}
|
398
333
|
it {data_file[:image].should == false}
|
399
334
|
it {data_file[:vendored].should == false}
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
include GitStatistics
|
3
|
+
|
4
|
+
describe CommitLineExtractor do
|
5
|
+
|
6
|
+
let(:extractor) { CommitLineExtractor.new(line) }
|
7
|
+
|
8
|
+
describe "#extract_change_file" do
|
9
|
+
let(:file) { extractor.changed }
|
10
|
+
|
11
|
+
context "with a simple changed file" do
|
12
|
+
let(:line) {"37 30 lib/file.rb"}
|
13
|
+
it {file[:additions].should == 37}
|
14
|
+
it {file[:deletions].should == 30}
|
15
|
+
it {file[:file].should == "lib/file.rb"}
|
16
|
+
end
|
17
|
+
|
18
|
+
context "with a simple rename/copy changed file" do
|
19
|
+
let(:line) {"11 3 old_file.rb => lib/file.rb"}
|
20
|
+
it {file[:additions].should == 11}
|
21
|
+
it {file[:deletions].should == 3}
|
22
|
+
it {file[:file].should == "lib/file.rb"}
|
23
|
+
it {file[:old_file].should == "old_file.rb"}
|
24
|
+
end
|
25
|
+
|
26
|
+
context "with a complex rename/copy changed file" do
|
27
|
+
let(:line) {"- - lib/{old_dir => new_dir}/file.rb"}
|
28
|
+
it {file[:additions].should == 0}
|
29
|
+
it {file[:deletions].should == 0}
|
30
|
+
it {file[:file].should == "lib/new_dir/file.rb"}
|
31
|
+
it {file[:old_file].should == "lib/old_dir/file.rb"}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#extract_create_delete_file" do
|
36
|
+
let(:file) { extractor.created_or_deleted }
|
37
|
+
|
38
|
+
context "with a create changed file" do
|
39
|
+
let(:line) {"create mode 100644 lib/dir/file.rb"}
|
40
|
+
it {file[:status].should == "create"}
|
41
|
+
it {file[:file].should == "lib/dir/file.rb"}
|
42
|
+
end
|
43
|
+
|
44
|
+
context "with a delete changed file" do
|
45
|
+
let(:line) {"delete mode 100644 lib/file.rb"}
|
46
|
+
it {file[:status].should == "delete"}
|
47
|
+
it {file[:file].should == "lib/file.rb"}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#extract_rename_copy_file" do
|
52
|
+
let(:file) { extractor.renamed_or_copied }
|
53
|
+
|
54
|
+
context "with a rename changed file" do
|
55
|
+
let(:line) {"rename lib/{old_dir => new_dir}/file.rb (100%)"}
|
56
|
+
it {file[:status].should == "rename"}
|
57
|
+
it {file[:old_file].should == "lib/old_dir/file.rb"}
|
58
|
+
it {file[:new_file].should == "lib/new_dir/file.rb"}
|
59
|
+
it {file[:similar].should == 100}
|
60
|
+
end
|
61
|
+
|
62
|
+
context "with a copy changed file" do
|
63
|
+
let(:line) {"copy lib/dir/{old_file.rb => new_file.rb} (75%)"}
|
64
|
+
it {file[:status].should == "copy"}
|
65
|
+
it {file[:old_file].should == "lib/dir/old_file.rb"}
|
66
|
+
it {file[:new_file].should == "lib/dir/new_file.rb"}
|
67
|
+
it {file[:similar].should == 75}
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/spec/commits_spec.rb
CHANGED
@@ -170,12 +170,12 @@ describe Commits do
|
|
170
170
|
|
171
171
|
context "with invalid type" do
|
172
172
|
let(:sort) {:wrong}
|
173
|
-
it {stats.should
|
173
|
+
it {stats.should.nil?}
|
174
174
|
end
|
175
175
|
|
176
176
|
context "with invalid data" do
|
177
177
|
let(:fixture_file) {nil}
|
178
|
-
it {stats.should
|
178
|
+
it {stats.should.nil?}
|
179
179
|
end
|
180
180
|
end
|
181
181
|
|
data/spec/spec_helper.rb
CHANGED
@@ -11,7 +11,7 @@ def fixture(file)
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def setup_commits(commits, file_load, file_save, pretty)
|
14
|
-
return if file_load
|
14
|
+
return if file_load.nil? || file_save.nil?
|
15
15
|
commits.load(fixture(file_load))
|
16
16
|
commits.save(file_save, pretty)
|
17
17
|
end
|
data/spec/utilities_spec.rb
CHANGED
@@ -18,22 +18,22 @@ describe Utilities do
|
|
18
18
|
|
19
19
|
context "when not in a repository directory" do
|
20
20
|
let(:dir) {Dir.pwd + "../"} # git_statistics/../
|
21
|
-
it {repo.should
|
21
|
+
it {repo.should.nil?}
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
describe "#
|
25
|
+
describe "#max_length_in_list" do
|
26
26
|
let(:max) {nil}
|
27
27
|
let(:list) {[]}
|
28
|
-
let(:results) {Utilities.
|
28
|
+
let(:results) {Utilities.max_length_in_list(list, max)}
|
29
29
|
|
30
30
|
context "with empty list" do
|
31
|
-
it {results.should
|
31
|
+
it {results.should.nil?}
|
32
32
|
end
|
33
33
|
|
34
34
|
context "with nil list" do
|
35
35
|
let(:list) {nil}
|
36
|
-
it {results.should
|
36
|
+
it {results.should.nil?}
|
37
37
|
end
|
38
38
|
|
39
39
|
context "with preset minimum length" do
|
@@ -52,35 +52,6 @@ describe Utilities do
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
-
describe "#unique_data_in_hash" do
|
56
|
-
let(:type) {:word}
|
57
|
-
let(:list) {Utilities.unique_data_in_hash(data, type)}
|
58
|
-
|
59
|
-
context "with valid type" do
|
60
|
-
let(:data) {
|
61
|
-
{:entry_a => {type => "test"},
|
62
|
-
:entry_b => {type => "a"},
|
63
|
-
:entry_c => {type => "a"},
|
64
|
-
:entry_d => {type => "is"},
|
65
|
-
:entry_e => {type => "test"}}
|
66
|
-
}
|
67
|
-
|
68
|
-
it {list.size.should == 3}
|
69
|
-
it {list.include?("is").should be_true}
|
70
|
-
it {list.include?("a").should be_true}
|
71
|
-
it {list.include?("test").should be_true}
|
72
|
-
end
|
73
|
-
|
74
|
-
context "with invalid type" do
|
75
|
-
let(:data) {
|
76
|
-
{:entry_a => {:wrong => "test"},
|
77
|
-
:entry_b => {:wrong => "is"}}
|
78
|
-
}
|
79
|
-
|
80
|
-
it {list.should == [nil]}
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
55
|
describe "#clean_string" do
|
85
56
|
let(:unclean) {" master "}
|
86
57
|
let(:clean) {Utilities.clean_string(unclean)}
|
@@ -90,6 +61,23 @@ describe Utilities do
|
|
90
61
|
end
|
91
62
|
end
|
92
63
|
|
64
|
+
describe "#get_modified_time" do
|
65
|
+
let(:file) { 'file' }
|
66
|
+
before do
|
67
|
+
OS.stub(:mac?) { false }
|
68
|
+
OS.stub(:linux?) { false }
|
69
|
+
end
|
70
|
+
after { Utilities.get_modified_time(file) }
|
71
|
+
context "on a Mac" do
|
72
|
+
before { OS.stub(:mac?) { true } }
|
73
|
+
it { Utilities.should_receive(:time_at).with(%[stat -f %m file]) { 10 } }
|
74
|
+
end
|
75
|
+
context "on a Linux" do
|
76
|
+
before { OS.stub(:linux?) { true } }
|
77
|
+
it { Utilities.should_receive(:time_at).with(%[stat -c %Y file]) { 10 } }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
93
81
|
describe "#split_old_new_file" do
|
94
82
|
let(:files) {Utilities.split_old_new_file(old, new)}
|
95
83
|
context "with a change in middle" do
|
@@ -147,12 +135,12 @@ describe Utilities do
|
|
147
135
|
|
148
136
|
context "file is nil" do
|
149
137
|
let(:blob) {Utilities.find_blob_in_tree(tree, nil)}
|
150
|
-
it {blob.should
|
138
|
+
it {blob.should.nil?}
|
151
139
|
end
|
152
140
|
|
153
141
|
context "file is empty" do
|
154
142
|
let(:file) {""}
|
155
|
-
it {blob.should
|
143
|
+
it {blob.should.nil?}
|
156
144
|
end
|
157
145
|
|
158
146
|
context "file is submodule" do
|
@@ -163,8 +151,8 @@ describe Utilities do
|
|
163
151
|
end
|
164
152
|
end
|
165
153
|
|
166
|
-
describe "#
|
167
|
-
let(:files) {Utilities.
|
154
|
+
describe "#number_of_matching_files" do
|
155
|
+
let(:files) {Utilities.number_of_matching_files(directory, pattern)}
|
168
156
|
let(:pattern) {/\d+\.json/}
|
169
157
|
let(:directory) {Dir.pwd + File::Separator + "tmp_dir_for_spec" + File::Separator}
|
170
158
|
|
@@ -172,6 +160,12 @@ describe Utilities do
|
|
172
160
|
FileUtils.mkdir_p(directory)
|
173
161
|
end
|
174
162
|
|
163
|
+
context "with missing directory" do
|
164
|
+
subject { files }
|
165
|
+
before { FileUtils.rmdir(directory) }
|
166
|
+
it { should == 0 }
|
167
|
+
end
|
168
|
+
|
175
169
|
after(:each) do
|
176
170
|
FileUtils.rmdir(directory)
|
177
171
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: git_statistics
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-01-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: json
|
@@ -113,12 +113,14 @@ files:
|
|
113
113
|
- lib/git_statistics.rb
|
114
114
|
- lib/git_statistics/blob.rb
|
115
115
|
- lib/git_statistics/collector.rb
|
116
|
+
- lib/git_statistics/commit_line_extractor.rb
|
116
117
|
- lib/git_statistics/commits.rb
|
117
118
|
- lib/git_statistics/initialize.rb
|
118
119
|
- lib/git_statistics/results.rb
|
119
120
|
- lib/git_statistics/utilities.rb
|
120
121
|
- lib/git_statistics/version.rb
|
121
122
|
- spec/collector_spec.rb
|
123
|
+
- spec/commit_line_extractor_spec.rb
|
122
124
|
- spec/commits_spec.rb
|
123
125
|
- spec/fixtures/commit_buffer_changes.txt
|
124
126
|
- spec/fixtures/commit_buffer_information.txt
|
@@ -161,6 +163,7 @@ specification_version: 3
|
|
161
163
|
summary: Gem that provides the ability to gather detailed git statistics
|
162
164
|
test_files:
|
163
165
|
- spec/collector_spec.rb
|
166
|
+
- spec/commit_line_extractor_spec.rb
|
164
167
|
- spec/commits_spec.rb
|
165
168
|
- spec/fixtures/commit_buffer_changes.txt
|
166
169
|
- spec/fixtures/commit_buffer_information.txt
|