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
data/git_stats.gemspec
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'git_stats/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |gem|
|
|
7
|
+
gem.name = "git_stats"
|
|
8
|
+
gem.version = GitStats::VERSION
|
|
9
|
+
gem.authors = ["Tomasz Gieniusz"]
|
|
10
|
+
gem.email = ["tomasz.gieniusz@gmail.com"]
|
|
11
|
+
gem.description = %q{Git history statistics generator}
|
|
12
|
+
gem.summary = %q{HTML statistics generator from git repository}
|
|
13
|
+
gem.homepage = "https://github.com/tomgi/git_stats"
|
|
14
|
+
|
|
15
|
+
gem.files = `git ls-files`.split($/)
|
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
|
18
|
+
gem.require_paths = ["lib"]
|
|
19
|
+
|
|
20
|
+
gem.add_dependency('activesupport')
|
|
21
|
+
gem.add_dependency('actionpack')
|
|
22
|
+
gem.add_dependency('tilt')
|
|
23
|
+
gem.add_dependency('haml')
|
|
24
|
+
gem.add_dependency('launchy')
|
|
25
|
+
gem.add_dependency('lazy_high_charts_tomgi')
|
|
26
|
+
gem.add_dependency('i18n')
|
|
27
|
+
end
|
data/lib/git_stats.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
module GitStats
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
require 'active_support/all'
|
|
6
|
+
require 'action_pack'
|
|
7
|
+
require 'action_view'
|
|
8
|
+
require 'fileutils'
|
|
9
|
+
require 'tilt'
|
|
10
|
+
require 'pathname'
|
|
11
|
+
require 'lazy_high_charts'
|
|
12
|
+
require 'launchy'
|
|
13
|
+
require 'i18n'
|
|
14
|
+
|
|
15
|
+
require 'git_stats/base'
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
require 'git_stats/core_extensions/hash'
|
|
3
|
+
require 'git_stats/core_extensions/string'
|
|
4
|
+
require 'git_stats/core_extensions/symbol'
|
|
5
|
+
|
|
6
|
+
require 'git_stats/version'
|
|
7
|
+
require 'git_stats/i18n'
|
|
8
|
+
require 'git_stats/by_field_finder'
|
|
9
|
+
require 'git_stats/cli'
|
|
10
|
+
require 'git_stats/generator'
|
|
11
|
+
|
|
12
|
+
require 'git_stats/git_data/activity'
|
|
13
|
+
require 'git_stats/git_data/author'
|
|
14
|
+
require 'git_stats/git_data/blob'
|
|
15
|
+
require 'git_stats/git_data/command_parser'
|
|
16
|
+
require 'git_stats/git_data/command_runner'
|
|
17
|
+
require 'git_stats/git_data/commit'
|
|
18
|
+
require 'git_stats/git_data/repo'
|
|
19
|
+
require 'git_stats/git_data/short_stat'
|
|
20
|
+
|
|
21
|
+
require 'git_stats/stats_view/template'
|
|
22
|
+
require 'git_stats/stats_view/view'
|
|
23
|
+
require 'git_stats/stats_view/view_data'
|
|
24
|
+
|
|
25
|
+
require 'git_stats/stats_view/charts/activity_charts'
|
|
26
|
+
require 'git_stats/stats_view/charts/authors_charts'
|
|
27
|
+
require 'git_stats/stats_view/charts/chart'
|
|
28
|
+
require 'git_stats/stats_view/charts/charts'
|
|
29
|
+
require 'git_stats/stats_view/charts/repo_charts'
|
|
30
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
require "git_stats"
|
|
3
|
+
|
|
4
|
+
class GitStats::CLI
|
|
5
|
+
|
|
6
|
+
def start(*args)
|
|
7
|
+
raise "Wrong number of arguments\nUsage: git_stats repo_path output_path <output_lang>" unless [2, 3].include? args.size
|
|
8
|
+
|
|
9
|
+
repo_path, out_path, lang = args
|
|
10
|
+
I18n.locale = lang || :en
|
|
11
|
+
|
|
12
|
+
GitStats::Generator.new(repo_path, out_path) { |g|
|
|
13
|
+
g.add_command_observer { |command, result| puts "#{command}" }
|
|
14
|
+
}.render_all
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
class Hash
|
|
3
|
+
def to_key_indexed_array(params = {})
|
|
4
|
+
raise ArgumentError.new('all the keys must be numbers to convert to key indexed array') unless all? { |k, v| k.is_a? Numeric }
|
|
5
|
+
min_size = params[:min_size] || 0
|
|
6
|
+
default = params[:default]
|
|
7
|
+
inject(Array.new(min_size, default)) { |acc, (k, v)| acc[k] = v; acc }.map { |e| e || default }
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
module GitStats
|
|
3
|
+
class Generator
|
|
4
|
+
delegate :add_command_observer, to: :@repo
|
|
5
|
+
delegate :render_all, to: :@view
|
|
6
|
+
|
|
7
|
+
def initialize(repo_path, out_path)
|
|
8
|
+
validate_paths(repo_path, out_path)
|
|
9
|
+
|
|
10
|
+
@repo = GitData::Repo.new(path: repo_path)
|
|
11
|
+
view_data = StatsView::ViewData.new(@repo)
|
|
12
|
+
@view = StatsView::View.new(view_data, out_path)
|
|
13
|
+
|
|
14
|
+
yield self if block_given?
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
def validate_paths(repo_path, out_path)
|
|
19
|
+
validate_repo_path(repo_path)
|
|
20
|
+
validate_out_path(out_path)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def validate_repo_path(repo_path)
|
|
24
|
+
raise ArgumentError, "#{repo_path} is not a git repository" unless Dir.exists?("#{repo_path}/.git")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def validate_out_path(out_path)
|
|
28
|
+
raise ArgumentError, "#{out_path} is not a directory" unless Dir.exists?(out_path)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
module GitStats
|
|
3
|
+
module GitData
|
|
4
|
+
class Activity
|
|
5
|
+
|
|
6
|
+
def initialize(commits)
|
|
7
|
+
add_commits(commits)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def by_date
|
|
11
|
+
@by_date ||= default_hash
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def by_hour
|
|
15
|
+
@by_hour ||= default_hash
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def by_hour_array
|
|
19
|
+
by_hour.to_key_indexed_array(min_size: 24, default: 0)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def by_wday
|
|
23
|
+
@by_wday ||= default_hash
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def by_wday_array
|
|
27
|
+
by_wday.to_key_indexed_array(min_size: 7, default: 0)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def by_wday_hour
|
|
31
|
+
@by_wday_hour ||= default_double_hash
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def by_month
|
|
35
|
+
@by_month ||= default_hash
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def by_month_array
|
|
39
|
+
by_month.to_key_indexed_array(min_size: 13, default: 0)[1..-1]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def by_year
|
|
43
|
+
@by_year ||= default_hash
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def by_year_month
|
|
47
|
+
@by_year_week ||= default_double_hash
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
def add_commits(commits)
|
|
52
|
+
commits = commits.values if commits.is_a? Hash
|
|
53
|
+
commits.each do |commit|
|
|
54
|
+
add_commit_at(commit.date)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def add_commit_at(date)
|
|
59
|
+
self.by_date[date] += 1
|
|
60
|
+
self.by_hour[date.hour] += 1
|
|
61
|
+
self.by_wday[date.wday] += 1
|
|
62
|
+
self.by_wday_hour[date.wday][date.hour] += 1
|
|
63
|
+
self.by_month[date.month] += 1
|
|
64
|
+
self.by_year[date.year] += 1
|
|
65
|
+
self.by_year_month[date.year][date.month] += 1
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def default_hash
|
|
69
|
+
Hash.new(0)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def default_double_hash
|
|
73
|
+
Hash.new { |h, k| h[k] = Hash.new(0) }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
require 'git_stats/hash_initializable'
|
|
3
|
+
|
|
4
|
+
module GitStats
|
|
5
|
+
module GitData
|
|
6
|
+
class Author
|
|
7
|
+
include HashInitializable
|
|
8
|
+
|
|
9
|
+
attr_reader :repo, :name, :email
|
|
10
|
+
|
|
11
|
+
def commits
|
|
12
|
+
@commits ||= repo.commits.select { |commit| commit.author == self }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def changed_lines
|
|
16
|
+
insertions + deletions
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def insertions
|
|
20
|
+
short_stats.map(&:insertions).sum
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def deletions
|
|
24
|
+
short_stats.map(&:deletions).sum
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def commits_sum_by_date
|
|
28
|
+
sum = 0
|
|
29
|
+
commits.map { |commit|
|
|
30
|
+
sum += 1
|
|
31
|
+
[commit.date, sum]
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
[:insertions, :deletions, :changed_lines].each do |method|
|
|
36
|
+
define_method "#{method}_by_date" do
|
|
37
|
+
sum = 0
|
|
38
|
+
commits.map { |commit|
|
|
39
|
+
sum += commit.short_stat.send(method)
|
|
40
|
+
[commit.date, sum]
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def short_stats
|
|
46
|
+
commits.map(&:short_stat)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def activity
|
|
50
|
+
@activity ||= Activity.new(commits)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def dirname
|
|
54
|
+
@name.underscore.split.join '_'
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def to_s
|
|
58
|
+
"#{self.class} #@name <#@email>"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def ==(other)
|
|
62
|
+
[self.repo, self.name, self.email] == [other.repo, other.name, other.email]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'git_stats/hash_initializable'
|
|
4
|
+
|
|
5
|
+
module GitStats
|
|
6
|
+
module GitData
|
|
7
|
+
class Blob
|
|
8
|
+
include HashInitializable
|
|
9
|
+
|
|
10
|
+
attr_reader :repo, :sha, :filename
|
|
11
|
+
|
|
12
|
+
def lines_count
|
|
13
|
+
@lines_count ||= binary? ? 0 : repo.run("git cat-file blob #{self.sha} | wc -l").to_i
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def content
|
|
17
|
+
@content ||= repo.run("git cat-file blob #{self.sha}")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def extension
|
|
21
|
+
@ext ||= File.extname(filename)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def binary?
|
|
25
|
+
repo.run("git cat-file blob #{self.sha} | grep -m 1 '^'") =~ /Binary file/
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def to_s
|
|
29
|
+
"#{self.class} #@sha #@filename"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def ==(other)
|
|
33
|
+
[self.repo, self.sha, self.filename] == [other.repo, other.sha, other.filename]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
module GitStats
|
|
3
|
+
module GitData
|
|
4
|
+
class CommandParser
|
|
5
|
+
def parse(command, result)
|
|
6
|
+
cmd, params = command.scan(/git (.*) (.*)/).first.map(&:split).flatten
|
|
7
|
+
send("parse_#{cmd.underscore}", result, params)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def parse_shortlog(result, params)
|
|
11
|
+
result.lines.map do |line|
|
|
12
|
+
commits, name, email = line.scan(/(.*)\t(.*)<(.*)>/).first.map(&:strip)
|
|
13
|
+
{commits: commits.to_i, name: name, email: email}
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def parse_ls_tree(result, params)
|
|
18
|
+
result.lines.map do |line|
|
|
19
|
+
mode, type, sha, filename = line.scan(/(.*) (.*) (.*)\t(.*)/).first.map(&:strip)
|
|
20
|
+
{mode: mode, type: type, sha: sha, filename: filename}
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def parse_rev_list(result, params)
|
|
25
|
+
result.lines.map do |line|
|
|
26
|
+
sha, stamp, date, author_email = line.split('|').map(&:strip)
|
|
27
|
+
{sha: sha, stamp: stamp, date: date, author_email: author_email}
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
require 'git_stats/hash_initializable'
|
|
3
|
+
|
|
4
|
+
module GitStats
|
|
5
|
+
module GitData
|
|
6
|
+
class Commit
|
|
7
|
+
include HashInitializable
|
|
8
|
+
|
|
9
|
+
attr_reader :repo, :sha, :stamp, :date, :author
|
|
10
|
+
|
|
11
|
+
def files
|
|
12
|
+
@files ||= repo.run_and_parse("git ls-tree -r #{self.sha}").map do |file|
|
|
13
|
+
Blob.new(repo: repo, filename: file[:filename], sha: file[:sha])
|
|
14
|
+
end.extend(ByFieldFinder)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def binary_files
|
|
18
|
+
@binary_files ||= files.select { |f| f.binary? }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def text_files
|
|
22
|
+
@text_files ||= files - binary_files
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def files_by_extension
|
|
26
|
+
@files_by_extension ||= files.inject({}) { |acc, f| acc[f.extension] ||= []; acc[f.extension] << f; acc }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def files_by_extension_count
|
|
30
|
+
@files_by_extension_count ||= Hash[files_by_extension.map { |ext, files| [ext, files.count] }]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def lines_by_extension
|
|
34
|
+
@lines_by_extension ||= Hash[files_by_extension.map { |ext, files|
|
|
35
|
+
[ext, files.map(&:lines_count).sum]
|
|
36
|
+
}.delete_if { |ext, lines_count| lines_count == 0 }]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def files_count
|
|
40
|
+
@files_count ||= repo.run("git ls-tree -r --name-only #{self.sha} | wc -l").to_i
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def lines_count
|
|
44
|
+
@lines_count ||= repo.run("git diff --shortstat `git hash-object -t tree /dev/null` #{self.sha}").lines.map do |line|
|
|
45
|
+
line[/(\d+) insertions?/, 1].to_i
|
|
46
|
+
end.sum
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def short_stat
|
|
50
|
+
@short_stat ||= ShortStat.new(self)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def to_s
|
|
54
|
+
"#{self.class} #@sha"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def ==(other)
|
|
58
|
+
[self.repo, self.sha, self.stamp, self.date, self.author] ==
|
|
59
|
+
[other.repo, other.sha, other.stamp, other.date, other.author]
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|