commit-comment-tools 0.0.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.
@@ -0,0 +1,131 @@
1
+ require "grit"
2
+ require "active_record"
3
+
4
+ Grit::Git.git_timeout = 30 # timeout in secs
5
+ Grit::Git.git_max_size = 2 * 1024 * 1024 # size in bytes (2MB)
6
+
7
+ module Grit
8
+ class Commit
9
+ def diff_bytesize
10
+ @diff_bytesize ||= diffs.inject(0) do |memo, _diff|
11
+ memo + _diff.diff.bytesize
12
+ end
13
+ end
14
+
15
+ def diff_lines_count
16
+ @diff_lines_count ||= diffs.inject(0) do |memo, _diff|
17
+ memo + _diff.diff.lines.to_a.size
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ module CommitCommentTools
24
+ class Commit < ActiveRecord::Base
25
+ end
26
+
27
+ class RepositoryLoader
28
+ def initialize(repository_path, base_branch_name, branch_name="")
29
+ create_table
30
+ @repository_path = repository_path
31
+ @repository = Grit::Repo.new(repository_path)
32
+ @repository_name = File.basename(repository_path)
33
+ @base_branch_name = base_branch_name
34
+ @target_branches = @repository.remotes.select do |branch|
35
+ branch_name === branch.name
36
+ end
37
+ end
38
+
39
+ def load_commits
40
+ puts @repository_name
41
+ load_base_branch_commits
42
+ @target_branches.each do |branch|
43
+ load_branch_commits(branch.name)
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def load_base_branch_commits
50
+ n_commits = 0
51
+ n_records = 0
52
+ skip = Commit.where(repository_name: @repository_name,
53
+ branch_name: @base_branch_name).count
54
+ @repository.commits(@base_branch_name, nil, skip).each do |commit|
55
+ n_commits += 1
56
+ next if Commit.where(repository_name: @repository_name,
57
+ branch_name: @base_branch_name,
58
+ commit_hash: commit.id).exists?
59
+ begin
60
+ create_commit(@base_branch_name, commit)
61
+ n_records += 1
62
+ rescue Grit::Git::GitTimeout => ex
63
+ $stderr.puts "#{ex.message}:#{@repository_name}:#{@base_branch_name}:#{commit.id}:#{commit.committed_date}"
64
+ end
65
+ end
66
+ puts "#{@base_branch_name}:#{n_records}/#{n_commits}, skip=#{skip}"
67
+ end
68
+
69
+ def load_branch_commits(branch_name)
70
+ n_commits = 0
71
+ n_records = 0
72
+ base_commit_id = base_commit(branch_name)
73
+ @repository.commits_between(base_commit_id, branch_name).each do |commit|
74
+ n_commits += 1
75
+ next if Commit.where(repository_name: @repository_name,
76
+ branch_name: branch_name,
77
+ commit_hash: commit.id).exists?
78
+ begin
79
+ create_commit(branch_name, commit)
80
+ n_records += 1
81
+ rescue Grit::Git::GitTimeout => ex
82
+ $stderr.puts "#{ex.message}:#{commit.id}:#{commit.committed_date}"
83
+ end
84
+ end
85
+ puts "#{branch_name}:#{base_commit_id}:#{n_records}/#{n_commits}"
86
+ end
87
+
88
+ def create_commit(branch_name, commit)
89
+ Commit.create(repository_name: @repository_name,
90
+ branch_name: branch_name,
91
+ commit_hash: commit.id,
92
+ commit_message: commit.message,
93
+ committer_name: commit.committer.name,
94
+ committer_email: commit.committer.email,
95
+ committed_date: commit.committed_date,
96
+ diff_lines_count: commit.diff_lines_count,
97
+ diff_bytesize: commit.diff_bytesize)
98
+ end
99
+
100
+ def base_commit(branch_name)
101
+ log = ""
102
+ Dir.chdir(@repository_path) do
103
+ log = `git show-branch --sha1-name #{@base_branch_name} #{branch_name} | tail -1`
104
+ end
105
+ log[/\[(.+?)\]/, 1]
106
+ end
107
+
108
+ def create_table
109
+ return if Commit.table_exists?
110
+ ActiveRecord::Schema.define(:version => 1) do
111
+ create_table "commits", :force => false do |t|
112
+ t.string "repository_name"
113
+ t.string "branch_name"
114
+ t.string "commit_hash"
115
+ t.text "commit_message"
116
+ t.string "committer_name"
117
+ t.string "committer_email"
118
+ t.datetime "committed_date"
119
+ t.integer "diff_lines_count"
120
+ t.integer "diff_bytesize"
121
+ end
122
+
123
+ add_index("commits",
124
+ ["repository_name", "branch_name", "commit_hash"],
125
+ :name => "index_commits_on_commit_hash",
126
+ :unique => true)
127
+ end
128
+ end
129
+ end
130
+ end
131
+
@@ -0,0 +1,113 @@
1
+ require "grit"
2
+ require "pp"
3
+
4
+ # TODO move this monkey patch
5
+ module Grit
6
+ class Commit
7
+ def diff_bytesize
8
+ @diff_bytesize ||= diffs.inject(0) do |memo, _diff|
9
+ memo + _diff.diff.bytesize
10
+ end
11
+ end
12
+
13
+ def diff_lines_count
14
+ @diff_lines_count ||= diffs.inject(0) do |memo, _diff|
15
+ memo + _diff.diff.lines.to_a.size
16
+ end
17
+ end
18
+
19
+ def autor_name
20
+ author.name
21
+ end
22
+ end
23
+ end
24
+
25
+ module CommitCommentTools
26
+ class RepositoryStats
27
+
28
+ class CommitGroup
29
+ attr_reader :branch_name, :key, :commits
30
+
31
+ def initialize(branch_name, key, commits)
32
+ @branch_name = branch_name
33
+ @key = key
34
+ @commits = commits
35
+ end
36
+
37
+ def size
38
+ @commits.size
39
+ end
40
+
41
+ def diff_lines_count
42
+ @diff_lines_count ||= commits.inject(0) do |memo, commit|
43
+ memo + commit.diff_lines_count
44
+ end
45
+ end
46
+
47
+ def diff_bytesize
48
+ @diff_bytesize ||= commits.inject(0) do |memo, commit|
49
+ memo + commit.diff_bytesize
50
+ end
51
+ end
52
+ end
53
+
54
+ def initialize(repository_path, branch_name, resolution=:day)
55
+ @repository = Grit::Repo.new(repository_path)
56
+ @target_branches = @repository.remotes.select do |branch|
57
+ branch_name === branch.name
58
+ end
59
+ @resolution = resolution
60
+ end
61
+
62
+ def stats
63
+ # TODO format data
64
+ case @resolution
65
+ when :day
66
+ commit_groups = commit_groups_by_date
67
+ when :week
68
+ commit_groups = commit_groups_by_week
69
+ when :month
70
+ commit_groups = commit_groups_by_month
71
+ end
72
+ # TODO make simple
73
+ commit_groups.group_by do |commit_group|
74
+ commit_group.branch_name
75
+ end.each do |branch_name, commit_groups|
76
+ puts branch_name
77
+ commit_groups.sort_by do |group|
78
+ group.key
79
+ end.each do |group|
80
+ puts "#{group.key},#{group.size},#{group.diff_lines_count},#{group.diff_bytesize}"
81
+ end
82
+ end
83
+ end
84
+
85
+ def commit_groups_by_date
86
+ create_commit_groups("%Y-%m-%d")
87
+ end
88
+
89
+ def commit_groups_by_week
90
+ create_commit_groups("%Y-%Uw")
91
+ end
92
+
93
+ def commit_groups_by_month
94
+ create_commit_groups("%Y-%m")
95
+ end
96
+
97
+ def create_commit_groups(key_format="%Y-%m-%d")
98
+ @target_branches.map do |branch|
99
+ create_commit_group(branch.name, key_format)
100
+ end.flatten
101
+ end
102
+
103
+ def create_commit_group(branch_name, key_format="%Y-%m-%d")
104
+ # TODO Use Grit::Commit.find_all
105
+ groups = @repository.commits(branch_name, nil).group_by do |commit|
106
+ commit.date.strftime(key_format)
107
+ end
108
+ groups.map do |key, commits|
109
+ CommitGroup.new(branch_name, key, commits)
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,20 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2013 Haruka Yoshihara <yoshihara@clear-code.com>
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ module CommitCommentTools
19
+ VERSION = "0.0.1"
20
+ end
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ #
4
+ # Copyright (C) 2013 Haruka Yoshihara <yoshihara@clear-code.com>
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ $VERBOSE = true
20
+
21
+ base_dir = File.expand_path(File.join(File.dirname(__FILE__), ".."))
22
+ lib_dir = File.join(base_dir, "lib")
23
+ test_dir = File.join(base_dir, "test")
24
+
25
+ $LOAD_PATH.unshift(lib_dir)
26
+
27
+ require "test-unit"
28
+ require "test/unit/notify"
29
+
30
+ Thread.abort_on_exception = true
31
+
32
+ exit Test::Unit::AutoRunner.run(true, test_dir)
@@ -0,0 +1,78 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2013 Haruka Yoshihara <yoshihara@clear-code.com>
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require "commit-comment-tools/entry"
19
+
20
+ class EntryTest < Test::Unit::TestCase
21
+ def test_name
22
+ expected_name = "yamada"
23
+ entry = generate_entry(:name => expected_name)
24
+ assert_equal(expected_name, entry.name)
25
+ end
26
+
27
+ data(:with_zero_padding => "2013-02-05",
28
+ :without_zero_padding => "2013-2-5")
29
+ def test_date(date)
30
+ entry = generate_entry(:date => date)
31
+ expected_date = Date.parse("2013-02-05").strftime("%Y-%m-%d")
32
+ assert_equal(expected_date, entry.date)
33
+ end
34
+
35
+ def test_read_ratio
36
+ entry = generate_entry(:read_ratio => "10")
37
+ assert_equal(10, entry.read_ratio)
38
+ end
39
+
40
+ data(:one_line => "typoのコミットが多かった。",
41
+ :continues_line => "typoのコミットが多かった。\ngit-rebaseって何?",
42
+ :nonexistent => "")
43
+ def test_comment(comment)
44
+ entry = generate_entry(:comment => comment)
45
+ assert_equal(comment, entry.comment)
46
+ end
47
+
48
+ class InvalidEntryTest < self
49
+ data(:no_separate => "20130205",
50
+ :slash_separate => "2013/02/05",
51
+ :nonexistent => "")
52
+ def test_date(invalid_date)
53
+ assert_raise(CommitCommentTools::Entry::InvalidEntryError) do
54
+ generate_entry(:date => invalid_date)
55
+ end
56
+ end
57
+
58
+ data(:decimal_number => "10.5",
59
+ :no_number => "NoNumber",
60
+ :nonexistent => "")
61
+ def test_read_ratio(invalid_read_ratio)
62
+ assert_raise(CommitCommentTools::Entry::InvalidEntryError) do
63
+ generate_entry(:read_ratio => invalid_read_ratio)
64
+ end
65
+ end
66
+ end
67
+
68
+ private
69
+ def generate_entry(options={})
70
+ name = options[:name] || "yamada"
71
+ date = options[:date] || "2013-02-20"
72
+ read_ratio = options[:read_ratio] || "10"
73
+ comment = options[:comment] || "あんまり読めなかった。"
74
+
75
+ entry_chunk = "#{date}:#{read_ratio}%:#{comment}"
76
+ CommitCommentTools::Entry.new(name, entry_chunk)
77
+ end
78
+ end
@@ -0,0 +1,87 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2013 Haruka Yoshihara <yoshihara@clear-code.com>
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require "commit-comment-tools/report-normalizer"
19
+
20
+ class TestReportNormalizer < Test::Unit::TestCase
21
+ def setup
22
+ @normalizer = CommitCommentTools::ReportNormlizer.new
23
+ end
24
+
25
+ def test_normalize
26
+ daily_report = {
27
+ "2013-1-30" => {
28
+ :read_ratio => "40",
29
+ :comment => "特になし"
30
+ },
31
+ "2013-2-1" => {
32
+ :read_ratio => "80",
33
+ :comment => "typoが多かった"
34
+ }
35
+ }
36
+ person_report = {"yamada" => daily_report}
37
+ actual_report = @normalizer.normalize(person_report)
38
+ assert_normalize_report(actual_report)
39
+ end
40
+
41
+ def test_normalize_daily_report_with_zero_padding
42
+ daily_report = {
43
+ "2013-1-30" => {
44
+ :read_ratio => "40",
45
+ :comment => "特になし"
46
+ },
47
+ "2013-2-1" => {
48
+ :read_ratio => "80",
49
+ :comment => "typoが多かった"
50
+ }
51
+ }
52
+ actual_report = @normalizer.normalize_daily_report("yamada", daily_report)
53
+ assert_normalize_report(actual_report)
54
+ end
55
+
56
+ def test_normalize_daily_report_without_zero_padding
57
+ daily_report = {
58
+ "2013-1-30" => {
59
+ :read_ratio => "40",
60
+ :comment => "特になし"
61
+ },
62
+ "2013-2-1" => {
63
+ :read_ratio => "80",
64
+ :comment => "typoが多かった"
65
+ }
66
+ }
67
+ actual_report = @normalizer.normalize_daily_report("yamada", daily_report)
68
+ assert_normalize_report(actual_report)
69
+ end
70
+
71
+ def assert_normalize_report(actual_report)
72
+ expected_report = {
73
+ "yamada" =>
74
+ {"2013-01-30" =>
75
+ {
76
+ :read_ratio => 40,
77
+ :comment => "特になし"
78
+ },
79
+ "2013-02-01" =>
80
+ {:read_ratio => 80,
81
+ :comment => "typoが多かった"
82
+ }
83
+ }
84
+ }
85
+ assert_equal(expected_report, actual_report)
86
+ end
87
+ end