git_stats 1.0.11 → 1.0.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.gitmodules +3 -0
  3. data/README.md +16 -10
  4. data/config/locales/de.yml +2 -0
  5. data/config/locales/en.yml +4 -1
  6. data/lib/git_stats/base.rb +3 -1
  7. data/lib/git_stats/cli.rb +5 -3
  8. data/lib/git_stats/core_extensions/enumerable.rb +10 -0
  9. data/lib/git_stats/generator.rb +4 -4
  10. data/lib/git_stats/git_data/comment_stat.rb +38 -0
  11. data/lib/git_stats/git_data/commit.rb +8 -4
  12. data/lib/git_stats/git_data/repo.rb +46 -13
  13. data/lib/git_stats/git_data/short_stat.rb +1 -1
  14. data/lib/git_stats/git_data/tree.rb +23 -0
  15. data/lib/git_stats/i18n.rb +1 -0
  16. data/lib/git_stats/stats_view/charts/authors_charts.rb +6 -4
  17. data/lib/git_stats/stats_view/charts/charts.rb +1 -1
  18. data/lib/git_stats/stats_view/charts/repo_charts.rb +10 -0
  19. data/lib/git_stats/stats_view/view.rb +2 -1
  20. data/lib/git_stats/version.rb +1 -1
  21. data/spec/factories.rb +8 -0
  22. data/spec/git_data/commit_range_spec.rb +2 -2
  23. data/spec/git_data/commit_spec.rb +1 -1
  24. data/spec/git_data/generator_spec.rb +5 -7
  25. data/spec/git_data/repo_spec.rb +5 -5
  26. data/spec/git_data/short_stat_spec.rb +1 -1
  27. data/spec/git_data/tree_spec.rb +41 -0
  28. data/spec/integration/author_spec.rb +2 -2
  29. data/spec/integration/file_spec.rb +2 -2
  30. data/spec/integration/shared.rb +91 -0
  31. data/spec/integration/tree_spec.rb +197 -0
  32. data/templates/author_details/_author_details.haml +4 -4
  33. data/templates/authors/_authors.haml +5 -5
  34. data/templates/comments/_comments.haml +11 -0
  35. data/templates/comments/by_date.haml +1 -0
  36. data/templates/general.haml +3 -0
  37. metadata +31 -25
  38. data/lib/git_stats/by_field_finder.rb +0 -7
  39. data/spec/by_field_finder_spec.rb +0 -25
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9f3520a3ad6f9c046a9b1764cedcf337dfadd229
4
- data.tar.gz: 8b48cb4ea3d79e15bc0bb2ecf67df7b67f5ed61a
3
+ metadata.gz: 2fdf7c3f12067e71e3eddbd26ece408572f6d0bf
4
+ data.tar.gz: 79b20aeae19c213893f447432a34bd81bb89880e
5
5
  SHA512:
6
- metadata.gz: 7decc1f75bd9778ddcb96d6c76c2751c92db8d7a88e04827ad7e4073476f2e2ac18b8136512253833160d837593994ed485e831da62ef3b32c5a329d348e2754
7
- data.tar.gz: c31d7266352df2493e81d6a93ecb0f0a1785e281c708f170fe5843c0b2e9230ee259feb5a4383206ff56822040550e1c73c0226170e2eff4f5594b6fc17821ca
6
+ metadata.gz: 3286e2ccc323720aa0532f0112d77ff103136646ac14f64e425c7787e3c9b67bc91eee7e0f23e22cfe15a0d0ca02796a61f9e7aa8fab0fae5cc23f4aa9194a7d
7
+ data.tar.gz: 87b139080a6f5420c7267cc858d73ba4494c745e40b60e599fe2ac2f763a615b08e28d67f89c235c7f85bad73a6b3891ec3148c7b3542035fedf7129edb4d1bb
@@ -1,3 +1,6 @@
1
1
  [submodule "spec/integration/test_repo"]
2
2
  path = spec/integration/test_repo
3
3
  url = git://github.com/tomgi/git_stats_test_repo.git
4
+ [submodule "spec/integration/test_repo_tree"]
5
+ path = spec/integration/test_repo_tree
6
+ url = git://github.com/irevert/git_stats_test_repo_tree
data/README.md CHANGED
@@ -34,16 +34,22 @@ It browses the repository and outputs html page with statistics.
34
34
  git_stats generate
35
35
 
36
36
  Options:
