gitalytics 1.3.0 → 1.3.1
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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +0 -1
- data/CHANGELOG.md +11 -0
- data/Gemfile +1 -0
- data/README.md +10 -2
- data/Rakefile +1 -0
- data/assets/_authors.html.haml +50 -0
- data/assets/_commits.html.haml +21 -0
- data/assets/_css.html.haml +190 -0
- data/assets/_dashboard.html.haml +45 -0
- data/assets/_dates.html.haml +24 -0
- data/assets/_javascript.html.haml +44 -0
- data/assets/gitalytics.html.haml +63 -0
- data/bin/gitalytics +16 -7
- data/gitalytics.gemspec +14 -12
- data/lib/gitalytics.rb +27 -22
- data/lib/gitalytics/commit.rb +3 -5
- data/lib/gitalytics/gitlog.rb +22 -25
- data/lib/gitalytics/user.rb +11 -11
- data/lib/gitalytics/version.rb +1 -1
- data/lib/haml_helper.rb +8 -0
- data/test/files/block1 +4 -0
- data/test/files/block2 +4 -0
- data/test/files/block3 +7 -0
- data/test/files/git_log +15 -0
- data/test/lib/test_commit.rb +5 -16
- data/test/lib/test_gitlog.rb +25 -50
- data/test/lib/test_user.rb +33 -22
- metadata +49 -6
- data/assets/gitalytics.html.erb +0 -520
@@ -0,0 +1,63 @@
|
|
1
|
+
- require 'haml_helper'
|
2
|
+
!!!
|
3
|
+
%html{lang: "en"}
|
4
|
+
%head
|
5
|
+
%meta{content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
|
6
|
+
%meta{charset: "utf-8"}/
|
7
|
+
%meta{content: "IE=edge", "http-equiv" => "X-UA-Compatible"}/
|
8
|
+
%meta{content: "width=device-width, initial-scale=1.0", name: "viewport"}/
|
9
|
+
%meta{content: "Gitalytics Report", name: "description"}/
|
10
|
+
%meta{content: "Gonzalo Robaina", name: "author"}/
|
11
|
+
%title Gitalytics report
|
12
|
+
/ Bootstrap core CSS
|
13
|
+
%link{crossorigin: "anonymous", href: "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css", integrity: "sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7", rel: "stylesheet"}/
|
14
|
+
%link{crossorigin: "anonymous", href: "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css", integrity: "sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r", rel: "stylesheet"}/
|
15
|
+
/ HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries
|
16
|
+
/[if lt IE 9]
|
17
|
+
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
|
18
|
+
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
|
19
|
+
= render :css
|
20
|
+
%body
|
21
|
+
/ Wrap all page content here
|
22
|
+
#wrap
|
23
|
+
/ Fixed navbar
|
24
|
+
.navbar.navbar-default.navbar-fixed-top{role: "navigation"}
|
25
|
+
.container
|
26
|
+
.navbar-header
|
27
|
+
%button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", type: "button"}
|
28
|
+
%span.sr-only Toggle navigation
|
29
|
+
%span.icon-bar
|
30
|
+
%span.icon-bar
|
31
|
+
%span.icon-bar
|
32
|
+
%a.navbar-brand{href: "https://rubygems.org/gems/gitalytics"} Gitalytics
|
33
|
+
.collapse.navbar-collapse
|
34
|
+
%ul.nav.navbar-nav
|
35
|
+
%li.active
|
36
|
+
%a{data: {toggle: :tab}, href: "#dashboard"} Dashboard
|
37
|
+
%li
|
38
|
+
%a{data: {toggle: :tab}, href: "#authors"} Authors
|
39
|
+
%li
|
40
|
+
%a{data: {toggle: :tab}, href: "#commits"} Commits
|
41
|
+
%li
|
42
|
+
%a{data: {toggle: :tab}, href: "#dates"} Dates
|
43
|
+
/ <li><a href="#contact">Contact</a></li>
|
44
|
+
/ /.nav-collapse
|
45
|
+
/ Begin page content
|
46
|
+
.container
|
47
|
+
.tab-content
|
48
|
+
= render :dashboard, users: @users, commits: @commits
|
49
|
+
= render :authors, users: @users
|
50
|
+
= render :commits, commits: @commits
|
51
|
+
= render :dates, commits: @commits
|
52
|
+
#footer
|
53
|
+
.container
|
54
|
+
%p.text-muted
|
55
|
+
Gitalytics v#{Gitalytics::VERSION} by
|
56
|
+
%a{href: "http://gonzalo.robaina.me"} Gonzalo Robaina
|
57
|
+
/
|
58
|
+
External JavaScript Files
|
59
|
+
\==================================================
|
60
|
+
%script{src: "https://code.jquery.com/jquery-2.2.4.min.js"}
|
61
|
+
%script{crossorigin: "anonymous", integrity: "sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS", src: "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"}
|
62
|
+
%script{src: "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.6/Chart.min.js"}
|
63
|
+
= render :javascript, { users: @users, weekday_commits: @weekday_commits }
|
data/bin/gitalytics
CHANGED
@@ -4,37 +4,46 @@
|
|
4
4
|
Encoding.default_external = Encoding::UTF_8
|
5
5
|
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
|
6
6
|
|
7
|
+
require 'benchmark'
|
7
8
|
require 'optparse'
|
8
9
|
require 'gitalytics'
|
9
10
|
|
10
11
|
options = {
|
12
|
+
benchmark: false,
|
11
13
|
browser: false,
|
12
14
|
format: 'cli',
|
13
15
|
group_by: 'name'
|
14
16
|
}
|
15
17
|
|
16
18
|
OptionParser.new do |opts|
|
17
|
-
opts.banner =
|
19
|
+
opts.banner = 'Usage: gitalytics [options]'
|
18
20
|
|
19
|
-
opts.on(
|
21
|
+
opts.on('-v', '--version', 'Display gem version') do
|
20
22
|
p "Gitalytics #{Gitalytics::VERSION}"
|
21
23
|
exit
|
22
24
|
end
|
23
25
|
|
24
|
-
opts.on(
|
26
|
+
opts.on('-h', '--html', 'Outputs html report and displays it in default web browser') do
|
25
27
|
options[:format] = 'html'
|
26
28
|
options[:browser] = true
|
27
29
|
end
|
28
30
|
|
29
|
-
opts.on(
|
31
|
+
opts.on('-n', '--no-browser', 'Do not open html report in browser') do
|
30
32
|
options[:format] = 'html'
|
31
33
|
options[:browser] = false
|
32
34
|
end
|
33
35
|
|
34
|
-
opts.on(
|
36
|
+
opts.on('-e', '--group-email', "Group commits by author's email address") do
|
35
37
|
options[:group_by] = 'email'
|
36
38
|
end
|
39
|
+
|
40
|
+
opts.on('-b', '--benchmark', 'Benchmark gitalytics') do
|
41
|
+
options[:benchmark] = true
|
42
|
+
end
|
37
43
|
end.parse!
|
38
44
|
|
39
|
-
|
40
|
-
Gitalytics.analyze(options)
|
45
|
+
if options[:benchmark]
|
46
|
+
puts Benchmark.measure { Gitalytics.analyze(options) }
|
47
|
+
else
|
48
|
+
Gitalytics.analyze(options)
|
49
|
+
end
|
data/gitalytics.gemspec
CHANGED
@@ -4,25 +4,27 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'gitalytics/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'gitalytics'
|
8
8
|
spec.version = Gitalytics::VERSION
|
9
9
|
|
10
|
-
spec.authors = [
|
11
|
-
spec.email =
|
10
|
+
spec.authors = ['Gonzalo Robaina']
|
11
|
+
spec.email = 'gonzalo@robaina.me'
|
12
12
|
|
13
|
-
spec.summary =
|
14
|
-
spec.description =
|
15
|
-
spec.homepage =
|
16
|
-
spec.license =
|
13
|
+
spec.summary = 'Git Analytics'
|
14
|
+
spec.description = 'Get usefull analytics from your git log'
|
15
|
+
spec.homepage = 'http://gonza.uy/gitalytics'
|
16
|
+
spec.license = 'MIT'
|
17
17
|
|
18
18
|
spec.files = `git ls-files -z`.split("\x0")
|
19
19
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
20
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
21
|
-
spec.require_paths = [
|
21
|
+
spec.require_paths = ['lib']
|
22
22
|
|
23
|
-
spec.required_ruby_version = '>=
|
23
|
+
spec.required_ruby_version = '>= 2.0.0'
|
24
24
|
|
25
|
-
spec.add_development_dependency
|
26
|
-
spec.add_development_dependency
|
27
|
-
spec.
|
25
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
26
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
27
|
+
spec.add_development_dependency 'byebug'
|
28
|
+
spec.add_dependency 'color-generator'
|
29
|
+
spec.add_dependency 'haml'
|
28
30
|
end
|
data/lib/gitalytics.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'haml'
|
4
4
|
require 'matrix'
|
5
5
|
require 'gitalytics/commit'
|
6
6
|
require 'gitalytics/gitlog'
|
@@ -8,6 +8,7 @@ require 'gitalytics/user'
|
|
8
8
|
require 'gitalytics/version'
|
9
9
|
|
10
10
|
module Gitalytics
|
11
|
+
OUTPUT_FILE = 'gitalytics_result.html'.freeze
|
11
12
|
|
12
13
|
module_function
|
13
14
|
|
@@ -32,31 +33,35 @@ module Gitalytics
|
|
32
33
|
end
|
33
34
|
end
|
34
35
|
|
36
|
+
def prepare_data(data)
|
37
|
+
@users = data[:users].sort { |x, y| y.commits.length <=> x.commits.length }
|
38
|
+
@commits = data[:commits]
|
39
|
+
weekday_commits = @users.map(&:weekday_commits)
|
40
|
+
@weekday_commits = weekday_commits.map { |a| Vector[*a] }.inject(:+).to_a
|
41
|
+
end
|
42
|
+
|
43
|
+
def template_file
|
44
|
+
dir = File.dirname(__FILE__)
|
45
|
+
filepath = File.join(dir, '..', 'assets', 'gitalytics.html.haml')
|
46
|
+
File.read(filepath)
|
47
|
+
end
|
48
|
+
|
35
49
|
def output_html_report(data, open_in_browser)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
File.open(output_file, 'w+') do |file|
|
40
|
-
@users = data[:users].sort do |x, y|
|
41
|
-
y.commits.length <=> x.commits.length
|
42
|
-
end
|
43
|
-
@commits = data[:commits]
|
44
|
-
weekday_commits = @users.map(&:weekday_commits)
|
45
|
-
@weekday_commits = weekday_commits.map { |a| Vector[*a] }.inject(:+).to_a
|
46
|
-
|
47
|
-
file.write(erb.result(binding))
|
50
|
+
File.open(OUTPUT_FILE, 'w+') do |file|
|
51
|
+
prepare_data(data)
|
52
|
+
file.write(Haml::Engine.new(template_file).render(self))
|
48
53
|
end
|
49
|
-
open_report_in_browser
|
54
|
+
open_report_in_browser if open_in_browser
|
50
55
|
end
|
51
56
|
|
52
|
-
def open_report_in_browser
|
53
|
-
|
54
|
-
|
55
|
-
system "start #{
|
56
|
-
|
57
|
-
system "open #{
|
58
|
-
|
59
|
-
system "xdg-open #{
|
57
|
+
def open_report_in_browser
|
58
|
+
case RbConfig::CONFIG['host_os']
|
59
|
+
when /mswin|mingw|cygwin/
|
60
|
+
system "start #{OUTPUT_FILE}"
|
61
|
+
when /darwin/
|
62
|
+
system "open #{OUTPUT_FILE}"
|
63
|
+
when /linux|bsd/
|
64
|
+
system "xdg-open #{OUTPUT_FILE}"
|
60
65
|
end
|
61
66
|
end
|
62
67
|
end
|
data/lib/gitalytics/commit.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
class Commit
|
2
|
-
|
3
2
|
attr_accessor :hash, :author, :date, :subject, :summary
|
4
3
|
|
5
4
|
def initialize(hash)
|
@@ -8,15 +7,14 @@ class Commit
|
|
8
7
|
end
|
9
8
|
|
10
9
|
def insertions
|
11
|
-
summary.inject(0) { |
|
10
|
+
summary.inject(0) { |a, e| a + e[:insertions] }
|
12
11
|
end
|
13
12
|
|
14
13
|
def deletions
|
15
|
-
summary.inject(0) { |
|
14
|
+
summary.inject(0) { |a, e| a + e[:deletions] }
|
16
15
|
end
|
17
16
|
|
18
17
|
def files_committed
|
19
|
-
summary.map{ |s| s[:filename] }
|
18
|
+
summary.map { |s| s[:filename] }
|
20
19
|
end
|
21
|
-
|
22
20
|
end
|
data/lib/gitalytics/gitlog.rb
CHANGED
@@ -3,20 +3,19 @@ require 'gitalytics/user'
|
|
3
3
|
require 'date'
|
4
4
|
|
5
5
|
module GitLog
|
6
|
-
|
7
6
|
module_function
|
8
7
|
|
9
|
-
|
10
|
-
|
8
|
+
@users = []
|
9
|
+
@commits = []
|
11
10
|
|
12
|
-
|
13
|
-
blocks = get_blocks(
|
11
|
+
def parse_git_log(group_by)
|
12
|
+
blocks = get_blocks(get_log)
|
14
13
|
|
15
14
|
blocks.each do |(hash, block_string)|
|
16
|
-
commits << parse_block(hash, block_string,
|
15
|
+
@commits << parse_block(hash, block_string, group_by)
|
17
16
|
end
|
18
17
|
|
19
|
-
{users: users, commits: commits}
|
18
|
+
{ users: @users, commits: @commits }
|
20
19
|
end
|
21
20
|
|
22
21
|
def get_log
|
@@ -25,13 +24,13 @@ module GitLog
|
|
25
24
|
|
26
25
|
def get_blocks(log_string)
|
27
26
|
commits = log_string.scan(/^([a-f0-9]{40})/).map(&:first)
|
28
|
-
blocks
|
27
|
+
blocks = log_string.split(/^[a-f0-9]{40}/).map(&:strip)
|
29
28
|
blocks.shift # get rid of first empty string
|
30
29
|
|
31
30
|
commits.zip(blocks)
|
32
31
|
end
|
33
32
|
|
34
|
-
def parse_block(hash, block_string,
|
33
|
+
def parse_block(hash, block_string, group_by)
|
35
34
|
commit = Commit.new(hash)
|
36
35
|
|
37
36
|
block_string.encode!('UTF-8', 'UTF8-MAC') if defined?(Encoding::UTF8_MAC)
|
@@ -41,41 +40,39 @@ module GitLog
|
|
41
40
|
commit.subject = data[:subject]
|
42
41
|
commit.date = Date.parse(data[:date])
|
43
42
|
|
44
|
-
get_commit_author(data, commit,
|
43
|
+
get_commit_author(data, commit, group_by)
|
45
44
|
get_commit_summary(block_string, commit)
|
46
45
|
|
47
46
|
commit
|
48
47
|
end
|
49
48
|
|
50
49
|
def get_commit_summary(block_string, commit)
|
51
|
-
|
50
|
+
regex = /^(?<insertions>\d+)\s+(?<deletions>\d+)\s+(?<filename>.*)$/
|
51
|
+
block_string.scan(regex).each do |summary|
|
52
52
|
commit.summary << {
|
53
53
|
insertions: summary[0].to_i,
|
54
54
|
deletions: summary[1].to_i,
|
55
|
-
filename: summary[2]
|
55
|
+
filename: summary[2]
|
56
56
|
}
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
-
def get_commit_author(data, commit,
|
61
|
-
user = get_user(data[:name], data[:email],
|
60
|
+
def get_commit_author(data, commit, group_by)
|
61
|
+
user = get_user(data[:name], data[:email], group_by)
|
62
62
|
|
63
63
|
commit.author = user
|
64
64
|
user.commits << commit
|
65
65
|
end
|
66
66
|
|
67
|
-
def get_user(name, email,
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
users.each do |user|
|
74
|
-
return user if user.email == email
|
75
|
-
end
|
67
|
+
def get_user(name, email, group_by)
|
68
|
+
case group_by
|
69
|
+
when 'name'
|
70
|
+
u = @users.index { |user| user.name == name }
|
71
|
+
when 'email'
|
72
|
+
u = @users.index { |user| user.email == email }
|
76
73
|
end
|
77
|
-
users
|
74
|
+
return @users[u] if u
|
75
|
+
@users << new_user = User.new(name, email)
|
78
76
|
new_user
|
79
77
|
end
|
80
|
-
|
81
78
|
end
|
data/lib/gitalytics/user.rb
CHANGED
@@ -2,14 +2,15 @@ require 'color-generator'
|
|
2
2
|
require 'digest/md5'
|
3
3
|
|
4
4
|
class User
|
5
|
-
|
6
5
|
attr_accessor :name, :email, :commits, :colors
|
7
6
|
|
8
7
|
def initialize(name, email)
|
9
|
-
self.name
|
10
|
-
self.email
|
8
|
+
self.name = name
|
9
|
+
self.email = email
|
11
10
|
self.commits = []
|
12
|
-
self.colors
|
11
|
+
self.colors = ColorGenerator.new(saturation: 0.3, lightness: 0.75)
|
12
|
+
.create_rgb
|
13
|
+
.join(', ')
|
13
14
|
end
|
14
15
|
|
15
16
|
def gravatar
|
@@ -33,11 +34,11 @@ class User
|
|
33
34
|
end
|
34
35
|
|
35
36
|
def total_insertions
|
36
|
-
commits.map(&:insertions).inject(0) { |
|
37
|
+
commits.map(&:insertions).inject(0) { |a, e| a + e }
|
37
38
|
end
|
38
39
|
|
39
40
|
def total_deletions
|
40
|
-
commits.map(&:deletions).inject(0) { |
|
41
|
+
commits.map(&:deletions).inject(0) { |a, e| a + e }
|
41
42
|
end
|
42
43
|
|
43
44
|
def total_changes
|
@@ -45,7 +46,8 @@ class User
|
|
45
46
|
end
|
46
47
|
|
47
48
|
def summary
|
48
|
-
"#{name} has made #{commits.count} commits on #{working_days}
|
49
|
+
"#{name} has made #{commits.count} commits on #{working_days} "\
|
50
|
+
"separate days during a span of #{commits_period} days."
|
49
51
|
end
|
50
52
|
|
51
53
|
def rgba(opacity = 1)
|
@@ -53,10 +55,8 @@ class User
|
|
53
55
|
end
|
54
56
|
|
55
57
|
def weekday_commits
|
56
|
-
days = Array.new(7) {0}
|
57
|
-
commits.each
|
58
|
-
days[c.date.wday] += 1
|
59
|
-
end
|
58
|
+
days = Array.new(7) { 0 }
|
59
|
+
commits.each { |c| days[c.date.wday] += 1 }
|
60
60
|
days
|
61
61
|
end
|
62
62
|
end
|
data/lib/gitalytics/version.rb
CHANGED
data/lib/haml_helper.rb
ADDED
data/test/files/block1
ADDED
data/test/files/block2
ADDED
data/test/files/block3
ADDED
data/test/files/git_log
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
bea63e76c7c6d5afd42ac5b24a911b36e5c261f9 2013-02-13 23:11:16 -0200 Gonzalo Robaina <gonzalor@gmail.com> Print basic commit report on the console
|
2
|
+
|
3
|
+
6 1 bin/gitalytics
|
4
|
+
33 6 lib/gitalytics.rb
|
5
|
+
d392710a9e657c72e73ca87df3ed2c8c802441e4 2012-12-19 06:04:59 -0800 Gonzalo Robaina <gonzalor@gmail.com> Initial commit
|
6
|
+
|
7
|
+
16 0 .gitignore
|
8
|
+
4 0 README.md
|
9
|
+
9a99ae5dbc1eaadf268ca925cce9b68d10216151 2012-12-06 10:29:49 -0200 Gonzalo Robaina <gonzalor@gmail.com> initial commit
|
10
|
+
|
11
|
+
1 0 README.md
|
12
|
+
3 0 bin/gitalytics
|
13
|
+
13 0 gitalytics.gemspec
|
14
|
+
49 0 lib/gist.rb
|
15
|
+
11 0 lib/gitalytics.rb
|