git_statistics 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|