37
- p, [--path=PATH] # Path to repository from which statistics should be generated.
38
- # Default: .
39
- o, [--output=OUTPUT] # Output path where statistics should be written.
40
- # Default: ./git_stats
41
- l, [--language=LANGUAGE] # Language of written statistics.
42
- # Default: en
43
- f, [--from=FROM] # Commit from where statistics should start.
44
- t, [--to=TO] # Commit where statistics should stop.
45
- # Default: HEAD
46
- s, [--silent] # Silent mode. Don't output anything.
37
+ p, [--path=PATH] # Path to repository from which statistics should be generated.
38
+ # Default: .
39
+ o, [--out-path=OUT_PATH] # Output path where statistics should be written.
40
+ # Default: ./git_stats
41
+ l, [--language=LANGUAGE] # Language of written statistics.
42
+ # Default: en
43
+ f, [--from=FROM] # Commit from where statistics should start.
44
+ t, [--to=TO] # Commit where statistics should stop.
45
+ # Default: HEAD
46
+ s, [--silent], [--no-silent] # Silent mode. Don't output anything.
47
+ d, [--tree=TREE] # Tree where statistics should be generated.
48
+ # Default: .
49
+ c, [--comment=COMMENT] # The string which is used for comments.
50
+ # Default: //
51
+
52
+
47
53
 
48
54
  #### Start generator with default settings
49
55
 
@@ -12,8 +12,10 @@ de:
12
12
  commits_by_hour: Commits in der Stunde
13
13
  files: Dateien
14
14
  lines: Zeilen
15
+ comments: Kommentare
15
16
  files_by_date: Dateien nach Datum
16
17
  lines_by_date: Zeilen nach Datum
18
+ comments_by_date: Kommentare nach Datum
17
19
  files_by_extension: Dateien nach Erweiterungen
18
20
  lines_by_extension: Zeilen nach Erweiterungen
19
21
  hour_of_day: Tagesstunden
@@ -1,6 +1,7 @@
1
1
  en:
2
2
  project_name: Project name
3
3
  project_version: Project version
4
+ tree_path: Tree path
4
5
  generated_at: Generated at
5
6
  generator: Generator
6
7
  report_period: Report period
@@ -12,8 +13,10 @@ en:
12
13
  commits_by_hour: Commits by hour
13
14
  files: Files
14
15
  lines: Lines
16
+ comments: Comments
15
17
  files_by_date: Files by date
16
18
  lines_by_date: Lines by date
19
+ comments_by_date: Comments by date
17
20
  files_by_extension: Files by extension
18
21
  lines_by_extension: Lines by extension
19
22
  hour_of_day: Hour of day
@@ -57,4 +60,4 @@ en:
57
60
  details: Details
58
61
  insertions_by_date: Lines added by date
59
62
  deletions_by_date: Lines deleted by date
60
- changed_lines_by_date: Changed lines by date
63
+ changed_lines_by_date: Changed lines by date
@@ -2,10 +2,10 @@
2
2
  require 'git_stats/core_extensions/hash'
3
3
  require 'git_stats/core_extensions/string'
4
4
  require 'git_stats/core_extensions/symbol'
5
+ require 'git_stats/core_extensions/enumerable'
5
6
 
6
7
  require 'git_stats/version'
7
8
  require 'git_stats/i18n'
8
- require 'git_stats/by_field_finder'
9
9
  require 'git_stats/cli'
10
10
  require 'git_stats/generator'
11
11
  require 'git_stats/validator'
@@ -18,6 +18,8 @@ require 'git_stats/git_data/command_runner'
18
18
  require 'git_stats/git_data/commit'
19
19
  require 'git_stats/git_data/repo'
20
20
  require 'git_stats/git_data/short_stat'
21
+ require 'git_stats/git_data/comment_stat'
22
+ require 'git_stats/git_data/tree'
21
23
 
22
24
  require 'git_stats/stats_view/template'
23
25
  require 'git_stats/stats_view/view'
@@ -4,16 +4,18 @@ require "thor"
4
4
 
5
5
  class GitStats::CLI < Thor
6
6
  option :path, :aliases => :p, :default => '.', :desc => 'Path to repository from which statistics should be generated.'
7
- option :output, :aliases => :o, :default => './git_stats', :desc => 'Output path where statistics should be written.'
7
+ option :out_path, :aliases => :o, :default => './git_stats', :desc => 'Output path where statistics should be written.'
8
8
  option :language, :aliases => :l, :default => 'en', :desc => 'Language of written statistics.'
9
9
  option :from, :aliases => :f, :desc => 'Commit from where statistics should start.'
10
10
  option :to, :aliases => :t, :default => 'HEAD', :desc => 'Commit where statistics should stop.'
