git_statistics 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+
6
+ notifications:
7
+ email: false
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem "json"
4
+ gem "trollop"
5
+
6
+ group :test do
7
+ gem "simplecov"
8
+ gem "rspec"
9
+ gem "rake"
10
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,30 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.3)
5
+ json (1.6.6)
6
+ multi_json (1.2.0)
7
+ rake (0.9.2.2)
8
+ rspec (2.9.0)
9
+ rspec-core (~> 2.9.0)
10
+ rspec-expectations (~> 2.9.0)
11
+ rspec-mocks (~> 2.9.0)
12
+ rspec-core (2.9.0)
13
+ rspec-expectations (2.9.1)
14
+ diff-lcs (~> 1.1.3)
15
+ rspec-mocks (2.9.0)
16
+ simplecov (0.6.1)
17
+ multi_json (~> 1.0)
18
+ simplecov-html (~> 0.5.3)
19
+ simplecov-html (0.5.3)
20
+ trollop (1.16.2)
21
+
22
+ PLATFORMS
23
+ ruby
24
+
25
+ DEPENDENCIES
26
+ json
27
+ rake
28
+ rspec
29
+ simplecov
30
+ trollop
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2012 Kevin Jalbert
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,34 @@
1
+ [![Build Status](https://secure.travis-ci.org/kevinjalbert/git_statistics.png?branch=master)](http://travis-ci.org/kevinjalbert/git_statistics)
2
+
3
+ # Instructions
4
+
5
+ ### Using the gem
6
+ 1. Acquire gem (`gem install git_statistics`)
7
+ 2. Run `git_statistics` in any directory that is a git repository (use -h for options)
8
+
9
+ ### Working with source
10
+ 1. Clone the repository
11
+ 2. Install dependencies (`bundle install`)
12
+ 3. Run tests `bundle exec rake`
13
+ 4. Build and install local gem `bundle exec rake install`
14
+
15
+ # Statistics
16
+
17
+ The following statistics are collected (organized by author name or author email):
18
+
19
+ * Total number of commits
20
+ * Total number of merge commits
21
+ * Total source line insertions
22
+ * Total source line deletions
23
+ * Total file creates
24
+ * Total file deletes
25
+ * Total file renames
26
+ * Total file copies
27
+
28
+ ## Contributing
29
+
30
+ 1. Fork it
31
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
32
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
33
+ 4. Push to the branch (`git push origin my-new-feature`)
34
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require 'rake/clean'
4
+ require 'rspec/core/rake_task'
5
+ require "bundler/gem_tasks"
6
+
7
+ CLOBBER.include('coverage')
8
+
9
+ desc "Run all specs"
10
+ RSpec::Core::RakeTask.new do |t|
11
+ t.pattern = "./spec/**/*spec.rb"
12
+ end
13
+
14
+ task :default => :spec
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'git_statistics'
4
+
5
+ GitStatistics::GitStatistics.new(ARGV).execute
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/git_statistics/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.homepage = 'https://github.com/kevinjalbert/git_statistics'
6
+ gem.authors = ["Kevin Jalbert"]
7
+ gem.email = ["kevin.j.jalbert@gmail.com"]
8
+ gem.name = 'git_statistics'
9
+ gem.version = GitStatistics::VERSION
10
+ gem.summary = "Gem that provides the ability to gather detailed git statistics"
11
+ gem.description = "git_statistics is a gem that provides detailed git statistics"
12
+ gem.require_paths = ["lib"]
13
+ gem.files = `git ls-files`.split($\)
14
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
+ gem.executables << 'git_statistics'
17
+ gem.required_ruby_version = '>= 1.9.1'
18
+ gem.add_dependency('json')
19
+ gem.add_dependency('trollop')
20
+ end
@@ -0,0 +1,155 @@
1
+ module GitStatistics
2
+ class Collector
3
+
4
+ attr_accessor :commits
5
+
6
+ def initialize
7
+ @commits = Commits.new
8
+ end
9
+
10
+ def collect(branch, since="")
11
+
12
+ # Collect branches to use for git log
13
+ branches = collect_branches
14
+ branches = ["", ""] if branch
15
+
16
+ pipe = open("|git --no-pager log #{branches.join(' ')} --date=iso --reverse"\
17
+ " --no-color --numstat --summary #{since}"\
18
+ " --format=\"%H,%an,%ae,%ad,%p\"")
19
+
20
+ buffer = []
21
+ pipe.each do |line|
22
+
23
+ line = line.force_encoding("ISO-8859-1").encode("UTF-8")
24
+
25
+ if line.split(',').size == 5 # Matches the number of ',' in the format
26
+ extract_buffer(buffer) if not buffer.empty?
27
+ buffer = []
28
+ end
29
+
30
+ buffer << line.strip
31
+ end
32
+
33
+ # Extract the last commit
34
+ extract_buffer(buffer) if not buffer.empty?
35
+ end
36
+
37
+ def collect_branches
38
+
39
+ pipe = open("|git --no-pager branch --no-color")
40
+
41
+ branches = []
42
+ pipe.each do |line|
43
+
44
+ # Remove the '* ' leading the current branch
45
+ line = line[1..-1] if line[0] == '*'
46
+ branches << line.strip
47
+ end
48
+
49
+ return branches
50
+ end
51
+
52
+ def extract_buffer(buffer)
53
+
54
+ commit_info = buffer[0].split(',')
55
+
56
+ commit = (@commits[ commit_info[0] ] ||= Hash.new)
57
+ commit[:author] = commit_info[1]
58
+ commit[:author_email] = commit_info[2]
59
+ commit[:time] = commit_info[3]
60
+ commit[:insertions] = 0
61
+ commit[:deletions] = 0
62
+ commit[:creates] = 0
63
+ commit[:deletes] = 0
64
+ commit[:renames] = 0
65
+ commit[:copies] = 0
66
+
67
+ if commit_info[4] == nil or commit_info[4].split(' ').size == 1
68
+ commit[:merge] = false
69
+ else
70
+ commit[:merge] = true
71
+ end
72
+
73
+ # Only extract diff details if they exist
74
+ if buffer.size > 1
75
+
76
+ buffer[2..-1].each do |line|
77
+
78
+ next if extract_changes(commit, line)
79
+ next if extract_create_delete_file(commit, line)
80
+ next if extract_rename_copy_file(commit, line)
81
+
82
+ end
83
+ end
84
+ end
85
+
86
+ def extract_changes(commit, line)
87
+ changes = line.scan( /(\d+)\s(\d+)\s(.*)/ )[0]
88
+
89
+ if changes != nil and changes.size == 3
90
+ commit[:insertions] += changes[0].to_i
91
+ commit[:deletions] += changes[1].to_i
92
+ return true
93
+ end
94
+ end
95
+
96
+ def extract_create_delete_file(commit, line)
97
+ changes = line.scan(/(create|delete) mode \d+ ([^\\\n]*)/)[0]
98
+
99
+ if changes != nil and changes.size == 2
100
+ commit[:creates] += 1 if changes[0] == "create"
101
+ commit[:deletes] += 1 if changes[0] == "delete"
102
+ return true
103
+ end
104
+ end
105
+
106
+ def extract_rename_copy_file(commit, line)
107
+ changes = line.scan(/(rename|copy)([^(]*)/)[0]
108
+
109
+ if changes != nil and changes.size == 2
110
+ commit[:renames] += 1 if changes[0] == "rename"
111
+ commit[:copies] += 1 if changes[0] == "copy"
112
+ end
113
+ return true
114
+ end
115
+
116
+ def print_summary(sort_type, email, n=0)
117
+ n = 0 if n < 0
118
+
119
+ data = @commits.author_top_n_type(email, sort_type, n)
120
+
121
+ if data == nil
122
+ puts "ERROR: Parameter for --sort is not valid"
123
+ return
124
+ end
125
+
126
+ # Find the longest name/email (used for string formatting)
127
+ total_authors = @commits.author_list.length
128
+ author_length = 17
129
+ data.each do |key,value|
130
+ author_length = key.length if key.length > author_length
131
+ end
132
+
133
+ # Print header information
134
+ if n > 0 and n < total_authors
135
+ puts "Top #{n} authors(#{total_authors}) sorted by #{sort_type.to_s}\n\n"
136
+ else
137
+ puts "All authors(#{total_authors}) sorted by #{sort_type.to_s}\n\n"
138
+ end
139
+
140
+ pattern = "%-#{author_length}s|%7s|%10s|%9s|%7s|%7s|%7s|%6s|%6s|"
141
+ puts pattern % ['Name/email', 'commits', 'insertions', 'deletions', 'creates', 'deletes', 'renames', 'copies', 'merges']
142
+ puts "-"*68 + "-"*author_length
143
+
144
+ data.each do |key,value|
145
+ puts pattern % [key, value[:commits], value[:insertions], value[:deletions],
146
+ value[:creates], value[:deletes], value[:renames], value[:copies], value[:merges]]
147
+ end
148
+
149
+ puts "-"*68 + "-"*author_length
150
+ puts pattern % ["Repository Totals", @commits.totals[:commits],
151
+ @commits.totals[:insertions], @commits.totals[:deletions], @commits.totals[:creates],
152
+ @commits.totals[:deletes], @commits.totals[:renames], @commits.totals[:copies], @commits.totals[:merges]]
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,129 @@
1
+ module GitStatistics
2
+ class Commits < Hash
3
+
4
+ attr_accessor :author_list, :data_authors, :data_authors_email, :totals
5
+
6
+ def initialize
7
+ super
8
+ end
9
+
10
+ def authors
11
+ author_list = []
12
+ self.each do |key,value|
13
+ if not author_list.include?(value[:author])
14
+ author_list << value[:author]
15
+ end
16
+ end
17
+ return author_list
18
+ end
19
+
20
+ def authors_email
21
+ author_list = []
22
+ self.each do |key,value|
23
+ if not author_list.include?(value[:author_email])
24
+ author_list << value[:author_email]
25
+ end
26
+ end
27
+ return author_list
28
+ end
29
+
30
+ def authors_statistics(email, merge)
31
+
32
+ # Identify authors and author type
33
+ if email
34
+ @author_list = authors_email
35
+ type = :author_email
36
+ else
37
+ @author_list = authors
38
+ type = :author
39
+ end
40
+
41
+ # Initialize the stats hash
42
+ stats = Hash.new
43
+ @author_list.each do |author|
44
+ stats[author] = Hash.new
45
+ stats[author][:commits] = 0
46
+ stats[author][:insertions] = 0
47
+ stats[author][:deletions] = 0
48
+ stats[author][:creates] = 0
49
+ stats[author][:deletes] = 0
50
+ stats[author][:renames] = 0
51
+ stats[author][:copies] = 0
52
+ stats[author][:merges] = 0
53
+ end
54
+
55
+ # Collect the stats for each author
56
+ self.each do |key,value|
57
+ if not merge and value[:merge]
58
+ next
59
+ else
60
+ stats[value[type]][:merges] += 1 if value[:merge]
61
+ stats[value[type]][:commits] += 1
62
+ stats[value[type]][:insertions] += value[:insertions]
63
+ stats[value[type]][:deletions] += value[:deletions]
64
+ stats[value[type]][:creates] += value[:creates]
65
+ stats[value[type]][:deletes] += value[:deletes]
66
+ stats[value[type]][:renames] += value[:renames]
67
+ stats[value[type]][:copies] += value[:copies]
68
+ end
69
+ end
70
+ return stats
71
+ end
72
+
73
+ def author_top_n_type(email, type, n=0)
74
+ n = 0 if n < 0
75
+
76
+ if email
77
+ data = @data_authors_email
78
+ else
79
+ data = @data_authors
80
+ end
81
+
82
+ return nil if data == nil || !data.first[1].has_key?(type)
83
+ return data.sorted_hash {|a,b| b[1][type.to_sym] <=> a[1][type]}.to_a[0..n-1]
84
+ end
85
+
86
+ def calculate_statistics(email, merge)
87
+
88
+ # Calculate author statistics
89
+ @data_authors_email = authors_statistics(true, merge) if email
90
+ @data_authors = authors_statistics(false, merge) if not email
91
+
92
+ # Calculate totals
93
+ @totals = Hash.new(0)
94
+ self.each do |key,value|
95
+ if not merge and value[:merge]
96
+ next
97
+ else
98
+ @totals[:merges] += 1 if value[:merge]
99
+ @totals[:commits] += 1
100
+ @totals[:insertions] += value[:insertions]
101
+ @totals[:deletions] += value[:deletions]
102
+ @totals[:creates] += value[:creates]
103
+ @totals[:deletes] += value[:deletes]
104
+ @totals[:renames] += value[:renames]
105
+ @totals[:copies] += value[:copies]
106
+ end
107
+ end
108
+ end
109
+
110
+ def load(file)
111
+ self.merge!(JSON.parse(File.read(file), :symbolize_names => true))
112
+ end
113
+
114
+ def save(file, pretty)
115
+ if pretty
116
+ File.open(file, 'w') {|file| file.write(JSON.pretty_generate(self))}
117
+ else
118
+ File.open(file, 'w') {|file| file.write(self.to_json)}
119
+ end
120
+ end
121
+ end
122
+
123
+ class Hash < Hash
124
+
125
+ def sorted_hash(&block)
126
+ self.class[sort(&block)]
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,3 @@
1
+ require 'json'
2
+ require 'trollop'
3
+ Dir.glob(File.dirname(__FILE__) + '/*.rb') {|file| require file}
@@ -0,0 +1,3 @@
1
+ module GitStatistics
2
+ VERSION = "0.1.2"
3
+ end
@@ -0,0 +1,44 @@
1
+ require 'git_statistics/initialize'
2
+
3
+ module GitStatistics
4
+ class GitStatistics
5
+ def initialize(args=nil)
6
+ @opts = Trollop::options do
7
+ opt :email, "Use author's email instead of name", :default => false
8
+ opt :merges, "Factor in merges when calculating statistics", :default => false
9
+ opt :save, "Save the commits as commits.json", :default => false
10
+ opt :pretty, "Pretty print saved commits (larger file size)", :default => false
11
+ opt :load, "Load commits.json instead of re-collecting data", :default => false
12
+ opt :update, "Update commits.json with new data (same as save and load together)", :default => false
13
+ opt :sort, "Sort authors by {commits, insertions, deletions, creates, deletes, renames, copies, merges}", :default => "commits"
14
+ opt :top, "Show the top N authors in results", :default => 0
15
+ opt :branch, "Use current branch for statistics (otherwise all branches)", :default => false
16
+ end
17
+ end
18
+
19
+ def execute
20
+ collector = Collector.new
21
+
22
+ # Collect commit data
23
+ if @opts[:load] || @opts[:update]
24
+ collector.commits.load("commits.json")
25
+ else
26
+ collector.collect(@opts[:branch])
27
+ end
28
+
29
+ # Collect incremental recent data
30
+ if @opts[:update]
31
+ collector.collect(@opts[:branch], "--since=\"`date -r commits.json \"+%F %T\"`\"")
32
+ end
33
+
34
+ # Save data
35
+ if @opts[:save] || @opts[:update]
36
+ collector.commits.save("commits.json", @opts[:pretty])
37
+ end
38
+
39
+ collector.commits.calculate_statistics(@opts[:email], @opts[:merges])
40
+
41
+ collector.print_summary(@opts[:sort].to_sym, @opts[:email], @opts[:top])
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,6 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ include GitStatistics
3
+
4
+ describe Collector do
5
+
6
+ end
@@ -0,0 +1,381 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ include GitStatistics
3
+
4
+ describe Commits do
5
+ describe "#authors" do
6
+ context "with one author" do
7
+ commits = Commits.new
8
+ commits.load(fixture("single_author.json"))
9
+ author_list = commits.authors
10
+
11
+
12
+ it { author_list.size.should be(1) }
13
+ it { author_list[0].should == "Kevin Jalbert" }
14
+ end
15
+
16
+ context "with many authors" do
17
+ commits = Commits.new
18
+ commits.load(fixture("many_authors.json"))
19
+ author_list = commits.authors
20
+
21
+ it { author_list.size.should be(3) }
22
+ it { author_list[0].should == "Bart Simpson" }
23
+ it { author_list[1].should == "Kevin Jalbert" }
24
+ it { author_list[2].should == "Maggie Simpson" }
25
+ end
26
+
27
+ context "with no authors" do
28
+ commits = Commits.new
29
+ author_list = commits.authors
30
+
31
+ it { author_list.size.should be(0) }
32
+ end
33
+ end
34
+
35
+ describe "#authors_email" do
36
+ context "with one author" do
37
+ commits = Commits.new
38
+ commits.load(fixture("single_author.json"))
39
+ author_list = commits.authors_email
40
+
41
+ it { author_list.size.should be(1) }
42
+ it { author_list[0].should == "kevin.j.jalbert@gmail.com" }
43
+ end
44
+
45
+ context "with many authors" do
46
+ commits = Commits.new
47
+ commits.load(fixture("many_authors.json"))
48
+ author_list = commits.authors_email
49
+
50
+ it { author_list.size.should be(3) }
51
+ it { author_list[0].should == "bart.simpson@gmail.com" }
52
+ it { author_list[1].should == "kevin.j.jalbert@gmail.com" }
53
+ it { author_list[2].should == "maggie.simpson@gmail.com" }
54
+ end
55
+
56
+ context "with no authors" do
57
+ commits = Commits.new
58
+ author_list = commits.authors_email
59
+
60
+ it { author_list.size.should be(0) }
61
+ end
62
+ end
63
+
64
+ describe "#authors_statistics" do
65
+ context "with email" do
66
+ context "with merge" do
67
+ commits = Commits.new
68
+ commits.load(fixture("many_authors.json"))
69
+ results = commits.authors_statistics(true, true)
70
+
71
+ it {results.size.should be(3)}
72
+ it {results["bart.simpson@gmail.com"][:commits].should be(2)}
73
+ it {results["bart.simpson@gmail.com"][:insertions].should be(13)}
74
+ it {results["bart.simpson@gmail.com"][:deletions].should be(3)}
75
+ it {results["bart.simpson@gmail.com"][:creates].should be(2)}
76
+ it {results["bart.simpson@gmail.com"][:deletes].should be(0)}
77
+ it {results["bart.simpson@gmail.com"][:renames].should be(2)}
78
+ it {results["bart.simpson@gmail.com"][:copies].should be(0)}
79
+ it {results["bart.simpson@gmail.com"][:merges].should be(1)}
80
+
81
+ it {results["kevin.j.jalbert@gmail.com"][:commits].should be(1)}
82
+ it {results["kevin.j.jalbert@gmail.com"][:insertions].should be(62)}
83
+ it {results["kevin.j.jalbert@gmail.com"][:deletions].should be(6)}
84
+ it {results["kevin.j.jalbert@gmail.com"][:creates].should be(0)}
85
+ it {results["kevin.j.jalbert@gmail.com"][:deletes].should be(1)}
86
+ it {results["kevin.j.jalbert@gmail.com"][:renames].should be(0)}
87
+ it {results["kevin.j.jalbert@gmail.com"][:copies].should be(0)}
88
+ it {results["kevin.j.jalbert@gmail.com"][:merges].should be(1)}
89
+
90
+ it {results["maggie.simpson@gmail.com"][:commits].should be(2)}
91
+ it {results["maggie.simpson@gmail.com"][:insertions].should be(211)}
92
+ it {results["maggie.simpson@gmail.com"][:deletions].should be(192)}
93
+ it {results["maggie.simpson@gmail.com"][:creates].should be(7)}
94
+ it {results["maggie.simpson@gmail.com"][:deletes].should be(2)}
95
+ it {results["maggie.simpson@gmail.com"][:renames].should be(3)}
96
+ it {results["maggie.simpson@gmail.com"][:copies].should be(1)}
97
+ it {results["maggie.simpson@gmail.com"][:merges].should be(0)}
98
+
99
+ end
100
+
101
+ context "no merge" do
102
+ commits = Commits.new
103
+ commits.load(fixture("many_authors.json"))
104
+ results = commits.authors_statistics(true, false)
105
+
106
+ it {results.size.should be(3)}
107
+ it {results["bart.simpson@gmail.com"][:commits].should be(1)}
108
+ it {results["bart.simpson@gmail.com"][:insertions].should be(3)}
109
+ it {results["bart.simpson@gmail.com"][:deletions].should be(2)}
110
+ it {results["bart.simpson@gmail.com"][:creates].should be(2)}
111
+ it {results["bart.simpson@gmail.com"][:deletes].should be(0)}
112
+ it {results["bart.simpson@gmail.com"][:renames].should be(0)}
113
+ it {results["bart.simpson@gmail.com"][:copies].should be(0)}
114
+ it {results["bart.simpson@gmail.com"][:merges].should be(0)}
115
+
116
+ it {results["kevin.j.jalbert@gmail.com"][:commits].should be(0)}
117
+ it {results["kevin.j.jalbert@gmail.com"][:insertions].should be(0)}
118
+ it {results["kevin.j.jalbert@gmail.com"][:deletions].should be(0)}
119
+ it {results["kevin.j.jalbert@gmail.com"][:creates].should be(0)}
120
+ it {results["kevin.j.jalbert@gmail.com"][:deletes].should be(0)}
121
+ it {results["kevin.j.jalbert@gmail.com"][:renames].should be(0)}
122
+ it {results["kevin.j.jalbert@gmail.com"][:copies].should be(0)}
123
+ it {results["kevin.j.jalbert@gmail.com"][:merges].should be(0)}
124
+
125
+ it {results["maggie.simpson@gmail.com"][:commits].should be(2)}
126
+ it {results["maggie.simpson@gmail.com"][:insertions].should be(211)}
127
+ it {results["maggie.simpson@gmail.com"][:deletions].should be(192)}
128
+ it {results["maggie.simpson@gmail.com"][:creates].should be(7)}
129
+ it {results["maggie.simpson@gmail.com"][:deletes].should be(2)}
130
+ it {results["maggie.simpson@gmail.com"][:renames].should be(3)}
131
+ it {results["maggie.simpson@gmail.com"][:copies].should be(1)}
132
+ it {results["maggie.simpson@gmail.com"][:merges].should be(0)}
133
+ end
134
+ end
135
+
136
+ context "no email" do
137
+ context "with merge" do
138
+ commits = Commits.new
139
+ commits.load(fixture("many_authors.json"))
140
+ results = commits.authors_statistics(false, true)
141
+
142
+ it {results.size.should be(3)}
143
+ it {results["Bart Simpson"][:commits].should be(2)}
144
+ it {results["Bart Simpson"][:insertions].should be(13)}
145
+ it {results["Bart Simpson"][:deletions].should be(3)}
146
+ it {results["Bart Simpson"][:creates].should be(2)}
147
+ it {results["Bart Simpson"][:deletes].should be(0)}
148
+ it {results["Bart Simpson"][:renames].should be(2)}
149
+ it {results["Bart Simpson"][:copies].should be(0)}
150
+ it {results["Bart Simpson"][:merges].should be(1)}
151
+
152
+ it {results["Kevin Jalbert"][:commits].should be(1)}
153
+ it {results["Kevin Jalbert"][:insertions].should be(62)}
154
+ it {results["Kevin Jalbert"][:deletions].should be(6)}
155
+ it {results["Kevin Jalbert"][:creates].should be(0)}
156
+ it {results["Kevin Jalbert"][:deletes].should be(1)}
157
+ it {results["Kevin Jalbert"][:renames].should be(0)}
158
+ it {results["Kevin Jalbert"][:copies].should be(0)}
159
+ it {results["Kevin Jalbert"][:merges].should be(1)}
160
+
161
+ it {results["Maggie Simpson"][:commits].should be(2)}
162
+ it {results["Maggie Simpson"][:insertions].should be(211)}
163
+ it {results["Maggie Simpson"][:deletions].should be(192)}
164
+ it {results["Maggie Simpson"][:creates].should be(7)}
165
+ it {results["Maggie Simpson"][:deletes].should be(2)}
166
+ it {results["Maggie Simpson"][:renames].should be(3)}
167
+ it {results["Maggie Simpson"][:copies].should be(1)}
168
+ it {results["Maggie Simpson"][:merges].should be(0)}
169
+
170
+ end
171
+
172
+ context "no merge" do
173
+ commits = Commits.new
174
+ commits.load(fixture("many_authors.json"))
175
+ results = commits.authors_statistics(false, false)
176
+
177
+ it {results.size.should be(3)}
178
+ it {results["Bart Simpson"][:commits].should be(1)}
179
+ it {results["Bart Simpson"][:insertions].should be(3)}
180
+ it {results["Bart Simpson"][:deletions].should be(2)}
181
+ it {results["Bart Simpson"][:creates].should be(2)}
182
+ it {results["Bart Simpson"][:deletes].should be(0)}
183
+ it {results["Bart Simpson"][:renames].should be(0)}
184
+ it {results["Bart Simpson"][:copies].should be(0)}
185
+ it {results["Bart Simpson"][:merges].should be(0)}
186
+
187
+ it {results["Kevin Jalbert"][:commits].should be(0)}
188
+ it {results["Kevin Jalbert"][:insertions].should be(0)}
189
+ it {results["Kevin Jalbert"][:deletions].should be(0)}
190
+ it {results["Kevin Jalbert"][:creates].should be(0)}
191
+ it {results["Kevin Jalbert"][:deletes].should be(0)}
192
+ it {results["Kevin Jalbert"][:renames].should be(0)}
193
+ it {results["Kevin Jalbert"][:copies].should be(0)}
194
+ it {results["Kevin Jalbert"][:merges].should be(0)}
195
+
196
+ it {results["Maggie Simpson"][:commits].should be(2)}
197
+ it {results["Maggie Simpson"][:insertions].should be(211)}
198
+ it {results["Maggie Simpson"][:deletions].should be(192)}
199
+ it {results["Maggie Simpson"][:creates].should be(7)}
200
+ it {results["Maggie Simpson"][:deletes].should be(2)}
201
+ it {results["Maggie Simpson"][:renames].should be(3)}
202
+ it {results["Maggie Simpson"][:copies].should be(1)}
203
+ it {results["Maggie Simpson"][:merges].should be(0)}
204
+ end
205
+ end
206
+ end
207
+
208
+ describe "#author_top_n_type" do
209
+ context "no data" do
210
+ context "with email" do
211
+ commits = Commits.new
212
+ results = commits.author_top_n_type(true, :commits)
213
+
214
+ it { results.should be(nil)}
215
+ end
216
+
217
+ context "without email" do
218
+ commits = Commits.new
219
+ results = commits.author_top_n_type(false, :commits)
220
+
221
+ it { results.should be(nil)}
222
+ end
223
+ end
224
+
225
+ context "with data" do
226
+ context "with email" do
227
+ context "n is negative" do
228
+ commits = Commits.new
229
+ commits.load(fixture("many_authors.json"))
230
+ commits.calculate_statistics(true, true)
231
+ results = commits.author_top_n_type(true, :commits, -1)
232
+
233
+ it { results.size.should be(3)}
234
+ it { results[0][0].should == "bart.simpson@gmail.com"}
235
+ it { results[1][0].should == "maggie.simpson@gmail.com"}
236
+ it { results[2][0].should == "kevin.j.jalbert@gmail.com"}
237
+
238
+ end
239
+
240
+ context "n is 0" do
241
+ commits = Commits.new
242
+ commits.load(fixture("many_authors.json"))
243
+ commits.calculate_statistics(true, true)
244
+ results = commits.author_top_n_type(true, :commits, 0)
245
+
246
+ it { results.size.should be(3)}
247
+ it { results[0][0].should == "bart.simpson@gmail.com"}
248
+ it { results[1][0].should == "maggie.simpson@gmail.com"}
249
+ it { results[2][0].should == "kevin.j.jalbert@gmail.com"}
250
+ end
251
+
252
+ context "n is less then total" do
253
+ commits = Commits.new
254
+ commits.load(fixture("many_authors.json"))
255
+ commits.calculate_statistics(true, true)
256
+ results = commits.author_top_n_type(true, :commits, 2)
257
+
258
+ it { results.size.should be(2)}
259
+ it { results[0][0].should == "bart.simpson@gmail.com"}
260
+ it { results[1][0].should == "maggie.simpson@gmail.com"}
261
+ end
262
+
263
+ context "n is greater then total" do
264
+ commits = Commits.new
265
+ commits.load(fixture("many_authors.json"))
266
+ commits.calculate_statistics(true, true)
267
+ results = commits.author_top_n_type(true, :commits, 20)
268
+
269
+ it { results.size.should be(3)}
270
+ it { results[0][0].should == "bart.simpson@gmail.com"}
271
+ it { results[1][0].should == "maggie.simpson@gmail.com"}
272
+ it { results[2][0].should == "kevin.j.jalbert@gmail.com"}
273
+ end
274
+ end
275
+
276
+ context "no email" do
277
+ context "n is negative" do
278
+ commits = Commits.new
279
+ commits.load(fixture("many_authors.json"))
280
+ commits.calculate_statistics(false, true)
281
+ results = commits.author_top_n_type(false, :commits, -1)
282
+
283
+ it { results.size.should be(3)}
284
+ it { results[0][0].should == "Bart Simpson"}
285
+ it { results[1][0].should == "Maggie Simpson"}
286
+ it { results[2][0].should == "Kevin Jalbert"}
287
+
288
+ end
289
+
290
+ context "n is 0" do
291
+ commits = Commits.new
292
+ commits.load(fixture("many_authors.json"))
293
+ commits.calculate_statistics(false, true)
294
+ results = commits.author_top_n_type(false, :commits, 0)
295
+
296
+ it { results.size.should be(3)}
297
+ it { results[0][0].should == "Bart Simpson"}
298
+ it { results[1][0].should == "Maggie Simpson"}
299
+ it { results[2][0].should == "Kevin Jalbert"}
300
+ end
301
+
302
+ context "n is less then total" do
303
+ commits = Commits.new
304
+ commits.load(fixture("many_authors.json"))
305
+ commits.calculate_statistics(false, true)
306
+ results = commits.author_top_n_type(false, :commits, 2)
307
+
308
+ it { results.size.should be(2)}
309
+ it { results[0][0].should == "Bart Simpson"}
310
+ it { results[1][0].should == "Maggie Simpson"}
311
+ end
312
+
313
+ context "n is greater then total" do
314
+ commits = Commits.new
315
+ commits.load(fixture("many_authors.json"))
316
+ commits.calculate_statistics(false, true)
317
+ results = commits.author_top_n_type(false, :commits, 20)
318
+
319
+ it { results.size.should be(3)}
320
+ it { results[0][0].should == "Bart Simpson"}
321
+ it { results[1][0].should == "Maggie Simpson"}
322
+ it { results[2][0].should == "Kevin Jalbert"}
323
+ end
324
+ end
325
+ end
326
+ end
327
+
328
+ describe "#calculate_statistics (totals)" do
329
+ context "with merge" do
330
+ commits = Commits.new
331
+ commits.load(fixture("many_authors.json"))
332
+ commits.calculate_statistics(true, true)
333
+ results = commits.totals
334
+
335
+ it {results[:commits].should be(5)}
336
+ it {results[:insertions].should be(286)}
337
+ it {results[:deletions].should be(201)}
338
+ it {results[:creates].should be(9)}
339
+ it {results[:deletes].should be(3)}
340
+ it {results[:renames].should be(5)}
341
+ it {results[:copies].should be(1)}
342
+ it {results[:merges].should be(2)}
343
+ end
344
+
345
+ context "no merge" do
346
+ commits = Commits.new
347
+ commits.load(fixture("many_authors.json"))
348
+ commits.calculate_statistics(false, false)
349
+ results = commits.totals
350
+
351
+ it {results[:commits].should be(3)}
352
+ it {results[:insertions].should be(214)}
353
+ it {results[:deletions].should be(194)}
354
+ it {results[:creates].should be(9)}
355
+ it {results[:deletes].should be(2)}
356
+ it {results[:renames].should be(3)}
357
+ it {results[:copies].should be(1)}
358
+ it {results[:merges].should be(0)}
359
+ end
360
+ end
361
+
362
+ describe "#save" do
363
+ context "with pretty" do
364
+ commits = Commits.new
365
+ commits.load(fixture("single_author.json"))
366
+ commits.save("tmp.json", true)
367
+ same = FileUtils.compare_file("tmp.json", fixture("single_author.json"))
368
+ FileUtils.remove_file("tmp.json")
369
+ it { same.should be_true }
370
+ end
371
+
372
+ context "no pretty" do
373
+ commits = Commits.new
374
+ commits.load(fixture("single_author.json"))
375
+ commits.save("tmp.json", false)
376
+ same = FileUtils.compare_file("tmp.json", fixture("single_author_unpretty.json"))
377
+ FileUtils.remove_file("tmp.json")
378
+ it { same.should be_true }
379
+ end
380
+ end
381
+ end
@@ -0,0 +1,62 @@
1
+ {
2
+ "49164f12e4cd3b253e5de31242bc716c9e7d6f95": {
3
+ "author": "Bart Simpson",
4
+ "author_email": "bart.simpson@gmail.com",
5
+ "time": "2012-04-06 11:58:16 -0400",
6
+ "insertions": 3,
7
+ "deletions": 2,
8
+ "creates": 2,
9
+ "deletes": 0,
10
+ "renames": 0,
11
+ "copies": 0,
12
+ "merge": false
13
+ },
14
+ "642276027055befb50999c7c155391402bb7c2e8": {
15
+ "author": "Kevin Jalbert",
16
+ "author_email": "kevin.j.jalbert@gmail.com",
17
+ "time": "2012-04-09 21:02:42 -0400",
18
+ "insertions": 62,
19
+ "deletions": 6,
20
+ "creates": 0,
21
+ "deletes": 1,
22
+ "renames": 0,
23
+ "copies": 0,
24
+ "merge": true
25
+ },
26
+ "754e18e83c748700d883048dbc889c7b0db7121d": {
27
+ "author": "Bart Simpson",
28
+ "author_email": "bart.simpson@gmail.com",
29
+ "time": "2012-04-09 21:15:46 -0400",
30
+ "insertions": 10,
31
+ "deletions": 1,
32
+ "creates": 0,
33
+ "deletes": 0,
34
+ "renames": 2,
35
+ "copies": 0,
36
+ "merge": true
37
+ },
38
+ "f9e638cd4bf18d4f6db27a9c085a9f558f762cc3": {
39
+ "author": "Maggie Simpson",
40
+ "author_email": "maggie.simpson@gmail.com",
41
+ "time": "2012-04-09 21:42:07 -0400",
42
+ "insertions": 8,
43
+ "deletions": 5,
44
+ "creates": 4,
45
+ "deletes": 1,
46
+ "renames": 2,
47
+ "copies": 1,
48
+ "merge": false
49
+ },
50
+ "fba10799b65b0e50c8dc355c9acae08d22b193ae": {
51
+ "author": "Maggie Simpson",
52
+ "author_email": "maggie.simpson@gmail.com",
53
+ "time": "2012-04-11 11:48:07 -0400",
54
+ "insertions": 203,
55
+ "deletions": 187,
56
+ "creates": 3,
57
+ "deletes": 1,
58
+ "renames": 1,
59
+ "copies": 0,
60
+ "merge": false
61
+ }
62
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "f9e638cd4bf18d4f6db27a9c085a9f558f762cc3": {
3
+ "author": "Kevin Jalbert",
4
+ "author_email": "kevin.j.jalbert@gmail.com",
5
+ "time": "2012-04-09 21:42:07 -0400",
6
+ "insertions": 8,
7
+ "deletions": 5,
8
+ "creates": 0,
9
+ "deletes": 0,
10
+ "renames": 0,
11
+ "copies": 0,
12
+ "merge": false
13
+ },
14
+ "fba10799b65b0e50c8dc355c9acae08d22b193ae": {
15
+ "author": "Kevin Jalbert",
16
+ "author_email": "kevin.j.jalbert@gmail.com",
17
+ "time": "2012-04-11 11:48:07 -0400",
18
+ "insertions": 203,
19
+ "deletions": 187,
20
+ "creates": 3,
21
+ "deletes": 1,
22
+ "renames": 1,
23
+ "copies": 0,
24
+ "merge": false
25
+ }
26
+ }
@@ -0,0 +1 @@
1
+ {"f9e638cd4bf18d4f6db27a9c085a9f558f762cc3":{"author":"Kevin Jalbert","author_email":"kevin.j.jalbert@gmail.com","time":"2012-04-09 21:42:07 -0400","insertions":8,"deletions":5,"creates":0,"deletes":0,"renames":0,"copies":0,"merge":false},"fba10799b65b0e50c8dc355c9acae08d22b193ae":{"author":"Kevin Jalbert","author_email":"kevin.j.jalbert@gmail.com","time":"2012-04-11 11:48:07 -0400","insertions":203,"deletions":187,"creates":3,"deletes":1,"renames":1,"copies":0,"merge":false}}
@@ -0,0 +1,11 @@
1
+ require 'simplecov'
2
+
3
+ SimpleCov.start do
4
+ add_filter "/spec/"
5
+ end
6
+
7
+ require File.dirname(__FILE__) + '/../lib/git_statistics/initialize.rb'
8
+
9
+ def fixture(file)
10
+ File.new(File.dirname(__FILE__) + '/fixtures/' + file, 'r')
11
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: git_statistics
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Kevin Jalbert
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: json
16
+ requirement: &14258420 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *14258420
25
+ - !ruby/object:Gem::Dependency
26
+ name: trollop
27
+ requirement: &14257940 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *14257940
36
+ description: git_statistics is a gem that provides detailed git statistics
37
+ email:
38
+ - kevin.j.jalbert@gmail.com
39
+ executables:
40
+ - git_statistics
41
+ extensions: []
42
+ extra_rdoc_files: []
43
+ files:
44
+ - .gitignore
45
+ - .travis.yml
46
+ - Gemfile
47
+ - Gemfile.lock
48
+ - LICENSE
49
+ - README.md
50
+ - Rakefile
51
+ - bin/git_statistics
52
+ - git_statistics.gemspec
53
+ - lib/git_statistics.rb
54
+ - lib/git_statistics/collector.rb
55
+ - lib/git_statistics/commits.rb
56
+ - lib/git_statistics/initialize.rb
57
+ - lib/git_statistics/version.rb
58
+ - spec/collector_spec.rb
59
+ - spec/commits_spec.rb
60
+ - spec/fixtures/many_authors.json
61
+ - spec/fixtures/single_author.json
62
+ - spec/fixtures/single_author_unpretty.json
63
+ - spec/spec_helper.rb
64
+ homepage: https://github.com/kevinjalbert/git_statistics
65
+ licenses: []
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: 1.9.1
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ segments:
83
+ - 0
84
+ hash: -1773514383536300330
85
+ requirements: []
86
+ rubyforge_project:
87
+ rubygems_version: 1.8.15
88
+ signing_key:
89
+ specification_version: 3
90
+ summary: Gem that provides the ability to gather detailed git statistics
91
+ test_files:
92
+ - spec/collector_spec.rb
93
+ - spec/commits_spec.rb
94
+ - spec/fixtures/many_authors.json
95
+ - spec/fixtures/single_author.json
96
+ - spec/fixtures/single_author_unpretty.json
97
+ - spec/spec_helper.rb