git_stats 1.0.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/.gitignore +21 -0
- data/.gitmodules +3 -0
- data/.rspec +1 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +22 -0
- data/README.md +44 -0
- data/Rakefile +1 -0
- data/bin/git_stats +11 -0
- data/config/locales/en.yml +56 -0
- data/config/locales/pl.yml +56 -0
- data/config/locales/pl_default.yml +224 -0
- data/git_stats.gemspec +27 -0
- data/lib/git_stats.rb +15 -0
- data/lib/git_stats/base.rb +30 -0
- data/lib/git_stats/by_field_finder.rb +7 -0
- data/lib/git_stats/cli.rb +17 -0
- data/lib/git_stats/core_extensions/hash.rb +9 -0
- data/lib/git_stats/core_extensions/string.rb +6 -0
- data/lib/git_stats/core_extensions/symbol.rb +6 -0
- data/lib/git_stats/generator.rb +32 -0
- data/lib/git_stats/git_data/activity.rb +78 -0
- data/lib/git_stats/git_data/author.rb +67 -0
- data/lib/git_stats/git_data/blob.rb +38 -0
- data/lib/git_stats/git_data/command_parser.rb +33 -0
- data/lib/git_stats/git_data/command_runner.rb +10 -0
- data/lib/git_stats/git_data/commit.rb +63 -0
- data/lib/git_stats/git_data/repo.rb +137 -0
- data/lib/git_stats/git_data/short_stat.rb +33 -0
- data/lib/git_stats/hash_initializable.rb +7 -0
- data/lib/git_stats/i18n.rb +2 -0
- data/lib/git_stats/stats_view/charts/activity_charts.rb +70 -0
- data/lib/git_stats/stats_view/charts/authors_charts.rb +35 -0
- data/lib/git_stats/stats_view/charts/chart.rb +119 -0
- data/lib/git_stats/stats_view/charts/charts.rb +35 -0
- data/lib/git_stats/stats_view/charts/repo_charts.rb +53 -0
- data/lib/git_stats/stats_view/template.rb +20 -0
- data/lib/git_stats/stats_view/view.rb +72 -0
- data/lib/git_stats/stats_view/view_data.rb +31 -0
- data/lib/git_stats/version.rb +4 -0
- data/spec/by_field_finder_spec.rb +25 -0
- data/spec/factories.rb +25 -0
- data/spec/git_data/activity_spec.rb +38 -0
- data/spec/git_data/author_spec.rb +25 -0
- data/spec/git_data/blob_spec.rb +25 -0
- data/spec/git_data/cli_spec.rb +20 -0
- data/spec/git_data/command_observer_spec.rb +35 -0
- data/spec/git_data/commit_range_spec.rb +47 -0
- data/spec/git_data/commit_spec.rb +48 -0
- data/spec/git_data/generator_spec.rb +46 -0
- data/spec/git_data/repo_spec.rb +46 -0
- data/spec/git_data/short_stat_spec.rb +28 -0
- data/spec/hash_extension_spec.rb +26 -0
- data/spec/integration/activity_spec.rb +33 -0
- data/spec/integration/author_spec.rb +67 -0
- data/spec/integration/file_spec.rb +31 -0
- data/spec/integration/repo_spec.rb +68 -0
- data/spec/integration/shared.rb +37 -0
- data/spec/spec_helper.rb +13 -0
- data/templates/activity/_activity.haml +101 -0
- data/templates/activity/by_date.haml +1 -0
- data/templates/activity/day_of_week.haml +1 -0
- data/templates/activity/hour_of_day.haml +1 -0
- data/templates/activity/hour_of_week.haml +1 -0
- data/templates/activity/month_of_year.haml +1 -0
- data/templates/activity/year.haml +1 -0
- data/templates/activity/year_month.haml +1 -0
- data/templates/assets/bootstrap/css/bootstrap-responsive.css +1058 -0
- data/templates/assets/bootstrap/css/bootstrap-responsive.min.css +9 -0
- data/templates/assets/bootstrap/css/bootstrap.css +5774 -0
- data/templates/assets/bootstrap/css/bootstrap.min.css +9 -0
- data/templates/assets/bootstrap/img/glyphicons-halflings-white.png +0 -0
- data/templates/assets/bootstrap/img/glyphicons-halflings.png +0 -0
- data/templates/assets/bootstrap/js/bootstrap.js +2027 -0
- data/templates/assets/bootstrap/js/bootstrap.min.js +6 -0
- data/templates/assets/highstock.js +305 -0
- data/templates/assets/jquery.min.js +2 -0
- data/templates/authors/_authors.haml +66 -0
- data/templates/authors/best_authors.haml +1 -0
- data/templates/authors/changed_lines_by_author_by_date.haml +1 -0
- data/templates/authors/commits_sum_by_author_by_date.haml +1 -0
- data/templates/authors/deletions_by_author_by_date.haml +1 -0
- data/templates/authors/insertions_by_author_by_date.haml +1 -0
- data/templates/files/_files.haml +15 -0
- data/templates/files/by_date.haml +1 -0
- data/templates/files/by_extension.haml +1 -0
- data/templates/general.haml +28 -0
- data/templates/layout.haml +29 -0
- data/templates/lines/_lines.haml +15 -0
- data/templates/lines/by_date.haml +1 -0
- data/templates/lines/by_extension.haml +1 -0
- data/templates/static/index.html +5 -0
- metadata +268 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
module GitStats
|
|
3
|
+
module StatsView
|
|
4
|
+
module Charts
|
|
5
|
+
class RepoCharts
|
|
6
|
+
def initialize(repo)
|
|
7
|
+
@repo = repo
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def files_by_extension
|
|
11
|
+
Chart.new do |f|
|
|
12
|
+
f.column_hash_chart(
|
|
13
|
+
data: @repo.files_by_extension_count,
|
|
14
|
+
title: :files_by_extension.t,
|
|
15
|
+
y_text: :files.t
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def lines_by_extension
|
|
21
|
+
Chart.new do |f|
|
|
22
|
+
f.column_hash_chart(
|
|
23
|
+
data: @repo.lines_by_extension,
|
|
24
|
+
title: :lines_by_extension.t,
|
|
25
|
+
y_text: :lines.t
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def files_by_date
|
|
31
|
+
Chart.new do |f|
|
|
32
|
+
f.date_chart(
|
|
33
|
+
data: @repo.files_count_by_date,
|
|
34
|
+
title: :files_by_date.t,
|
|
35
|
+
y_text: :files.t
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def lines_by_date
|
|
41
|
+
Chart.new do |f|
|
|
42
|
+
f.date_chart(
|
|
43
|
+
data: @repo.lines_count_by_date,
|
|
44
|
+
title: :lines_by_date.t,
|
|
45
|
+
y_text: :lines.t
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
module GitStats
|
|
3
|
+
module StatsView
|
|
4
|
+
class Template
|
|
5
|
+
def initialize(name, layout=nil)
|
|
6
|
+
@name = name
|
|
7
|
+
@layout = layout
|
|
8
|
+
@template = Tilt.new("../../../../templates/#@name.haml".absolute_path)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def render(data, params={})
|
|
12
|
+
if @layout
|
|
13
|
+
@layout.render(data, :active_page => params[:active_page] || @name, :links => params[:links]) { @template.render(data, params) }
|
|
14
|
+
else
|
|
15
|
+
@template.render(data, params)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
module GitStats
|
|
3
|
+
module StatsView
|
|
4
|
+
class View
|
|
5
|
+
def initialize(view_data, out_path)
|
|
6
|
+
@view_data, @out_path = view_data, out_path
|
|
7
|
+
@layout = Tilt.new("../../../../templates/layout.haml".absolute_path)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def render_all
|
|
11
|
+
prepare_static_content
|
|
12
|
+
prepare_assets
|
|
13
|
+
|
|
14
|
+
all_templates.each do |template|
|
|
15
|
+
output = Template.new(template, @layout).render(@view_data, author: @view_data.repo, links: links)
|
|
16
|
+
write(output, "#@out_path/#{template}.html")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
render_authors_activity
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def render_authors_activity
|
|
23
|
+
done = []
|
|
24
|
+
@view_data.repo.authors.sort_by { |a| -a.commits.size }.each do |author|
|
|
25
|
+
next if done.include? author.dirname
|
|
26
|
+
done << author.dirname
|
|
27
|
+
all_templates('activity/').each do |template|
|
|
28
|
+
output = Template.new(template, @layout).render(@view_data,
|
|
29
|
+
author: author,
|
|
30
|
+
links: links,
|
|
31
|
+
active_page: "/authors/#{author.dirname}/#{template}")
|
|
32
|
+
write(output, "#@out_path/authors/#{author.dirname}/#{template}.html")
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def all_templates(root = '')
|
|
38
|
+
Dir["../../../../templates/#{root}**/[^_]*.haml".absolute_path].map {
|
|
39
|
+
|f| Pathname.new(f)
|
|
40
|
+
}.map { |f|
|
|
41
|
+
f.relative_path_from(Pathname.new('../../../../templates'.absolute_path)).sub_ext('')
|
|
42
|
+
}.map(&:to_s) - %w(layout)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def write(output, write_file)
|
|
48
|
+
FileUtils.mkdir_p(File.dirname(write_file))
|
|
49
|
+
File.open(write_file, 'w') { |f| f.write output }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def links
|
|
53
|
+
{
|
|
54
|
+
general: 'general.html',
|
|
55
|
+
activity: 'activity/by_date.html',
|
|
56
|
+
authors: 'authors/best_authors.html',
|
|
57
|
+
files: 'files/by_date.html',
|
|
58
|
+
lines: 'lines/by_date.html'
|
|
59
|
+
}
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def prepare_static_content
|
|
63
|
+
FileUtils.cp_r(Dir["../../../../templates/static/*".absolute_path], @out_path)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def prepare_assets
|
|
67
|
+
FileUtils.cp_r('../../../../templates/assets'.absolute_path, @out_path)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
module GitStats
|
|
3
|
+
module StatsView
|
|
4
|
+
class ViewData
|
|
5
|
+
include ActionView::Helpers::TagHelper
|
|
6
|
+
include LazyHighCharts::LayoutHelper
|
|
7
|
+
attr_reader :repo
|
|
8
|
+
|
|
9
|
+
def initialize(repo)
|
|
10
|
+
@repo = repo
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def charts
|
|
14
|
+
@charts ||= Charts::All.new(repo)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def render_partial(template_name, params = {})
|
|
18
|
+
Template.new(template_name).render(self, params)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def asset_path(asset, active_page)
|
|
22
|
+
Pathname.new("/assets/#{asset}").relative_path_from(Pathname.new("/#{active_page}").dirname)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def link_to(href, active_page)
|
|
26
|
+
Pathname.new("/#{href}").relative_path_from(Pathname.new("/#{active_page}").dirname)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
describe ByFieldFinder do
|
|
5
|
+
let(:sut) { [double(field1: 'aa', field2: 'bb'), double(field1: 'cc', field2: 'bb'), double(field1: 'aa', field2: 'dd')].extend(ByFieldFinder) }
|
|
6
|
+
|
|
7
|
+
[
|
|
8
|
+
{field: :field1, search_value: 'aa', matching_index: 0},
|
|
9
|
+
{field: :field1, search_value: 'cc', matching_index: 1},
|
|
10
|
+
{field: :field2, search_value: 'bb', matching_index: 0},
|
|
11
|
+
{field: :field2, search_value: 'dd', matching_index: 2},
|
|
12
|
+
].each do |test_params|
|
|
13
|
+
it 'should return first matching object' do
|
|
14
|
+
sut.send("by_#{test_params[:field]}", test_params[:search_value]).should == sut[test_params[:matching_index]]
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'should return nil if no object matches' do
|
|
19
|
+
sut.by_field1('xx').should == nil
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'should throw exception if elements doesnt respond to given field' do
|
|
23
|
+
expect { sut.by_non_existing_field }.to raise_error
|
|
24
|
+
end
|
|
25
|
+
end
|
data/spec/factories.rb
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
FactoryGirl.define do
|
|
3
|
+
initialize_with { new(attributes) }
|
|
4
|
+
|
|
5
|
+
factory :repo, class: GitStats::GitData::Repo do
|
|
6
|
+
path "repo_path"
|
|
7
|
+
factory :test_repo do
|
|
8
|
+
path 'spec/integration/test_repo'
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
factory :author, class: GitStats::GitData::Author do
|
|
13
|
+
sequence(:name) { |i| "author#{i}" }
|
|
14
|
+
sequence(:email) { |i| "author#{i}@gmail.com" }
|
|
15
|
+
association :repo, strategy: :build
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
factory :commit, class: GitStats::GitData::Commit do
|
|
19
|
+
sequence(:sha) { |i| i }
|
|
20
|
+
sequence(:stamp) { |i| i }
|
|
21
|
+
sequence(:date) { |i| Date.new(i) }
|
|
22
|
+
association :repo, strategy: :build
|
|
23
|
+
association :author, strategy: :build
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
describe GitStats::GitData::Activity do
|
|
5
|
+
let(:dates) { [
|
|
6
|
+
'10.05.2012 12:37',
|
|
7
|
+
'10.05.2012 13:53',
|
|
8
|
+
'06.05.2012 13:23',
|
|
9
|
+
'15.06.2011 15:02',
|
|
10
|
+
'27.09.2011 15:34'
|
|
11
|
+
] }
|
|
12
|
+
let(:commits) { dates.map { |d| GitStats::GitData::Commit.new(:date => DateTime.parse(d)) } }
|
|
13
|
+
let(:activity) { GitStats::GitData::Activity.new(commits) }
|
|
14
|
+
|
|
15
|
+
it 'by_hour should count commits by hour' do
|
|
16
|
+
activity.by_hour.should == {12 => 1, 13 => 2, 15 => 2}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'by_wday should count commits by day of week where 0 = sunday, 1 = monday, ...' do
|
|
20
|
+
activity.by_wday.should == {0 => 1, 2 => 1, 3 => 1, 4 => 2}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'by_wday_hour should count commits by day of week and by hour' do
|
|
24
|
+
activity.by_wday_hour.should == {0 => {13 => 1}, 2 => {15 => 1}, 3 => {15 => 1}, 4 => {12 => 1, 13 => 1}}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'by_month should count commits by month' do
|
|
28
|
+
activity.by_month.should == {5 => 3, 6 => 1, 9 => 1}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'by_year should count commits by year' do
|
|
32
|
+
activity.by_year.should == {2011 => 2, 2012 => 3}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'by_year_month should count commits by day of year and by month' do
|
|
36
|
+
activity.by_year_month.should == {2011 => {6 => 1, 9 => 1}, 2012 => {5 => 3}}
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
describe GitStats::GitData::Author do
|
|
5
|
+
let(:repo) { build(:repo) }
|
|
6
|
+
let(:author) { build(:author, repo: repo) }
|
|
7
|
+
let(:other_author) { build(:author, repo: repo) }
|
|
8
|
+
let(:my_commits) { 10.times.map { |i| double("my_commit #{i}", author: author, short_stat: double("my_short_stat #{i}", insertions: 5, deletions: 10)) } }
|
|
9
|
+
let(:other_commits) { 10.times.map { |i| double("other_commit #{i}", author: other_author) } }
|
|
10
|
+
|
|
11
|
+
before { repo.stub(:commits => my_commits + other_commits) }
|
|
12
|
+
|
|
13
|
+
it 'commits should give repo commits filtered to this author' do
|
|
14
|
+
author.commits.should == my_commits
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'should count lines added from short stat' do
|
|
18
|
+
author.insertions.should == 50
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'should count lines deleted from short stat' do
|
|
22
|
+
author.deletions.should == 100
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
describe GitStats::GitData::Blob do
|
|
5
|
+
let(:repo) { double }
|
|
6
|
+
let(:png_blob) { GitStats::GitData::Blob.new(filename: 'abc.png', sha: 'hash_png', repo: repo) }
|
|
7
|
+
let(:txt_blob) { GitStats::GitData::Blob.new(filename: 'abc.txt', sha: 'hash_txt', repo: repo) }
|
|
8
|
+
|
|
9
|
+
it 'should return 0 as lines count when files is binary' do
|
|
10
|
+
png_blob.should_receive(:binary?).and_return true
|
|
11
|
+
png_blob.lines_count.should == 0
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'should return actual lines count when files is not binary' do
|
|
15
|
+
txt_blob.should_receive(:binary?).and_return false
|
|
16
|
+
repo.should_receive(:run).with("git cat-file blob hash_txt | wc -l").and_return 42
|
|
17
|
+
txt_blob.lines_count.should == 42
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'should invoke grep to check if file is binary' do
|
|
21
|
+
repo.should_receive(:run).with("git cat-file blob hash_png | grep -m 1 '^'").and_return "Binary file matches"
|
|
22
|
+
png_blob.should be_binary
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
describe GitStats::CLI do
|
|
5
|
+
let(:repo_path) { 'repo_path' }
|
|
6
|
+
let(:out_path) { 'out_path' }
|
|
7
|
+
|
|
8
|
+
it 'should invoke generator with console arguments given' do
|
|
9
|
+
generator = double('generator')
|
|
10
|
+
GitStats::Generator.should_receive(:new).with(repo_path, out_path).and_return(generator)
|
|
11
|
+
generator.should_receive(:render_all)
|
|
12
|
+
|
|
13
|
+
subject.start(repo_path, out_path)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'should raise error when 2 arguments are not given' do
|
|
17
|
+
expect { subject.start("only one argument") }.to raise_error
|
|
18
|
+
expect { subject.start("too", "much", "arguments") }.to raise_error
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
describe GitStats::GitData::Repo do
|
|
5
|
+
let(:repo) { build(:repo) }
|
|
6
|
+
|
|
7
|
+
describe 'command observers' do
|
|
8
|
+
context 'should be invoked after every command' do
|
|
9
|
+
it 'should accept block' do
|
|
10
|
+
command_runner = double('command_runner')
|
|
11
|
+
repo = build(:repo, command_runner: command_runner)
|
|
12
|
+
|
|
13
|
+
observer = double('observer')
|
|
14
|
+
repo.add_command_observer { |command, result| observer.invoked(command, result) }
|
|
15
|
+
command_runner.should_receive(:run).with(repo.path, 'aa').and_return('bb')
|
|
16
|
+
observer.should_receive(:invoked).with('aa', 'bb')
|
|
17
|
+
|
|
18
|
+
repo.run('aa')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'should accept Proc' do
|
|
22
|
+
command_runner = double('command_runner')
|
|
23
|
+
repo = build(:repo, command_runner: command_runner)
|
|
24
|
+
|
|
25
|
+
observer = double('observer')
|
|
26
|
+
repo.add_command_observer(observer)
|
|
27
|
+
command_runner.should_receive(:run).with(repo.path, 'aa').and_return('bb')
|
|
28
|
+
observer.should_receive(:call).with('aa', 'bb')
|
|
29
|
+
|
|
30
|
+
repo.run('aa')
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
describe GitStats::GitData::Repo do
|
|
5
|
+
let(:repo) { build(:repo) }
|
|
6
|
+
|
|
7
|
+
describe 'commit range' do
|
|
8
|
+
it 'should return HEAD by default' do
|
|
9
|
+
repo.commit_range.should == 'HEAD'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'should return last_commit if it was given' do
|
|
13
|
+
repo = build(:repo, last_commit_sha: 'abc')
|
|
14
|
+
repo.commit_range.should == 'abc'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'should return range from first_commit to HEAD if first_commit was given' do
|
|
18
|
+
repo = build(:repo, first_commit_sha: 'abc')
|
|
19
|
+
repo.commit_range.should == 'abc..HEAD'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'should return range from first to last commit if both were given' do
|
|
23
|
+
repo = build(:repo, first_commit_sha: 'abc', last_commit_sha: 'def')
|
|
24
|
+
repo.commit_range.should == 'abc..def'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context 'git commands using range' do
|
|
28
|
+
let(:repo) { build(:repo, first_commit_sha: 'abc', last_commit_sha: 'def') }
|
|
29
|
+
|
|
30
|
+
it 'should affect authors command' do
|
|
31
|
+
repo.should_receive(:run).with('git shortlog -se abc..def').and_return("")
|
|
32
|
+
repo.authors
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'should affect commits command' do
|
|
36
|
+
repo.should_receive(:run).with("git rev-list --pretty=format:'%h|%at|%ai|%aE' abc..def | grep -v commit").and_return("")
|
|
37
|
+
repo.commits
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'should affect project version command' do
|
|
41
|
+
repo.should_receive(:run).with('git rev-parse --short abc..def').and_return("")
|
|
42
|
+
repo.project_version
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
describe GitStats::GitData::Commit do
|
|
5
|
+
let(:commit) { build(:commit, sha: 'abc') }
|
|
6
|
+
|
|
7
|
+
describe 'git output parsing' do
|
|
8
|
+
context 'parsing git ls-tree output' do
|
|
9
|
+
before {
|
|
10
|
+
commit.repo.should_receive(:run).with('git ls-tree -r abc').and_return("100644 blob 5ade7ad51a75ee7db4eb06cecd3918d38134087d lib/git_stats/git_data/commit.rb
|
|
11
|
+
100644 blob db01e94677a8f72289848e507a52a43de2ea109a lib/git_stats/git_data/repo.rb
|
|
12
|
+
100644 blob 1463eacb3ac9f95f21f360f1eb935a84a9ee0895 templates/index.haml
|
|
13
|
+
100644 blob 31d8b960a67f195bdedaaf9e7aa70b2389f3f1a8 templates/assets/bootstrap/css/bootstrap.min.css
|
|
14
|
+
") }
|
|
15
|
+
|
|
16
|
+
it 'should be parsed to files' do
|
|
17
|
+
commit.files.should == [
|
|
18
|
+
GitStats::GitData::Blob.new(repo: commit.repo, sha: "5ade7ad51a75ee7db4eb06cecd3918d38134087d", filename: "lib/git_stats/git_data/commit.rb"),
|
|
19
|
+
GitStats::GitData::Blob.new(repo: commit.repo, sha: "db01e94677a8f72289848e507a52a43de2ea109a", filename: "lib/git_stats/git_data/repo.rb"),
|
|
20
|
+
GitStats::GitData::Blob.new(repo: commit.repo, sha: "1463eacb3ac9f95f21f360f1eb935a84a9ee0895", filename: "templates/index.haml"),
|
|
21
|
+
GitStats::GitData::Blob.new(repo: commit.repo, sha: "31d8b960a67f195bdedaaf9e7aa70b2389f3f1a8", filename: "templates/assets/bootstrap/css/bootstrap.min.css"),
|
|
22
|
+
]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'should group files by extension' do
|
|
26
|
+
commit.files_by_extension.should == {'.rb' => [
|
|
27
|
+
GitStats::GitData::Blob.new(repo: commit.repo, sha: "5ade7ad51a75ee7db4eb06cecd3918d38134087d", filename: "lib/git_stats/git_data/commit.rb"),
|
|
28
|
+
GitStats::GitData::Blob.new(repo: commit.repo, sha: "db01e94677a8f72289848e507a52a43de2ea109a", filename: "lib/git_stats/git_data/repo.rb")
|
|
29
|
+
], '.haml' => [
|
|
30
|
+
GitStats::GitData::Blob.new(repo: commit.repo, sha: "1463eacb3ac9f95f21f360f1eb935a84a9ee0895", filename: "templates/index.haml")
|
|
31
|
+
], '.css' => [
|
|
32
|
+
GitStats::GitData::Blob.new(repo: commit.repo, sha: "31d8b960a67f195bdedaaf9e7aa70b2389f3f1a8", filename: "templates/assets/bootstrap/css/bootstrap.min.css")
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'should count lines by extension excluding empty or binary files' do
|
|
38
|
+
GitStats::GitData::Blob.should_receive(:new).and_return(
|
|
39
|
+
double(lines_count: 40, extension: '.rb'),
|
|
40
|
+
double(lines_count: 60, extension: '.rb'),
|
|
41
|
+
double(lines_count: 0, extension: '.haml'),
|
|
42
|
+
double(lines_count: 20, extension: '.css'),
|
|
43
|
+
)
|
|
44
|
+
commit.lines_by_extension.should == {'.rb' => 100, '.css' => 20}
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|