git_statistics 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +7 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +15 -0
- data/Guardfile +7 -0
- data/README.md +7 -2
- data/git_statistics.gemspec +1 -0
- data/lib/git_statistics/collector.rb +130 -245
- data/lib/git_statistics/commits.rb +19 -24
- data/lib/git_statistics/initialize.rb +1 -0
- data/lib/git_statistics/results.rb +96 -0
- data/lib/git_statistics/utilities.rb +108 -0
- data/lib/git_statistics/version.rb +1 -1
- data/lib/git_statistics.rb +7 -4
- data/spec/collector_spec.rb +365 -0
- data/spec/commits_spec.rb +238 -0
- data/spec/fixtures/commit_buffer_changes.txt +9 -0
- data/spec/fixtures/commit_buffer_information.txt +1 -0
- data/spec/fixtures/commit_buffer_information_first.txt +1 -0
- data/spec/fixtures/commit_buffer_information_with_merge.txt +1 -0
- data/spec/fixtures/commit_buffer_whole.txt +6 -0
- data/spec/fixtures/git_many_branches.txt +2 -0
- data/spec/fixtures/git_zero_branches.txt +1 -0
- data/spec/fixtures/header_output.txt +4 -0
- data/spec/fixtures/language_data_output.txt +2 -0
- data/spec/fixtures/multiple_authors.json +1 -0
- data/spec/fixtures/single_author_pretty.json +79 -0
- data/spec/fixtures/summary_output.txt +17 -0
- data/spec/results_spec.rb +143 -0
- data/spec/utilities_spec.rb +169 -0
- metadata +54 -2
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
# CHANGELOG
|
2
|
+
|
3
|
+
* [0.3.0 - September 12, 2012](https://github.com/kevinjalbert/git_statistics/compare/v0.2.0...v0.3.0)
|
4
|
+
* [0.2.0 - September 6, 2012](https://github.com/kevinjalbert/git_statistics/compare/v0.1.2...v0.2.0)
|
5
|
+
* [0.1.2 - April 12, 2012](https://github.com/kevinjalbert/git_statistics/compare/v0.1.1...v0.1.2)
|
6
|
+
* [0.1.1 - April 12, 2012](https://github.com/kevinjalbert/git_statistics/compare/0e82e507e5b64a1140623c702015b97b1b3f7f81...v0.1.1)
|
data/Gemfile
CHANGED
@@ -4,9 +4,20 @@ gem "json"
|
|
4
4
|
gem "trollop"
|
5
5
|
gem "grit"
|
6
6
|
gem "github-linguist"
|
7
|
+
gem "os"
|
7
8
|
|
8
9
|
group :test do
|
9
10
|
gem "simplecov"
|
10
11
|
gem "rspec"
|
11
12
|
gem "rake"
|
12
13
|
end
|
14
|
+
|
15
|
+
group :development do
|
16
|
+
gem "guard"
|
17
|
+
gem "guard-rspec"
|
18
|
+
end
|
19
|
+
|
20
|
+
group :darwin do
|
21
|
+
gem "rb-fsevent"
|
22
|
+
gem "terminal-notifier-guard"
|
23
|
+
end
|
data/Gemfile.lock
CHANGED
@@ -15,13 +15,21 @@ GEM
|
|
15
15
|
diff-lcs (~> 1.1)
|
16
16
|
mime-types (~> 1.15)
|
17
17
|
posix-spawn (~> 0.3.6)
|
18
|
+
guard (1.3.2)
|
19
|
+
listen (>= 0.4.2)
|
20
|
+
thor (>= 0.14.6)
|
21
|
+
guard-rspec (1.2.1)
|
22
|
+
guard (>= 1.1)
|
18
23
|
json (1.7.5)
|
24
|
+
listen (0.5.0)
|
19
25
|
mime-types (1.19)
|
20
26
|
multi_json (1.3.6)
|
27
|
+
os (0.9.6)
|
21
28
|
posix-spawn (0.3.6)
|
22
29
|
pygments.rb (0.2.13)
|
23
30
|
rubypython (~> 0.5.3)
|
24
31
|
rake (0.9.2.2)
|
32
|
+
rb-fsevent (0.9.1)
|
25
33
|
rspec (2.11.0)
|
26
34
|
rspec-core (~> 2.11.0)
|
27
35
|
rspec-expectations (~> 2.11.0)
|
@@ -37,6 +45,8 @@ GEM
|
|
37
45
|
multi_json (~> 1.0)
|
38
46
|
simplecov-html (~> 0.5.3)
|
39
47
|
simplecov-html (0.5.3)
|
48
|
+
terminal-notifier-guard (1.5.3)
|
49
|
+
thor (0.16.0)
|
40
50
|
trollop (2.0)
|
41
51
|
|
42
52
|
PLATFORMS
|
@@ -45,8 +55,13 @@ PLATFORMS
|
|
45
55
|
DEPENDENCIES
|
46
56
|
github-linguist
|
47
57
|
grit
|
58
|
+
guard
|
59
|
+
guard-rspec
|
48
60
|
json
|
61
|
+
os
|
49
62
|
rake
|
63
|
+
rb-fsevent
|
50
64
|
rspec
|
51
65
|
simplecov
|
66
|
+
terminal-notifier-guard
|
52
67
|
trollop
|
data/Guardfile
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
guard :rspec, :version => 2, :all_on_start => false do
|
4
|
+
watch(%r{^spec/.+_spec\.rb$})
|
5
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
6
|
+
watch('spec/spec_helper.rb') { "spec" }
|
7
|
+
end
|
data/README.md
CHANGED
@@ -26,11 +26,16 @@ This gem will analyze every commit within a git repository using `git log` and [
|
|
26
26
|
* Total file renames
|
27
27
|
* Total file copies
|
28
28
|
|
29
|
-
This gem also uses [github/linguist](https://github.com/github/linguist) to determine the
|
29
|
+
This gem also uses [github/linguist](https://github.com/github/linguist) to determine the language of each individual file within commits. This augments the reported statistics by breaking down the author's statistics by languages.
|
30
30
|
|
31
31
|
This gem also has the ability to save the acquired data into a JSON file (in either a compressed or pretty format). If a saved file is present for the repository you can use the gem to load the data from the file, thus saving time for re-displaying the statistics using a different set of display flags (what statistic to sort on, number of authors to show, consider merges, etc...). In the event that a repository updates with new commits the gem allows you to update the saved file with the new commits.
|
32
32
|
|
33
|
-
|
33
|
+
# Example Output
|
34
|
+
The following is the output produced by *git_statistics* when used on the [pengwynn/octokit](https://github.com/pengwynn/octokit) (at commit [95a9de3](https://github.com/pengwynn/octokit/commit/95a9de325bee4ca03c9c1d61de2d643666c90037)) git repository. In this output we show the top three authors in rankings based on number of commits (merge commits are excluded from these results).
|
35
|
+
|
36
|
+
![screenshot](http://cloud.github.com/downloads/kevinjalbert/git_statistics/pengwynn_octokit_output.png)
|
37
|
+
|
38
|
+
# Contributing
|
34
39
|
|
35
40
|
1. Fork it
|
36
41
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
data/git_statistics.gemspec
CHANGED
@@ -6,16 +6,7 @@ module GitStatistics
|
|
6
6
|
def initialize(verbose)
|
7
7
|
@commits = Commits.new
|
8
8
|
@verbose = verbose
|
9
|
-
|
10
|
-
# Connect to git repository if it exists
|
11
|
-
directory = Pathname.new(Dir.pwd)
|
12
|
-
while @repo == nil && !directory.root? do
|
13
|
-
begin
|
14
|
-
@repo = Grit::Repo.new(directory)
|
15
|
-
rescue
|
16
|
-
directory = directory.parent
|
17
|
-
end
|
18
|
-
end
|
9
|
+
@repo = Utilities.get_repository
|
19
10
|
|
20
11
|
# Abort if no git repository is found
|
21
12
|
if @repo == nil
|
@@ -23,86 +14,134 @@ module GitStatistics
|
|
23
14
|
end
|
24
15
|
end
|
25
16
|
|
26
|
-
def collect(branch,
|
17
|
+
def collect(branch, time_since="", time_until="")
|
18
|
+
# Create pipe for git log to acquire branches
|
19
|
+
pipe = open("|git --no-pager branch --no-color")
|
20
|
+
|
27
21
|
# Collect branches to use for git log
|
28
|
-
branches = collect_branches
|
22
|
+
branches = collect_branches(pipe)
|
29
23
|
branches = ["", ""] if branch
|
30
24
|
|
31
25
|
# Create pipe for the git log to acquire commits
|
32
26
|
pipe = open("|git --no-pager log #{branches.join(' ')} --date=iso --reverse"\
|
33
27
|
" --no-color --find-copies-harder --numstat --encoding=utf-8 "\
|
34
|
-
"--summary #{
|
28
|
+
"--summary #{time_since} #{time_until} "\
|
29
|
+
"--format=\"%H,%an,%ae,%ad,%p\"")
|
35
30
|
|
36
31
|
# Use a buffer approach to queue up lines from the log for each commit
|
37
32
|
buffer = []
|
38
33
|
pipe.each do |line|
|
39
34
|
|
40
|
-
line = clean_string(line)
|
35
|
+
line = Utilities.clean_string(line)
|
41
36
|
|
42
37
|
# Extract the buffer (commit) when we match ','x5 in the log format (delimeter)
|
43
38
|
if line.split(',').size == 5
|
39
|
+
|
40
|
+
# Sometimes 'git log' doesn't populate the buffer, try fallback option if so
|
41
|
+
buffer = fall_back_collect_commit(line.split(',').first) if buffer.size == 1
|
42
|
+
|
44
43
|
extract_commit(buffer) if not buffer.empty?
|
45
44
|
buffer = []
|
46
45
|
end
|
47
46
|
|
48
|
-
buffer << line
|
47
|
+
buffer << line
|
49
48
|
end
|
50
49
|
|
51
50
|
# Extract the last commit
|
52
51
|
extract_commit(buffer) if not buffer.empty?
|
53
52
|
end
|
54
53
|
|
55
|
-
def
|
56
|
-
|
57
|
-
pipe
|
54
|
+
def fall_back_collect_commit(sha)
|
55
|
+
|
56
|
+
# Create pipe for the git log to acquire commits
|
57
|
+
pipe = open("|git --no-pager show #{sha} --date=iso --reverse"\
|
58
|
+
" --no-color --find-copies-harder --numstat --encoding=utf-8 "\
|
59
|
+
"--summary --format=\"%H,%an,%ae,%ad,%p\"")
|
60
|
+
|
61
|
+
buffer = []
|
62
|
+
pipe.each do |line|
|
63
|
+
buffer << Utilities.clean_string(line)
|
64
|
+
end
|
58
65
|
|
59
|
-
#
|
66
|
+
# Check that the buffer has valid information (i.e., sha was valid)
|
67
|
+
if buffer.empty?
|
68
|
+
return nil
|
69
|
+
elsif buffer.first.split(',').first == sha
|
70
|
+
return buffer
|
71
|
+
else
|
72
|
+
return nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def collect_branches(pipe)
|
77
|
+
# Acquire all available branches from repository
|
60
78
|
branches = []
|
61
79
|
pipe.each do |line|
|
62
80
|
|
63
81
|
# Remove the '*' leading the current branch
|
64
82
|
line = line[1..-1] if line[0] == '*'
|
65
|
-
branches << clean_string(line)
|
83
|
+
branches << Utilities.clean_string(line)
|
66
84
|
end
|
67
85
|
|
68
86
|
return branches
|
69
87
|
end
|
70
88
|
|
71
|
-
def
|
72
|
-
#
|
73
|
-
commit_info =
|
74
|
-
sha = commit_info[0]
|
89
|
+
def acquire_commit_data(line)
|
90
|
+
# Split up formated line
|
91
|
+
commit_info = line.split(',')
|
75
92
|
|
76
93
|
# Initialize commit data
|
77
|
-
data = (@commits[
|
94
|
+
data = (@commits[commit_info[0]] ||= Hash.new(0))
|
78
95
|
data[:author] = commit_info[1]
|
79
96
|
data[:author_email] = commit_info[2]
|
80
97
|
data[:time] = commit_info[3]
|
81
98
|
data[:files] = []
|
82
99
|
|
83
|
-
# Flag commit as merge if
|
100
|
+
# Flag commit as merge if necessary (determined if two parents)
|
84
101
|
if commit_info[4] == nil or commit_info[4].split(' ').size == 1
|
85
102
|
data[:merge] = false
|
86
103
|
else
|
87
104
|
data[:merge] = true
|
88
105
|
end
|
89
106
|
|
90
|
-
|
107
|
+
return {:sha => commit_info[0], :data => data}
|
108
|
+
end
|
109
|
+
|
110
|
+
def extract_commit(buffer)
|
111
|
+
# Acquire general commit information
|
112
|
+
commit_data = acquire_commit_data(buffer[0])
|
113
|
+
|
114
|
+
puts "Extracting #{commit_data[:sha]}" if @verbose
|
115
|
+
|
116
|
+
# Abort if the commit sha extracted form the buffer is invalid
|
117
|
+
if commit_data[:sha].scan(/[\d|a-f]{40}/)[0] == nil
|
118
|
+
puts "Invalid buffer containing commit information"
|
119
|
+
return
|
120
|
+
end
|
91
121
|
|
92
122
|
# Identify all changed files for this commit
|
93
|
-
files = identify_changed_files(buffer)
|
123
|
+
files = identify_changed_files(buffer[2..-1])
|
124
|
+
|
125
|
+
# No files were changed in this commit, abort commit
|
126
|
+
if files == nil
|
127
|
+
puts "No files were changed"
|
128
|
+
return
|
129
|
+
end
|
94
130
|
|
95
131
|
# Acquire blob for each changed file and process it
|
96
132
|
files.each do |file|
|
97
|
-
blob = get_blob(sha, file)
|
133
|
+
blob = get_blob(commit_data[:sha], file)
|
98
134
|
|
99
|
-
# Only process blobs,
|
135
|
+
# Only process blobs, or log the submodules and problematic files
|
100
136
|
if blob.instance_of?(Grit::Blob)
|
101
|
-
process_blob(data, blob, file)
|
137
|
+
process_blob(commit_data[:data], blob, file)
|
138
|
+
elsif blob.instance_of?(Grit::Submodule)
|
139
|
+
puts "Ignoring submodule #{blob.name}"
|
102
140
|
else
|
103
141
|
puts "Problem processing file #{file[:file]}"
|
104
142
|
end
|
105
143
|
end
|
144
|
+
return commit_data[:data]
|
106
145
|
end
|
107
146
|
|
108
147
|
def get_blob(sha, file)
|
@@ -110,7 +149,7 @@ module GitStatistics
|
|
110
149
|
file = file[:file].split(File::Separator)
|
111
150
|
|
112
151
|
# Acquire blob of the file for this specific commit
|
113
|
-
blob = find_blob_in_tree(
|
152
|
+
blob = Utilities.find_blob_in_tree(@repo.tree(sha), file)
|
114
153
|
|
115
154
|
# If we cannot find blob in current commit (deleted file), check previous commit
|
116
155
|
if blob == nil || blob.instance_of?(Grit::Tree)
|
@@ -118,94 +157,60 @@ module GitStatistics
|
|
118
157
|
return nil if prev_commit == nil
|
119
158
|
|
120
159
|
prev_tree = @repo.tree(prev_commit.id)
|
121
|
-
blob = find_blob_in_tree(
|
160
|
+
blob = Utilities.find_blob_in_tree(prev_tree, file)
|
122
161
|
end
|
123
162
|
return blob
|
124
163
|
end
|
125
164
|
|
126
165
|
def identify_changed_files(buffer)
|
127
|
-
|
128
|
-
changed_files = []
|
129
|
-
if buffer.size > 2
|
130
|
-
|
131
|
-
# For each modification extract the details
|
132
|
-
buffer[2..-1].each do |line|
|
166
|
+
return buffer if buffer == nil
|
133
167
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
changed_files << data
|
138
|
-
next # This line is processed, skip to next
|
139
|
-
end
|
168
|
+
# For each modification extract the details
|
169
|
+
changed_files = []
|
170
|
+
buffer.each do |line|
|
140
171
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
if file[:file] == data[:file]
|
148
|
-
file[:status] = data[:status]
|
149
|
-
augmented = true
|
150
|
-
break
|
151
|
-
end
|
152
|
-
end
|
153
|
-
changed_files << data if !augmented
|
154
|
-
next # This line is processed, skip to next
|
155
|
-
end
|
172
|
+
# Extract changed file information if it exists
|
173
|
+
data = extract_change_file(line)
|
174
|
+
if data != nil
|
175
|
+
changed_files << data
|
176
|
+
next # This line is processed, skip to next
|
177
|
+
end
|
156
178
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
augmented = true
|
168
|
-
break
|
169
|
-
end
|
179
|
+
# Extract details of create/delete files if it exists
|
180
|
+
data = extract_create_delete_file(line)
|
181
|
+
if data != nil
|
182
|
+
augmented = false
|
183
|
+
# Augment changed file with create/delete information if possible
|
184
|
+
changed_files.each do |file|
|
185
|
+
if file[:file] == data[:file]
|
186
|
+
file[:status] = data[:status]
|
187
|
+
augmented = true
|
188
|
+
break
|
170
189
|
end
|
171
|
-
changed_files << data if !augmented
|
172
|
-
next # This line is processed, skip to next
|
173
190
|
end
|
191
|
+
changed_files << data if !augmented
|
192
|
+
next # This line is processed, skip to next
|
174
193
|
end
|
175
|
-
end
|
176
|
-
return changed_files
|
177
|
-
end
|
178
|
-
|
179
|
-
def find_blob_in_tree(sha, tree, file)
|
180
|
-
# Check If cannot find tree in commit or if we found a submodule as the changed file
|
181
|
-
if tree == nil
|
182
|
-
return nil
|
183
|
-
elsif tree.instance_of?(Grit::Submodule)
|
184
|
-
return tree
|
185
|
-
end
|
186
|
-
|
187
|
-
# If the blob is within the current directory (tree)
|
188
|
-
if file.size == 1
|
189
|
-
blob = tree / file.first
|
190
|
-
|
191
|
-
# Check if blob is nil (could not find changed file in tree)
|
192
|
-
if blob == nil
|
193
194
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
195
|
+
# Extract details of rename/copy files if it exists
|
196
|
+
data = extract_rename_copy_file(line)
|
197
|
+
if data != nil
|
198
|
+
augmented = false
|
199
|
+
# Augment changed file with rename/copy information if possible
|
200
|
+
changed_files.each do |file|
|
201
|
+
if file[:file] == data[:new_file]
|
202
|
+
file[:status] = data[:status]
|
203
|
+
file[:old_file] = data[:old_file]
|
204
|
+
file[:similar] = data[:similar]
|
205
|
+
augmented = true
|
206
|
+
break
|
198
207
|
end
|
199
208
|
end
|
200
|
-
|
201
|
-
#
|
202
|
-
return find_blob_in_tree(sha, blob, file)
|
209
|
+
changed_files << data if !augmented
|
210
|
+
next # This line is processed, skip to next
|
203
211
|
end
|
204
|
-
return blob
|
205
|
-
else
|
206
|
-
# Explore deeper in the tree to find the blob of the changed file
|
207
|
-
return find_blob_in_tree(sha, tree / file.first, file[1..-1])
|
208
212
|
end
|
213
|
+
return changed_files
|
209
214
|
end
|
210
215
|
|
211
216
|
def process_blob(data, blob, file)
|
@@ -221,36 +226,21 @@ module GitStatistics
|
|
221
226
|
data[:additions] += file[:additions]
|
222
227
|
data[:deletions] += file[:deletions]
|
223
228
|
|
224
|
-
#
|
225
|
-
|
226
|
-
|
229
|
+
# Acquire specifics on blob
|
230
|
+
file_hash[:binary] = blob.binary?
|
231
|
+
file_hash[:image] = blob.image?
|
232
|
+
file_hash[:vendored] = blob.vendored?
|
233
|
+
file_hash[:generated] = blob.generated?
|
234
|
+
|
235
|
+
# Identify the language of the blob if possible
|
236
|
+
if blob.language == nil
|
237
|
+
file_hash[:language] = "Unknown"
|
227
238
|
else
|
228
|
-
file_hash[:
|
229
|
-
file_hash[:image] = blob.image?
|
230
|
-
file_hash[:vendored] = blob.vendored?
|
231
|
-
file_hash[:generated] = blob.generated?
|
232
|
-
|
233
|
-
# Identify the language of the blob if possible
|
234
|
-
if blob.language == nil
|
235
|
-
file_hash[:language] = "Unknown"
|
236
|
-
else
|
237
|
-
file_hash[:language] = blob.language.name
|
238
|
-
end
|
239
|
+
file_hash[:language] = blob.language.name
|
239
240
|
end
|
240
241
|
data[:files] << file_hash
|
241
|
-
end
|
242
242
|
|
243
|
-
|
244
|
-
#if file_name.include?("foo")
|
245
|
-
#blob = @repo.tree("1ec5c2674fd792e8f9ddbff5afcacc3e1f7c506d") / "actionpack" / "test" / "fixtures" / "public" / "foo"
|
246
|
-
#ap "=-=-=-=-=-=-="
|
247
|
-
#ap file_name
|
248
|
-
#ap "--------------------"
|
249
|
-
#ap blob.contents[2].name
|
250
|
-
#ap "=-=-=-=-=-=-="
|
251
|
-
#end
|
252
|
-
# Clean up a string and force utf-8 encoding
|
253
|
-
return file_name.strip.gsub('"', '').gsub("\\\\", "\\").force_encoding("utf-8")
|
243
|
+
return data
|
254
244
|
end
|
255
245
|
|
256
246
|
def extract_change_file(line)
|
@@ -258,11 +248,11 @@ module GitStatistics
|
|
258
248
|
changes = line.scan(/^([-|\d]+)\s+([-|\d]+)\s+(.+)\s+=>\s+(.+)/)[0]
|
259
249
|
if changes != nil and changes.size == 4
|
260
250
|
# Split up the file into the old and new file
|
261
|
-
split_file = split_old_new_file(changes[2], changes[3])
|
251
|
+
split_file = Utilities.split_old_new_file(changes[2], changes[3])
|
262
252
|
return {:additions => changes[0].to_i,
|
263
253
|
:deletions => changes[1].to_i,
|
264
|
-
:file => clean_string(split_file[:new_file]),
|
265
|
-
:old_file => clean_string(split_file[:old_file])}
|
254
|
+
:file => Utilities.clean_string(split_file[:new_file]),
|
255
|
+
:old_file => Utilities.clean_string(split_file[:old_file])}
|
266
256
|
end
|
267
257
|
|
268
258
|
# Use regex to detect a changed file | 1 2 /path/test/file.txt
|
@@ -270,7 +260,7 @@ module GitStatistics
|
|
270
260
|
if changes != nil and changes.size == 3
|
271
261
|
return {:additions => changes[0].to_i,
|
272
262
|
:deletions => changes[1].to_i,
|
273
|
-
:file => clean_string(changes[2])}
|
263
|
+
:file => Utilities.clean_string(changes[2])}
|
274
264
|
end
|
275
265
|
return nil
|
276
266
|
end
|
@@ -279,8 +269,8 @@ module GitStatistics
|
|
279
269
|
# Use regex to detect a create/delete file | create mode 100644 /path/test/file.txt
|
280
270
|
changes = line.scan(/^(create|delete) mode \d+ ([^\\\n]*)/)[0]
|
281
271
|
if changes != nil and changes.size == 2
|
282
|
-
return {:status => clean_string(changes[0]),
|
283
|
-
:file => clean_string(changes[1])}
|
272
|
+
return {:status => Utilities.clean_string(changes[0]),
|
273
|
+
:file => Utilities.clean_string(changes[1])}
|
284
274
|
end
|
285
275
|
return nil
|
286
276
|
end
|
@@ -290,118 +280,13 @@ module GitStatistics
|
|
290
280
|
changes = line.scan(/^(rename|copy)\s+(.+)\s+=>\s+(.+)\s+\((\d+)/)[0]
|
291
281
|
if changes != nil and changes.size == 4
|
292
282
|
# Split up the file into the old and new file
|
293
|
-
split_file = split_old_new_file(changes[1], changes[2])
|
294
|
-
return {:status => clean_string(changes[0]),
|
295
|
-
:old_file => clean_string(split_file[:old_file]),
|
296
|
-
:new_file => clean_string(split_file[:new_file]),
|
283
|
+
split_file = Utilities.split_old_new_file(changes[1], changes[2])
|
284
|
+
return {:status => Utilities.clean_string(changes[0]),
|
285
|
+
:old_file => Utilities.clean_string(split_file[:old_file]),
|
286
|
+
:new_file => Utilities.clean_string(split_file[:new_file]),
|
297
287
|
:similar => changes[3].to_i}
|
298
288
|
end
|
299
289
|
return nil
|
300
290
|
end
|
301
|
-
|
302
|
-
def split_old_new_file(old, new)
|
303
|
-
# Split the old and new chunks up (separted by the =>)
|
304
|
-
split_old = old.split('{')
|
305
|
-
split_new = new.split('}')
|
306
|
-
|
307
|
-
# Handle recombine the file splits into their whole paths)
|
308
|
-
if split_old.size == 1 && split_new.size == 1
|
309
|
-
old_file = split_old[0]
|
310
|
-
new_file = split_new[0]
|
311
|
-
elsif split_new.size == 1
|
312
|
-
old_file = split_old[0] + split_old[1] + split_new[0]
|
313
|
-
new_file = split_old[0] + split_new[0]
|
314
|
-
elsif split_old.size == 1
|
315
|
-
old_file = split_old[0] + split_new[1]
|
316
|
-
new_file = split_old[0] + split_new[0] + split_new[1]
|
317
|
-
else
|
318
|
-
old_file = split_old[0] + split_old[1] + split_new[1]
|
319
|
-
new_file = split_old[0] + split_new[0] + split_new[1]
|
320
|
-
end
|
321
|
-
|
322
|
-
# Return files, yet remove the '//' if present from combining splits
|
323
|
-
return {:old_file => old_file.gsub('//', '/'),
|
324
|
-
:new_file => new_file.gsub('//', '/')}
|
325
|
-
end
|
326
|
-
|
327
|
-
def print_summary(sort_type, email, n=0)
|
328
|
-
# Default to a 0 if given a negative number to display
|
329
|
-
n = 0 if n < 0
|
330
|
-
|
331
|
-
# Acquire data based on sorty type and top # to show
|
332
|
-
data = @commits.author_top_n_type(sort_type, n)
|
333
|
-
if data == nil
|
334
|
-
raise "Parameter for --sort is not valid"
|
335
|
-
end
|
336
|
-
|
337
|
-
# Acquire formatting pattern for output
|
338
|
-
author_length = find_longest_author(data)
|
339
|
-
language_length = find_longest_language(data)
|
340
|
-
pattern = "%-#{author_length}s | %-#{language_length}s | %7s | %9s | %9s | %7s | %7s | %7s | %6s | %6s |"
|
341
|
-
|
342
|
-
# Print query/header information
|
343
|
-
print_header(pattern, sort_type, n, author_length, language_length)
|
344
|
-
|
345
|
-
# Print per author information
|
346
|
-
data.each do |key,value|
|
347
|
-
puts pattern % [key, "", value[:commits], value[:additions],
|
348
|
-
value[:deletions], value[:create], value[:delete],
|
349
|
-
value[:rename], value[:copy], value[:merges]]
|
350
|
-
print_language_data(pattern, value)
|
351
|
-
end
|
352
|
-
|
353
|
-
# Reprint query/header for repository information
|
354
|
-
print_header(pattern, sort_type, n, author_length, language_length)
|
355
|
-
data = @commits.totals
|
356
|
-
puts pattern % ["Repository Totals", "", data[:commits],
|
357
|
-
data[:additions], data[:deletions], data[:create],
|
358
|
-
data[:delete], data[:rename], data[:copy], data[:merges]]
|
359
|
-
print_language_data(pattern, data)
|
360
|
-
end
|
361
|
-
|
362
|
-
def print_language_data(pattern, data)
|
363
|
-
# Print information of each language for the data
|
364
|
-
data[:languages].each do |key,value|
|
365
|
-
puts pattern % ["", key, "", value[:additions], value[:deletions],
|
366
|
-
value[:create], value[:delete], value[:rename],
|
367
|
-
value[:copy], value[:merges]]
|
368
|
-
end
|
369
|
-
end
|
370
|
-
|
371
|
-
def print_header(pattern, sort_type, n, author_length, language_length)
|
372
|
-
total_authors = @commits.author_list.length
|
373
|
-
|
374
|
-
# Print summary information of displayed results
|
375
|
-
if n > 0 and n < total_authors
|
376
|
-
puts "\nTop #{n} authors(#{total_authors}) sorted by #{sort_type.to_s}\n"
|
377
|
-
else
|
378
|
-
puts "\nAll authors(#{total_authors}) sorted by #{sort_type.to_s}\n"
|
379
|
-
end
|
380
|
-
|
381
|
-
# Print column headers
|
382
|
-
puts "-"*87 + "-"*author_length + "-"*language_length
|
383
|
-
puts pattern % ['Name/Email', 'Language', 'Commits', 'Additions', 'Deletions', 'Creates', 'Deletes', 'Renames', 'Copies', 'Merges']
|
384
|
-
puts "-"*87 + "-"*author_length + "-"*language_length
|
385
|
-
end
|
386
|
-
|
387
|
-
def find_longest_author(data)
|
388
|
-
# Find the longest author name/email (for string formatting)
|
389
|
-
total_authors = @commits.author_list.length
|
390
|
-
author_length = 17
|
391
|
-
data.each do |key,value|
|
392
|
-
author_length = key.length if key.length > author_length
|
393
|
-
end
|
394
|
-
return author_length
|
395
|
-
end
|
396
|
-
|
397
|
-
def find_longest_language(data)
|
398
|
-
# Find the longest language name (for string formatting)
|
399
|
-
total_language = @commits.language_list.length
|
400
|
-
language_length = 9
|
401
|
-
@commits.language_list.each do |key,value|
|
402
|
-
language_length = key.length if key.length > language_length
|
403
|
-
end
|
404
|
-
return language_length
|
405
|
-
end
|
406
291
|
end
|
407
292
|
end
|