11
11
  option :silent, :aliases => :s, :type => :boolean, :desc => 'Silent mode. Don\'t output anything.'
12
-
12
+ option :tree, :aliases => :d, :default => '.', :desc => 'Tree where statistics should be generated.'
13
+ option :comment, :aliases => :c, :default => '//', :desc => 'The string which is used for comments.'
14
+
13
15
  desc 'generate', 'Generates the statistics of a repository'
14
16
  def generate
15
17
  I18n.locale = options[:language]
16
- GitStats::Generator.new(options[:path], options[:output], options[:from], options[:to]) { |g|
18
+ GitStats::Generator.new(options) { |g|
17
19
  g.add_command_observer { |command, result| puts "#{command}" } unless options[:silent]
18
20
  }.render_all
19
21
  end
@@ -0,0 +1,10 @@
1
+ module Enumerable
2
+ def first!(&block)
3
+ matching = find(&block)
4
+ if matching.nil?
5
+ raise "Sequence contains no matching elements"
6
+ else
7
+ matching
8
+ end
9
+ end
10
+ end
@@ -4,12 +4,12 @@ module GitStats
4
4
  delegate :add_command_observer, to: :@repo
5
5
  delegate :render_all, to: :@view
6
6
 
7
- def initialize(repo_path, out_path, first_commit_sha = nil, last_commit_sha = "HEAD")
8
- validate_repo_path(repo_path)
7
+ def initialize(options)
8
+ validate_repo_path(options[:path])
9
9
 
10
- @repo = GitData::Repo.new(path: repo_path, first_commit_sha: first_commit_sha, last_commit_sha: last_commit_sha)
10
+ @repo = GitData::Repo.new(options)
11
11
  view_data = StatsView::ViewData.new(@repo)
12
- @view = StatsView::View.new(view_data, out_path)
12
+ @view = StatsView::View.new(view_data, options[:out_path])
13
13
 
14
14
  yield self if block_given?
15
15
  end
@@ -0,0 +1,38 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module GitStats
3
+ module GitData
4
+ class CommentStat
5
+ attr_reader :commit, :insertions, :deletions
6
+
7
+ def initialize(commit)
8
+ @commit = commit
9
+ calculate_stat
10
+ end
11
+
12
+ def changed_lines
13
+ insertions + deletions
14
+ end
15
+
16
+ def to_s
17
+ "#{self.class} #@commit"
18
+ end
19
+
20
+ def escape_characters_in_string(string)
21
+ pattern = /(\'|\"|\.|\*|\/|\-|\\)/
22
+ string.gsub(pattern){|match|"\\" + match}
23
+ end
24
+
25
+ private
26
+ def calculate_stat
27
+ escaped_string = escape_characters_in_string(commit.repo.comment_string)
28
+ stat_line = commit.repo.run("git show #{commit.sha} | awk 'BEGIN {adds=0; dels=0} {if ($0 ~ /^\\+#{escaped_string}/) adds++; if ($0 ~ /^\-#{escaped_string}/) dels++} END {print adds \" insertions \" dels \" deletes\"}'").lines.to_a[0]
29
+ if stat_line.blank?
30
+ @insertions = @deletions = 0
31
+ else
32
+ @insertions = stat_line[/(\d+) insertions?/, 1].to_i
33
+ @deletions = stat_line[/(\d+) deletes?/, 1].to_i
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -9,9 +9,9 @@ module GitStats
9
9
  attr_reader :repo, :sha, :stamp, :date, :author
10
10
 
11
11
  def files
12
- @files ||= repo.run_and_parse("git ls-tree -r #{self.sha}").map do |file|
12
+ @files ||= repo.run_and_parse("git ls-tree -r #{self.sha} -- #{repo.tree_path}").map do |file|
13
13
  Blob.new(repo: repo, filename: file[:filename], sha: file[:sha])
14
- end.extend(ByFieldFinder)
14
+ end
15
15
  end
16
16
 
17
17
  def binary_files
@@ -37,11 +37,11 @@ module GitStats
37
37
  end
38
38
 
39
39
  def files_count
40
- @files_count ||= repo.run("git ls-tree -r --name-only #{self.sha} | wc -l").to_i
40
+ @files_count ||= repo.run("git ls-tree -r --name-only #{self.sha} -- #{repo.tree_path}| wc -l").to_i
41
41
  end
42
42
 
43
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|
44
+ @lines_count ||= repo.run("git diff --shortstat `git hash-object -t tree /dev/null` #{self.sha} -- #{repo.tree_path}").lines.map do |line|
45
45
  line[/(\d+) insertions?/, 1].to_i
46
46
  end.sum
47
47
  end
@@ -50,6 +50,10 @@ module GitStats
50
50
  @short_stat ||= ShortStat.new(self)
51
51
  end
52
52
 
53
+ def comment_stat
54
+ @comment_stat ||= CommentStat.new(self)
55
+ end
56
+
53
57
  def to_s
54
58
  "#{self.class} #@sha"
55
59
  end
@@ -6,32 +6,51 @@ module GitStats
6
6
  class Repo
7
7
  include HashInitializable
8
8
 
9
- attr_reader :path, :first_commit_sha, :last_commit_sha
10
-
11
9
  delegate :files, :files_by_extension, :files_by_extension_count, :lines_by_extension,
12
- :files_count, :binary_files, :text_files, :lines_count, to: :last_commit
10
+ :files_count, :binary_files, :text_files, :lines_count, :comments_count, to: :last_commit
13
11
 
14
12
  def initialize(params)
15
13
  super(params)
16
14
  @path = File.expand_path(@path)
15
+ @tree_path ||= "."
16
+ end
17
+
18
+ def path
19
+ @path ||= '.'
20
+ end
21
+
22
+ def first_commit_sha
23
+ @first_commit_sha
24
+ end
25
+
26
+ def last_commit_sha
27
+ @last_commit_sha ||= 'HEAD'
28
+ end
29
+
30
+ def tree_path
31
+ @tree_path ||= '.'
32
+ end
33
+
34
+ def comment_string
35
+ @comment_string ||= '//'
17
36
  end
18
37
 
19
38
  def authors
20
- @authors ||= run_and_parse("git shortlog -se #{commit_range}").map do |author|
39
+ @authors ||= run_and_parse("git shortlog -se #{commit_range} #{tree_path}").map do |author|
21
40
  Author.new(repo: self, name: author[:name], email: author[:email])
22
- end.extend(ByFieldFinder)
41
+ end
23
42
  end
24
43
 
25
44
  def commits
26
- @commits ||= run_and_parse("git rev-list --pretty=format:'%h|%at|%ai|%aE' #{commit_range} | grep -v commit").map do |commit_line|
45
+ @commits ||= run_and_parse("git rev-list --pretty=format:'%h|%at|%ai|%aE' #{commit_range} #{tree_path} | grep -v commit").map do |commit_line|
27
46
  Commit.new(
28
47
  repo: self,
29
48
  sha: commit_line[:sha],
30
49
  stamp: commit_line[:stamp],
31
50
  date: DateTime.parse(commit_line[:date]),
32
- author: authors.by_email(commit_line[:author_email])
51
+ author: authors.first! { |a| a.email == commit_line[:author_email] }
33
52
  )
34
- end.sort_by! { |e| e.date }.extend(ByFieldFinder)
53
+ end.sort_by! { |e| e.date }
35
54
  end
36
55
 
37
56
  def commits_period
@@ -63,6 +82,15 @@ module GitStats
63
82
  }]
