git_statistics 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/git-statistics +1 -1
- data/bin/git_statistics +1 -1
- data/lib/git_statistics.rb +48 -32
- data/lib/git_statistics/collector.rb +40 -187
- data/lib/git_statistics/commit_summary.rb +86 -0
- data/lib/git_statistics/commits.rb +18 -14
- data/lib/git_statistics/diff_summary.rb +79 -0
- data/lib/git_statistics/formatters/console.rb +31 -26
- data/lib/git_statistics/initialize.rb +7 -6
- data/lib/git_statistics/pipe.rb +1 -1
- data/lib/git_statistics/utilities.rb +28 -91
- data/lib/git_statistics/version.rb +1 -1
- data/spec/collector_spec.rb +41 -235
- data/spec/commit_summary_spec.rb +126 -0
- data/spec/commits_spec.rb +52 -58
- data/spec/formatters/console_spec.rb +11 -10
- data/spec/utilities_spec.rb +15 -128
- metadata +22 -75
- data/lib/git_statistics/blob.rb +0 -5
- data/lib/git_statistics/branches.rb +0 -35
- data/lib/git_statistics/commit_line_extractor.rb +0 -50
- data/lib/git_statistics/regex_matcher.rb +0 -23
- data/spec/branches_spec.rb +0 -44
- data/spec/commit_line_extractor_spec.rb +0 -70
- data/spec/regex_matcher_spec.rb +0 -35
@@ -33,7 +33,7 @@ module GitStatistics
|
|
33
33
|
Dir.entries(path).reject { |file| %w[. ..].include?(file) }
|
34
34
|
end
|
35
35
|
|
36
|
-
def flush_commits(force
|
36
|
+
def flush_commits(force=false)
|
37
37
|
if size >= limit || force
|
38
38
|
file_count = Utilities.number_of_matching_files(path, /\d+\.json/)
|
39
39
|
save(File.join(path, "#{file_count}.json"), @pretty)
|
@@ -54,13 +54,18 @@ module GitStatistics
|
|
54
54
|
# Identify authors and author type
|
55
55
|
type = email ? :author_email : :author
|
56
56
|
|
57
|
-
#
|
58
|
-
Dir.entries(path).
|
59
|
-
|
60
|
-
if file =~ /\d+\.json/
|
61
|
-
load(File.join(path, file))
|
57
|
+
# Process the commits from file or memory
|
58
|
+
files = Dir.entries(path) - [".", ".."]
|
59
|
+
if files.size == 0
|
62
60
|
process_commits(type, merge)
|
63
|
-
|
61
|
+
else
|
62
|
+
#Load commit file and extract the commits
|
63
|
+
files.each do |file|
|
64
|
+
if file =~ /\d+\.json/
|
65
|
+
load(File.join(path, file))
|
66
|
+
process_commits(type, merge)
|
67
|
+
clear
|
68
|
+
end
|
64
69
|
end
|
65
70
|
end
|
66
71
|
end
|
@@ -112,8 +117,9 @@ module GitStatistics
|
|
112
117
|
data[:languages][file[:language].to_sym][:additions] += file[:additions]
|
113
118
|
data[:languages][file[:language].to_sym][:deletions] += file[:deletions]
|
114
119
|
|
120
|
+
# Keep count of languages status (i.e., added, deleted) and keep keys consistent (i.e., added_files, deleted_files)
|
115
121
|
if file[:status] != nil
|
116
|
-
data[:languages][file[:language].to_sym][file[:status].to_sym] += 1
|
122
|
+
data[:languages][file[:language].to_sym][(file[:status]+'_files').to_sym] += 1
|
117
123
|
end
|
118
124
|
|
119
125
|
return data
|
@@ -125,10 +131,8 @@ module GitStatistics
|
|
125
131
|
data[:commits] += 1
|
126
132
|
data[:additions] += commit[:additions]
|
127
133
|
data[:deletions] += commit[:deletions]
|
128
|
-
data[:
|
129
|
-
data[:
|
130
|
-
data[:rename] += commit[:rename] if commit[:rename] != nil
|
131
|
-
data[:copy] += commit[:copy] if commit[:copy] != nil
|
134
|
+
data[:added_files] += commit[:added_files] if !commit[:added_files].nil?
|
135
|
+
data[:deleted_files] += commit[:deleted_files] if !commit[:deleted_files].nil?
|
132
136
|
return data
|
133
137
|
end
|
134
138
|
|
@@ -142,9 +146,9 @@ module GitStatistics
|
|
142
146
|
# Ensure the path to the file exists
|
143
147
|
FileUtils.mkdir_p(File.dirname(file))
|
144
148
|
# Save file in a simple or pretty format
|
145
|
-
File.open(file, 'w') do |
|
149
|
+
File.open(file, 'w') do |f|
|
146
150
|
json_content = pretty ? JSON.pretty_generate(self) : self.to_json
|
147
|
-
|
151
|
+
f.write(json_content)
|
148
152
|
end
|
149
153
|
end
|
150
154
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module GitStatistics
|
2
|
+
class DiffSummary < SimpleDelegator
|
3
|
+
def initialize(repo, patch)
|
4
|
+
@repo = repo
|
5
|
+
super(patch)
|
6
|
+
end
|
7
|
+
|
8
|
+
# We flip these around since we are diffing in the opposite direction -- new.diff(old)
|
9
|
+
def additions
|
10
|
+
__getobj__.deletions
|
11
|
+
end
|
12
|
+
|
13
|
+
# We flip these around since we are diffing in the opposite direction -- new.diff(old)
|
14
|
+
def deletions
|
15
|
+
__getobj__.additions
|
16
|
+
end
|
17
|
+
|
18
|
+
def net
|
19
|
+
additions - deletions
|
20
|
+
end
|
21
|
+
|
22
|
+
# We flip these around since we are diffing in the opposite direction -- new.diff(old)
|
23
|
+
def status
|
24
|
+
if delta.status == :deleted
|
25
|
+
return :added
|
26
|
+
elsif delta.status == :added
|
27
|
+
return :deleted
|
28
|
+
else
|
29
|
+
return delta.status
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def similarity
|
34
|
+
delta.similarity
|
35
|
+
end
|
36
|
+
|
37
|
+
def filename
|
38
|
+
if (status == :deleted)
|
39
|
+
delta.old_file[:path]
|
40
|
+
else
|
41
|
+
delta.new_file[:path]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# We flip these around since we are diffing in the opposite direction -- new.diff(old)
|
46
|
+
def blob
|
47
|
+
begin
|
48
|
+
if (status == :deleted)
|
49
|
+
blob = @repo.lookup(delta.new_file[:oid]) # Look at new instead of old
|
50
|
+
else
|
51
|
+
blob = @repo.lookup(delta.old_file[:oid]) # Look at old instead of new
|
52
|
+
end
|
53
|
+
rescue Rugged::OdbError
|
54
|
+
Log.warn "Could not find object (most likely a submodule)"
|
55
|
+
blob = nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def inspect
|
60
|
+
%Q{<GitStatistics::FileStat @filename=#{filename} @status=#{status} @similarity=#{similarity} @language=#{language} @additions=#{additions}, @deletions=#{deletions}, @net=#{net}>}
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_json
|
64
|
+
{ filename: filename, status: status, similarity: similarity, language: language, additions: additions, deletions: deletions, net: net}
|
65
|
+
end
|
66
|
+
|
67
|
+
# Determine the language of the file from the blob
|
68
|
+
def language
|
69
|
+
language = "Unknown"
|
70
|
+
unless blob.nil?
|
71
|
+
detected_language = LanguageSniffer.detect(filename, :content => blob.content).language
|
72
|
+
unless detected_language.nil?
|
73
|
+
language = detected_language.name
|
74
|
+
end
|
75
|
+
end
|
76
|
+
language
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -2,10 +2,13 @@ module GitStatistics
|
|
2
2
|
module Formatters
|
3
3
|
class Console
|
4
4
|
|
5
|
+
attr_reader :output
|
5
6
|
attr_accessor :commits, :config
|
6
7
|
|
7
8
|
def initialize(commits)
|
8
9
|
@commits = commits
|
10
|
+
@config = {}
|
11
|
+
@output = []
|
9
12
|
end
|
10
13
|
|
11
14
|
def prepare_result_summary(sort, email, top_n = 0)
|
@@ -26,7 +29,7 @@ module GitStatistics
|
|
26
29
|
|
27
30
|
# Acquire formatting pattern for output
|
28
31
|
@pattern = "| %-#{config[:author_length]}s | %-#{config[:language_length]}s | %7s | %9s | %9s | %7s | %7s | %7s | %6s | %6s |"
|
29
|
-
config
|
32
|
+
@config
|
30
33
|
end
|
31
34
|
|
32
35
|
def print_summary(sort, email, top_n = 0)
|
@@ -35,47 +38,45 @@ module GitStatistics
|
|
35
38
|
config = prepare_result_summary(sort, email, top_n)
|
36
39
|
|
37
40
|
# Print query/header information
|
38
|
-
|
39
|
-
|
41
|
+
print_header
|
40
42
|
# Print per author information
|
41
43
|
config[:data].each do |name, commit_data|
|
42
|
-
|
43
|
-
|
44
|
+
print_row(name, commit_data)
|
45
|
+
print_language_data(commit_data)
|
44
46
|
end
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
47
|
+
add_row separator
|
48
|
+
print_row("Repository Totals", commit_totals)
|
49
|
+
print_language_data(commit_totals)
|
50
|
+
display!
|
51
|
+
end
|
52
|
+
|
53
|
+
def display!
|
54
|
+
output.join("\n")
|
49
55
|
end
|
50
56
|
|
51
57
|
def print_language_data(data)
|
52
|
-
output = []
|
53
58
|
# Print information of each language for the data
|
54
59
|
data[:languages].each do |language, commit_data|
|
55
|
-
|
60
|
+
print_row("", commit_data, language)
|
56
61
|
end
|
57
62
|
output
|
58
63
|
end
|
59
64
|
|
60
65
|
def print_row(name, commit_info, language = '')
|
61
|
-
format_for_row(name, language, commit_info[:commits],
|
62
|
-
commit_info[:additions], commit_info[:deletions], commit_info[:
|
63
|
-
commit_info[:
|
66
|
+
add_row format_for_row(name, language, commit_info[:commits],
|
67
|
+
commit_info[:additions], commit_info[:deletions], commit_info[:added_files],
|
68
|
+
commit_info[:deleted_files], commit_info[:renamed_files], commit_info[:copied_files], commit_info[:merges])
|
64
69
|
end
|
65
70
|
|
66
71
|
def print_header
|
67
|
-
|
68
|
-
|
69
|
-
output << get_header_info
|
70
|
-
output
|
72
|
+
get_author_info(@commits.stats.size)
|
73
|
+
get_header_info
|
71
74
|
end
|
72
75
|
|
73
76
|
def get_header_info
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
headers << separator
|
78
|
-
headers
|
77
|
+
add_row separator
|
78
|
+
add_row format_for_row('Name/Email', 'Language', 'Commits', 'Additions', 'Deletions', 'Creates', 'Deletes', 'Renames', 'Copies', 'Merges')
|
79
|
+
add_row separator
|
79
80
|
end
|
80
81
|
|
81
82
|
def format_for_row(*columns)
|
@@ -83,15 +84,19 @@ module GitStatistics
|
|
83
84
|
end
|
84
85
|
|
85
86
|
def separator
|
86
|
-
"-" * 89 + "-"*config[:author_length] + "-"*config[:language_length]
|
87
|
+
("-" * 89) + ("-" * config[:author_length]) + ("-" * config[:language_length])
|
87
88
|
end
|
88
89
|
|
89
90
|
def get_author_info(total_authors)
|
90
91
|
if config[:top_n] > 0 && config[:top_n] < total_authors
|
91
|
-
|
92
|
+
add_row "Top #{config[:top_n]} authors(#{total_authors}) sorted by #{config[:sort]}"
|
93
|
+
else
|
94
|
+
add_row "All authors(#{total_authors}) sorted by #{config[:sort]}"
|
92
95
|
end
|
96
|
+
end
|
93
97
|
|
94
|
-
|
98
|
+
def add_row(string_or_array)
|
99
|
+
output.concat Array(string_or_array)
|
95
100
|
end
|
96
101
|
end
|
97
102
|
end
|
@@ -1,10 +1,11 @@
|
|
1
1
|
require 'json'
|
2
|
-
require 'grit'
|
3
|
-
require 'linguist'
|
4
2
|
require 'pathname'
|
3
|
+
require 'ostruct'
|
4
|
+
require 'optparse'
|
5
|
+
require 'delegate'
|
6
|
+
require 'fileutils'
|
5
7
|
|
6
|
-
|
7
|
-
require '
|
8
|
-
require 'git_statistics/regex_matcher'
|
8
|
+
require 'rugged'
|
9
|
+
require 'language_sniffer'
|
9
10
|
|
10
|
-
Dir.glob(File.dirname(__FILE__) + '/**/*.rb') {|file| require file}
|
11
|
+
Dir.glob(File.dirname(__FILE__) + '/**/*.rb') { |file| require file }
|
data/lib/git_statistics/pipe.rb
CHANGED
@@ -3,103 +3,35 @@ require 'rbconfig'
|
|
3
3
|
module GitStatistics
|
4
4
|
module Utilities
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
raise NotInRepository unless repo_path
|
12
|
-
Grit::Repo.new(repo_path.to_s)
|
13
|
-
rescue NotInRepository
|
14
|
-
Log.error "You must be within a Git project to run git-statistics."
|
15
|
-
exit 0
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.max_length_in_list(list, max = nil)
|
19
|
-
return nil if list.nil?
|
20
|
-
list.each do |key,value|
|
21
|
-
max = key.length if max.nil? || key.length > max
|
22
|
-
end
|
23
|
-
max
|
6
|
+
def self.max_length_in_list(list, min_length = nil)
|
7
|
+
list ||= []
|
8
|
+
min_length = min_length.to_i
|
9
|
+
list_max = list.map { |k,_| k.length }.max || 0
|
10
|
+
list_max >= min_length ? list_max : min_length
|
24
11
|
end
|
25
12
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
# Handle recombine the file splits into their whole paths)
|
32
|
-
if split_old.one? && split_new.one?
|
33
|
-
old_file = split_old[0]
|
34
|
-
new_file = split_new[0]
|
35
|
-
elsif split_new.one?
|
36
|
-
old_file = split_old[0] + split_old[1]
|
37
|
-
new_file = split_old[0] + split_new[0]
|
38
|
-
elsif split_old.one?
|
39
|
-
old_file = split_old[0] + split_new[1]
|
40
|
-
new_file = split_old[0] + split_new[0] + split_new[1]
|
41
|
-
else
|
42
|
-
old_file = split_old[0] + split_old[1] + split_new[1]
|
43
|
-
new_file = split_old[0] + split_new[0] + split_new[1]
|
44
|
-
end
|
45
|
-
|
46
|
-
# Return files, yet remove the '//' if present from combining splits
|
47
|
-
return {:old_file => old_file.gsub('//', '/'),
|
48
|
-
:new_file => new_file.gsub('//', '/')}
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.find_blob_in_tree(tree, file)
|
52
|
-
# Check If cannot find tree in commit or if we found a submodule as the changed file
|
53
|
-
if tree.nil? || file.nil?
|
54
|
-
return nil
|
55
|
-
elsif tree.instance_of?(Grit::Submodule)
|
56
|
-
return tree
|
57
|
-
end
|
58
|
-
|
59
|
-
# If the blob is within the current directory (tree)
|
60
|
-
if file.one?
|
61
|
-
blob = tree / file.first
|
62
|
-
|
63
|
-
# Check if blob is nil (could not find changed file in tree)
|
64
|
-
if blob.nil?
|
65
|
-
|
66
|
-
# Try looking for submodules as they cannot be found using tree / file notation
|
67
|
-
tree.contents.each do |content|
|
68
|
-
if file.first == content.name
|
69
|
-
return tree
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
# Exit through recusion with the base case of a nil tree/blob
|
74
|
-
return find_blob_in_tree(blob, file)
|
75
|
-
end
|
76
|
-
return blob
|
77
|
-
else
|
78
|
-
# Explore deeper in the tree to find the blob of the changed file
|
79
|
-
return find_blob_in_tree(tree / file.first, file[1..-1])
|
80
|
-
end
|
81
|
-
end
|
13
|
+
COMMANDS = {
|
14
|
+
:windows => ->{ raise "`stat` is not supported on Windows" },
|
15
|
+
:mac => ->{ "-f %m" }
|
16
|
+
}
|
17
|
+
COMMANDS.default = ->{ "-c %Y" }
|
82
18
|
|
83
19
|
def self.get_modified_time(file)
|
84
|
-
|
85
|
-
raise "`stat` is not supported on the Windows operating system"
|
86
|
-
end
|
87
|
-
flags = os == :mac ? "-f %m" : "-c %Y"
|
20
|
+
flags = COMMANDS[os].()
|
88
21
|
time_at("stat #{flags} #{file}")
|
89
22
|
end
|
90
23
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
:
|
95
|
-
|
96
|
-
:
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
:unknown
|
24
|
+
class OperatingSystem
|
25
|
+
OPERATING_SYSTEMS = {
|
26
|
+
/mswin|msys|mingw|cygwin|bccwin|wince|emc/ => :windows,
|
27
|
+
/darwin|mac os/ => :mac,
|
28
|
+
/linux/ => :linux,
|
29
|
+
/solaris|bsd/ => :unix
|
30
|
+
}
|
31
|
+
OPERATING_SYSTEMS.default = :unknown
|
32
|
+
|
33
|
+
def self.determine(os_name)
|
34
|
+
OPERATING_SYSTEMS.select { |k,_| k =~ os_name }.first
|
103
35
|
end
|
104
36
|
end
|
105
37
|
|
@@ -107,11 +39,16 @@ module GitStatistics
|
|
107
39
|
Time.at(%x{#{cmd}}.to_i)
|
108
40
|
end
|
109
41
|
|
42
|
+
def self.os
|
43
|
+
OperatingSystem.determine(RbConfig::CONFIG['host_os'])
|
44
|
+
end
|
45
|
+
|
110
46
|
def self.number_of_matching_files(directory, pattern)
|
111
47
|
Dir.entries(directory).grep(pattern).size
|
112
48
|
rescue SystemCallError
|
113
|
-
|
49
|
+
Log.error "No such directory #{File.expand_path(directory)}"
|
114
50
|
0
|
115
51
|
end
|
52
|
+
|
116
53
|
end
|
117
54
|
end
|
data/spec/collector_spec.rb
CHANGED
@@ -5,33 +5,29 @@ describe Collector do
|
|
5
5
|
let(:limit) {100}
|
6
6
|
let(:fresh) {true}
|
7
7
|
let(:pretty) {false}
|
8
|
-
let(:
|
9
|
-
|
10
|
-
# Create buffer which is an array of cleaned lines
|
11
|
-
let(:buffer) {
|
12
|
-
fixture(fixture_file).lines
|
13
|
-
}
|
8
|
+
let(:repo) { GIT_REPO }
|
9
|
+
let(:collector) {Collector.new(repo, limit, fresh, pretty)}
|
14
10
|
|
15
11
|
describe "#collect" do
|
16
|
-
let(:branch) {
|
12
|
+
let(:branch) {CLI::DEFAULT_BRANCH}
|
17
13
|
let(:email) {false}
|
18
14
|
let(:merge) {true}
|
19
|
-
let(:time_since) {"
|
20
|
-
let(:time_until) {"
|
15
|
+
let(:time_since) {"Tue Sep 24 14:15:44 2012 -0400"}
|
16
|
+
let(:time_until) {"Tue Sep 26 14:45:05 2012 -0400"}
|
21
17
|
let(:author) {"Kevin Jalbert"}
|
22
18
|
|
23
19
|
let(:setup) {
|
24
|
-
collector.collect(branch, time_since, time_until)
|
20
|
+
collector.collect({:branch => branch, :time_since => time_since, :time_until => time_until})
|
25
21
|
collector.commits.calculate_statistics(email, merge)
|
26
22
|
@subject = collector.commits.stats[author]
|
27
23
|
}
|
28
24
|
|
29
25
|
context "with no merge commits" do
|
30
26
|
let(:merge) {false}
|
31
|
-
let(:time_since) {"
|
32
|
-
let(:time_until) {"
|
27
|
+
let(:time_since) {"Tue Sep 10 14:15:44 2012 -0400"}
|
28
|
+
let(:time_until) {"Tue Sep 11 14:45:05 2012 -0400"}
|
33
29
|
|
34
|
-
before(:all) {setup}
|
30
|
+
before(:all) { setup }
|
35
31
|
|
36
32
|
it{@subject[:additions].should == 276}
|
37
33
|
it{@subject[:deletions].should == 99}
|
@@ -40,279 +36,89 @@ describe Collector do
|
|
40
36
|
|
41
37
|
it{@subject[:languages][:Ruby][:additions].should == 270}
|
42
38
|
it{@subject[:languages][:Ruby][:deletions].should == 99}
|
43
|
-
it{@subject[:languages][:Ruby][:
|
44
|
-
it{@subject[:languages][:
|
45
|
-
it{@subject[:languages][:
|
46
|
-
it{@subject[:languages][:
|
39
|
+
it{@subject[:languages][:Ruby][:added_files].should == 2}
|
40
|
+
it{@subject[:languages][:Text][:additions].should == 6}
|
41
|
+
it{@subject[:languages][:Text][:deletions].should == 0}
|
42
|
+
it{@subject[:languages][:Text][:added_files].should == 1}
|
47
43
|
end
|
48
44
|
|
49
45
|
context "with merge commits and merge option" do
|
50
|
-
before(:all) {setup}
|
46
|
+
before(:all) { setup }
|
51
47
|
|
52
|
-
it{@subject[:additions].should ==
|
53
|
-
it{@subject[:deletions].should ==
|
54
|
-
it{@subject[:commits].should ==
|
48
|
+
it{@subject[:additions].should == 1240}
|
49
|
+
it{@subject[:deletions].should == 934}
|
50
|
+
it{@subject[:commits].should == 9}
|
55
51
|
it{@subject[:merges].should == 1}
|
56
52
|
|
57
53
|
it{@subject[:languages][:Markdown][:additions].should == 1}
|
58
54
|
it{@subject[:languages][:Markdown][:deletions].should == 0}
|
59
|
-
it{@subject[:languages][:Ruby][:additions].should ==
|
60
|
-
it{@subject[:languages][:Ruby][:deletions].should ==
|
55
|
+
it{@subject[:languages][:Ruby][:additions].should == 1227}
|
56
|
+
it{@subject[:languages][:Ruby][:deletions].should == 934}
|
61
57
|
it{@subject[:languages][:Unknown][:additions].should == 12}
|
62
58
|
it{@subject[:languages][:Unknown][:deletions].should == 0}
|
63
59
|
end
|
64
60
|
|
65
61
|
context "with merge commits and no merge option" do
|
66
62
|
let(:merge) {false}
|
67
|
-
before(:all) {setup}
|
63
|
+
before(:all) { setup }
|
68
64
|
|
69
|
-
it{@subject[:additions].should ==
|
70
|
-
it{@subject[:deletions].should ==
|
71
|
-
it{@subject[:commits].should ==
|
65
|
+
it{@subject[:additions].should == 581}
|
66
|
+
it{@subject[:deletions].should == 452}
|
67
|
+
it{@subject[:commits].should == 8}
|
72
68
|
it{@subject[:merges].should == 0}
|
73
69
|
|
74
70
|
it{@subject[:languages][:Markdown][:additions].should == 1}
|
75
71
|
it{@subject[:languages][:Markdown][:deletions].should == 0}
|
76
|
-
it{@subject[:languages][:Ruby][:additions].should ==
|
77
|
-
it{@subject[:languages][:Ruby][:deletions].should ==
|
72
|
+
it{@subject[:languages][:Ruby][:additions].should == 574}
|
73
|
+
it{@subject[:languages][:Ruby][:deletions].should == 452}
|
78
74
|
it{@subject[:languages][:Unknown][:additions].should == 6}
|
79
75
|
it{@subject[:languages][:Unknown][:deletions].should == 0}
|
80
76
|
end
|
81
77
|
end
|
82
78
|
|
83
|
-
describe "#acquire_commit_data" do
|
84
|
-
let(:data) {collector.acquire_commit_data(buffer.first)}
|
85
|
-
|
86
|
-
context "no parent, first commit" do
|
87
|
-
let(:fixture_file) {"commit_buffer_information_first.txt"}
|
88
|
-
it {data[:sha].should == "111111aa111a11111a11aa11aaaa11a111111a11"}
|
89
|
-
it {data[:data][:author].should == "Test Author"}
|
90
|
-
it {data[:data][:author_email].should == "author@test.com"}
|
91
|
-
it {data[:data][:time].should == "2011-01-11 11:11:11 +0000"}
|
92
|
-
it {data[:data][:merge].should be_false}
|
93
|
-
end
|
94
|
-
|
95
|
-
context "without merge, one parent" do
|
96
|
-
let(:fixture_file) {"commit_buffer_information.txt"}
|
97
|
-
it {data[:sha].should == "111111aa111a11111a11aa11aaaa11a111111a11"}
|
98
|
-
it {data[:data][:author].should == "Test Author"}
|
99
|
-
it {data[:data][:author_email].should == "author@test.com"}
|
100
|
-
it {data[:data][:time].should == "2011-01-11 11:11:11 +0000"}
|
101
|
-
it {data[:data][:merge].should be_false}
|
102
|
-
end
|
103
|
-
|
104
|
-
context "with merge, two parents" do
|
105
|
-
let(:fixture_file) {"commit_buffer_information_with_merge.txt"}
|
106
|
-
it {data[:sha].should == "111111aa111a11111a11aa11aaaa11a111111a11"}
|
107
|
-
it {data[:data][:author].should == "Test Author"}
|
108
|
-
it {data[:data][:author_email].should == "author@test.com"}
|
109
|
-
it {data[:data][:time].should == "2011-01-11 11:11:11 +0000"}
|
110
|
-
it {data[:data][:merge].should be_true}
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
describe "#identify_changed_files" do
|
115
|
-
let(:files) {collector.identify_changed_files(buffer)}
|
116
|
-
let(:fixture_file) {"commit_buffer_changes.txt"}
|
117
|
-
|
118
|
-
context "with no changes" do
|
119
|
-
let(:buffer) {[]}
|
120
|
-
it {files.size.should == 0}
|
121
|
-
it {files[0].should.nil?}
|
122
|
-
end
|
123
|
-
|
124
|
-
context "with all types (create,delete,rename,copy) of files" do
|
125
|
-
it {files.size.should == 5}
|
126
|
-
|
127
|
-
it {files[0][:additions].should == 45}
|
128
|
-
it {files[0][:deletions].should == 1}
|
129
|
-
it {files[0][:file].should == "dir/README"}
|
130
|
-
|
131
|
-
it {files[1][:additions].should == 6}
|
132
|
-
it {files[1][:deletions].should == 5}
|
133
|
-
it {files[1][:old_file].should == "dir/lib/old_dir/copy_file.rb"}
|
134
|
-
it {files[1][:file].should == "dir/lib/new_dir/copy_file.rb"}
|
135
|
-
it {files[1][:status].should == "copy"}
|
136
|
-
|
137
|
-
it {files[2][:additions].should == 0}
|
138
|
-
it {files[2][:deletions].should == 0}
|
139
|
-
it {files[2][:old_file].should == "dir/lib/rename/old_file.rb"}
|
140
|
-
it {files[2][:file].should == "dir/lib/rename/new_file.rb"}
|
141
|
-
it {files[2][:status].should == "rename"}
|
142
|
-
|
143
|
-
it {files[3][:additions].should == 0}
|
144
|
-
it {files[3][:deletions].should == 127}
|
145
|
-
it {files[3][:file].should == "dir/lib/delete_file.rb"}
|
146
|
-
it {files[3][:status].should == "delete"}
|
147
|
-
|
148
|
-
it {files[4][:additions].should == 60}
|
149
|
-
it {files[4][:deletions].should == 0}
|
150
|
-
it {files[4][:file].should == "dir/lib/create_file.rb"}
|
151
|
-
it {files[4][:status].should == "create"}
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
79
|
describe "#extract_commit" do
|
156
|
-
let(:
|
157
|
-
|
158
|
-
context "with valid buffer" do
|
159
|
-
let(:fixture_file) {"commit_buffer_whole.txt"}
|
80
|
+
let(:commit) {repo.lookup(sha)}
|
81
|
+
let(:data) {collector.extract_commit(commit, nil)}
|
160
82
|
|
83
|
+
context "with valid commit" do
|
84
|
+
let(:sha) {"260bc61e2c42930d91f3503c5849b0a2351275cf"}
|
161
85
|
it {data[:author].should == "Kevin Jalbert"}
|
162
86
|
it {data[:author_email].should == "kevin.j.jalbert@gmail.com"}
|
163
|
-
it {data[:time].should
|
87
|
+
it {data[:time].should match /\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [-|+]\d{4}/}
|
164
88
|
|
165
89
|
it {data[:merge].should == false}
|
166
90
|
it {data[:additions].should == 30}
|
167
91
|
it {data[:deletions].should == 2}
|
168
|
-
it {data[:
|
92
|
+
it {data[:added_files].should == 1}
|
93
|
+
it {data[:deleted_files].should == 0}
|
169
94
|
|
170
|
-
it {data[:files][0][:
|
95
|
+
it {data[:files][0][:filename].should == "Gemfile"}
|
171
96
|
it {data[:files][0][:additions].should == 0}
|
172
97
|
it {data[:files][0][:deletions].should == 1}
|
173
98
|
it {data[:files][0][:status].should.nil?}
|
174
|
-
it {data[:files][0][:binary].should == false}
|
175
|
-
it {data[:files][0][:image].should == false}
|
176
|
-
it {data[:files][0][:vendored].should == false}
|
177
|
-
it {data[:files][0][:generated].should == false}
|
178
99
|
it {data[:files][0][:language].should == "Ruby"}
|
179
100
|
|
180
|
-
it {data[:files][1][:
|
101
|
+
it {data[:files][1][:filename].should == "Gemfile.lock"}
|
181
102
|
it {data[:files][1][:additions].should == 30}
|
182
103
|
it {data[:files][1][:deletions].should == 0}
|
183
|
-
it {data[:files][1][:status].should
|
184
|
-
it {data[:files][1][:binary].should == false}
|
185
|
-
it {data[:files][1][:image].should == false}
|
186
|
-
it {data[:files][1][:vendored].should == false}
|
187
|
-
it {data[:files][1][:generated].should == true}
|
104
|
+
it {data[:files][1][:status].should eq(:added)}
|
188
105
|
it {data[:files][1][:language].should == "Unknown"}
|
189
106
|
|
190
|
-
it {data[:files][2][:
|
107
|
+
it {data[:files][2][:filename].should == "lib/git_statistics/initialize.rb"}
|
191
108
|
it {data[:files][2][:additions].should == 0}
|
192
109
|
it {data[:files][2][:deletions].should == 1}
|
193
110
|
it {data[:files][2][:status].should.nil?}
|
194
|
-
it {data[:files][2][:binary].should == false}
|
195
|
-
it {data[:files][2][:image].should == false}
|
196
|
-
it {data[:files][2][:vendored].should == false}
|
197
|
-
it {data[:files][2][:generated].should == false}
|
198
111
|
it {data[:files][2][:language].should == "Ruby"}
|
199
112
|
end
|
200
113
|
|
201
|
-
context "with
|
202
|
-
let(:
|
203
|
-
it {data.
|
204
|
-
end
|
205
|
-
|
206
|
-
context "with invalid buffer" do
|
207
|
-
let(:buffer) {"invalid input"}
|
208
|
-
it {data.should.nil?}
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
describe "#fall_back_collect_commit" do
|
213
|
-
subject { collector.fall_back_collect_commit(sha) }
|
214
|
-
context "with valid sha" do
|
215
|
-
let(:fixture_file) { "commit_buffer_whole.txt" }
|
216
|
-
let(:sha) { "260bc61e2c42930d91f3503c5849b0a2351275cf" }
|
217
|
-
it { should == buffer }
|
114
|
+
context "with invalid commit" do
|
115
|
+
let(:sha) {"111111aa111a11111a11aa11aaaa11a111111a11"}
|
116
|
+
it { expect {data}.to raise_error(Rugged::OdbError) }
|
218
117
|
end
|
219
118
|
|
220
119
|
context "with invalid sha" do
|
221
|
-
let(:sha) { "
|
222
|
-
it {
|
223
|
-
end
|
224
|
-
end
|
225
|
-
|
226
|
-
describe "#get_blob" do
|
227
|
-
let(:sha) { "695b487432e8a1ede765b4e3efda088ab87a77f8" } # Commit within repository
|
228
|
-
subject { collector.get_blob(sha, file) }
|
229
|
-
|
230
|
-
context "with valid blob" do
|
231
|
-
let(:file) {{:file => "Gemfile.lock"}}
|
232
|
-
it { should be_a Grit::Blob }
|
233
|
-
its(:name) { should == File.basename(file[:file]) }
|
234
|
-
end
|
235
|
-
|
236
|
-
context "with deleted file" do
|
237
|
-
let(:file) {{:file => "spec/collector_spec.rb"}}
|
238
|
-
it { should be_a Grit::Blob }
|
239
|
-
its(:name) { should == File.basename(file[:file]) }
|
240
|
-
end
|
241
|
-
|
242
|
-
context "with invalid blob" do
|
243
|
-
let(:file) {{:file => "dir/nothing.rb"}}
|
244
|
-
it { should be_nil }
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
|
-
describe "#process_blob" do
|
249
|
-
let(:sha) {"695b487432e8a1ede765b4e3efda088ab87a77f8"} # Commit within repository
|
250
|
-
let(:blob) {collector.get_blob(sha, file)}
|
251
|
-
let(:data) {
|
252
|
-
data = Hash.new(0)
|
253
|
-
data[:files] = []
|
254
|
-
collector.process_blob(data, blob, file)
|
255
|
-
}
|
256
|
-
let(:data_file) {data_file = data[:files].first}
|
257
|
-
|
258
|
-
context "with status (delete) blob" do
|
259
|
-
let(:file) {{:file => "spec/collector_spec.rb",
|
260
|
-
:additions => 0,
|
261
|
-
:deletions => 6,
|
262
|
-
:status => "delete"}}
|
263
|
-
|
264
|
-
it {data[:additions].should == file[:additions]}
|
265
|
-
it {data[:deletions].should == file[:deletions]}
|
266
|
-
|
267
|
-
it {data_file[:name].should == file[:file]}
|
268
|
-
it {data_file[:additions].should == file[:additions]}
|
269
|
-
it {data_file[:deletions].should == file[:deletions]}
|
270
|
-
it {data_file[:status].should == file[:status]}
|
271
|
-
it {data_file[:binary].should == false}
|
272
|
-
it {data_file[:image].should == false}
|
273
|
-
it {data_file[:vendored].should == false}
|
274
|
-
it {data_file[:generated].should == false}
|
275
|
-
it {data_file[:language].should == "Ruby"}
|
276
|
-
end
|
277
|
-
|
278
|
-
context "with invalid language blob" do
|
279
|
-
let(:file) {{:file => "Gemfile.lock",
|
280
|
-
:additions => 33,
|
281
|
-
:deletions => 11,
|
282
|
-
:status => nil}}
|
283
|
-
|
284
|
-
it {data[:additions].should == file[:additions]}
|
285
|
-
it {data[:deletions].should == file[:deletions]}
|
286
|
-
|
287
|
-
it {data_file[:name].should == file[:file]}
|
288
|
-
it {data_file[:additions].should == file[:additions]}
|
289
|
-
it {data_file[:deletions].should == file[:deletions]}
|
290
|
-
it {data_file[:status].should.nil?}
|
291
|
-
it {data_file[:binary].should == false}
|
292
|
-
it {data_file[:image].should == false}
|
293
|
-
it {data_file[:vendored].should == false}
|
294
|
-
it {data_file[:generated].should == true}
|
295
|
-
it {data_file[:language].should == "Unknown"}
|
296
|
-
end
|
297
|
-
|
298
|
-
context "with valid language blob" do
|
299
|
-
let(:file) {{:file => "README.md",
|
300
|
-
:additions => 7,
|
301
|
-
:deletions => 3,
|
302
|
-
:status => nil}}
|
303
|
-
|
304
|
-
it {data[:additions].should == file[:additions]}
|
305
|
-
it {data[:deletions].should == file[:deletions]}
|
306
|
-
|
307
|
-
it {data_file[:name].should == file[:file]}
|
308
|
-
it {data_file[:additions].should == file[:additions]}
|
309
|
-
it {data_file[:deletions].should == file[:deletions]}
|
310
|
-
it {data_file[:status].should.nil?}
|
311
|
-
it {data_file[:binary].should == false}
|
312
|
-
it {data_file[:image].should == false}
|
313
|
-
it {data_file[:vendored].should == false}
|
314
|
-
it {data_file[:generated].should == false}
|
315
|
-
it {data_file[:language].should == "Markdown"}
|
120
|
+
let(:sha) {"invalid input"}
|
121
|
+
it { expect {data}.to raise_error(Rugged::InvalidError) }
|
316
122
|
end
|
317
123
|
end
|
318
124
|
|