64
83
  end
65
84
 
85
+ def comments_count_by_date
86
+ sum = 0
87
+ @comment_count_each_day ||= Hash[commits.map { |commit|
88
+ sum += commit.comment_stat.insertions
89
+ sum -= commit.comment_stat.deletions
90
+ [commit.date.to_date, sum]
91
+ }].fill_empty_days!(aggregated: true)
92
+ end
93
+
66
94
  def last_commit
67
95
  commits.last
68
96
  end
@@ -71,14 +99,14 @@ module GitStats
71
99
  @first_commit_sha.blank? ? last_commit_sha : "#@first_commit_sha..#{last_commit_sha}"
72
100
  end
73
101
 
74
- def last_commit_sha
75
- @last_commit_sha ||= 'HEAD'
76
- end
77
-
78
102
  def short_stats
79
103
  @short_stats ||= commits.map(&:short_stat)
80
104
  end
81
105
 
106
+ def comment_stats
107
+ @comment_stats ||= commits.map(&:comment_stat)
108
+ end
109
+
82
110
  def activity
83
111
  @activity ||= Activity.new(commits)
84
112
  end
@@ -87,8 +115,12 @@ module GitStats
87
115
  @project_version ||= run("git rev-parse --short #{commit_range}").strip
88
116
  end
89
117
 
118
+ def tree
119
+ @tree ||= Tree.new(repo: self, relative_path: @tree_path)
120
+ end
121
+
90
122
  def project_name
91
- @project_name ||= File.basename(path)
123
+ @project_name ||= (File.expand_path(File.join(path, tree_path)).sub(File.dirname(File.expand_path(path))+File::SEPARATOR,"") || File.basename(path))
92
124
  end
93
125
 
94
126
  def run(command)
@@ -132,6 +164,7 @@ module GitStats
132
164
  command_observers.each { |o| o.call(command, result) }
133
165
  end
134
166
 
167
+
135
168
  end
136
169
  end
137
170
  end
@@ -19,7 +19,7 @@ module GitStats
19
19
 
20
20
  private
21
21
  def calculate_stat
22
- stat_line = commit.repo.run("git show --shortstat --oneline #{commit.sha}").lines.to_a[1]
22
+ stat_line = commit.repo.run("git show --shortstat --oneline #{commit.sha} -- #{commit.repo.tree_path}").lines.to_a[1]
23
23
  if stat_line.blank?
24
24
  @files_changed = @insertions = @deletions = 0
25
25
  else
@@ -0,0 +1,23 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'git_stats/hash_initializable'
3
+
4
+ module GitStats
5
+ module GitData
6
+ class Tree
7
+ include HashInitializable
8
+
9
+ attr_reader :repo, :relative_path
10
+
11
+ def authors
12
+ @authors ||= run_and_parse("git shortlog -se #{commit_range}").map do |author|
13
+ Author.new(repo: self, name: author[:name], email: author[:email])
14
+ end
15
+ end
16
+
17
+ def ==(other)
18
+ ((self.repo == other.repo) && (self.relative_path == other.relative_path))
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -1,2 +1,3 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  I18n.load_path += Dir['../../../../config/locales/*.yml'.absolute_path]
3
+ I18n.enforce_available_locales = true
@@ -3,14 +3,16 @@ module GitStats
3
3
  module StatsView
4
4
  module Charts
5
5
  class AuthorsCharts
6
+ AUTHORS_ON_CHART_LIMIT = 4
7
+
6
8
  def initialize(authors)
7
9
  @authors = authors
8
10
  end
9
11
 
10
- def commits_sum_by_author_by_date(limit = 4, authors = nil)
12
+ def commits_sum_by_author_by_date(authors = nil)
11
13
  Chart.new do |f|
12
14
  f.multi_date_chart(
13
- data: (authors || @authors.sort_by { |author| -author.commits.size }[0..limit]).map { |author| {name: author.name, data: author.commits_sum_by_date} },
15
+ data: (authors || @authors.sort_by { |author| -author.commits.size }[0..AUTHORS_ON_CHART_LIMIT]).map { |author| {name: author.name, data: author.commits_sum_by_date} },
14
16
  title: :lines_by_date.t,
15
17
  y_text: :lines.t
16
18
  )
@@ -18,10 +20,10 @@ module GitStats
18
20
  end
19
21
 
20
22
  [:insertions, :deletions, :changed_lines].each do |method|
21
- define_method "#{method}_by_author_by_date" do |limit = 4, authors = nil|
23
+ define_method "#{method}_by_author_by_date" do |authors = nil|
22
24
  Chart.new do |f|
23
25
  f.multi_date_chart(
24
- data: (authors || @authors.sort_by { |author| -author.send(method) }[0..limit]).map { |author| {name: author.name, data: author.send("#{method}_by_date")} },
26
+ data: (authors || @authors.sort_by { |author| -author.send(method) }[0..AUTHORS_ON_CHART_LIMIT]).map { |author| {name: author.name, data: author.send("#{method}_by_date")} },
25
27
  title: :lines_by_date.t,
26
28
  y_text: :lines.t
27
29
  )
@@ -3,7 +3,7 @@ module GitStats
3
3
  module StatsView
4
4
  module Charts
5
5
  class All
6
- delegate :files_by_extension, :lines_by_extension, :files_by_date, :lines_by_date, to: :repo_charts
6
+ delegate :files_by_extension, :lines_by_extension, :files_by_date, :lines_by_date, :comments_by_date, to: :repo_charts
7
7
 
8
8
  delegate :commits_sum_by_author_by_date, :changed_lines_by_author_by_date,
9
9
  :insertions_by_author_by_date, :deletions_by_author_by_date, to: :authors_charts
@@ -47,6 +47,16 @@ module GitStats
47
47
  end
48
48
  end
49
49
 
50
+ def comments_by_date
51
+ Chart.new do |f|
52
+ f.date_chart(
53
+ data: @repo.comments_count_by_date,
54
+ title: :comments_by_date.t,
55
+ y_text: :comments.t
56
+ )
57
+ end
58
+ end
59
+
50
60
  end
51
61
  end
52
62
  end