metior 0.1.4 → 0.2.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/.travis.yml +2 -0
- data/.yardopts +1 -0
- data/Changelog.md +23 -0
- data/Gemfile +1 -17
- data/Gemfile.lock +28 -21
- data/README.md +66 -20
- data/lib/metior.rb +30 -14
- data/lib/metior/actor.rb +28 -22
- data/lib/metior/auto_include_vcs.rb +43 -0
- data/lib/metior/collections/actor_collection.rb +97 -0
- data/lib/metior/collections/collection.rb +84 -0
- data/lib/metior/collections/commit_collection.rb +309 -0
- data/lib/metior/commit.rb +128 -48
- data/lib/metior/errors.rb +2 -2
- data/lib/metior/git/commit.rb +32 -31
- data/lib/metior/git/repository.rb +71 -6
- data/lib/metior/github/commit.rb +5 -16
- data/lib/metior/github/repository.rb +68 -40
- data/lib/metior/report.rb +139 -0
- data/lib/metior/report/view.rb +120 -0
- data/lib/metior/report/view_helper.rb +47 -0
- data/lib/metior/repository.rb +225 -56
- data/lib/metior/vcs.rb +12 -3
- data/lib/metior/version.rb +1 -1
- data/metior.gemspec +28 -26
- data/reports/default.rb +17 -0
- data/reports/default/images/favicon.png +0 -0
- data/reports/default/stylesheets/default.css +128 -0
- data/reports/default/templates/actor/minimal.mustache +1 -0
- data/reports/default/templates/commit/minimal.mustache +1 -0
- data/reports/default/templates/index.mustache +27 -0
- data/reports/default/templates/most_significant_authors.mustache +11 -0
- data/reports/default/templates/most_significant_commits.mustache +13 -0
- data/reports/default/templates/repository_information.mustache +17 -0
- data/reports/default/templates/top_committers.mustache +11 -0
- data/reports/default/views/index.rb +33 -0
- data/reports/default/views/most_significant_authors.rb +19 -0
- data/reports/default/views/most_significant_commits.rb +19 -0
- data/reports/default/views/repository_information.rb +47 -0
- data/reports/default/views/top_committers.rb +21 -0
- data/test/fixtures.rb +54 -36
- data/test/helper.rb +10 -3
- data/test/{test_class_loading.rb → test_1st_class_loading.rb} +1 -1
- data/test/test_actor_colletion.rb +78 -0
- data/test/test_collection.rb +61 -0
- data/test/test_commit_collection.rb +139 -0
- data/test/test_git.rb +58 -5
- data/test/test_github.rb +52 -9
- data/test/test_metior.rb +22 -1
- data/test/test_report.rb +49 -0
- data/test/test_repository.rb +46 -9
- data/test/test_vcs.rb +36 -13
- metadata +105 -43
data/lib/metior/commit.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
#
|
4
4
|
# Copyright (c) 2011, Sebastian Staudt
|
5
5
|
|
6
|
+
require 'metior/auto_include_vcs'
|
7
|
+
|
6
8
|
module Metior
|
7
9
|
|
8
10
|
# This class represents a commit in a source code repository
|
@@ -15,11 +17,7 @@ module Metior
|
|
15
17
|
# @author Sebastian Staudt
|
16
18
|
class Commit
|
17
19
|
|
18
|
-
|
19
|
-
attr_reader :added_files
|
20
|
-
|
21
|
-
# @return [Fixnum] The lines of code that have been added in this commit
|
22
|
-
attr_reader :additions
|
20
|
+
include AutoIncludeVCS
|
23
21
|
|
24
22
|
# @return [Actor] This commit's author
|
25
23
|
attr_reader :author
|
@@ -30,73 +28,116 @@ module Metior
|
|
30
28
|
# @return [Object] A unique identifier of the commit in the repository
|
31
29
|
attr_reader :id
|
32
30
|
|
31
|
+
# @return [Array<Object>] The unique identifiers of the children of this
|
32
|
+
# commit
|
33
|
+
attr_reader :children
|
34
|
+
|
33
35
|
# @return [Time] The date this commit has been committed
|
34
36
|
attr_reader :committed_date
|
35
37
|
|
36
38
|
# @return [Actor] This commit's committer
|
37
39
|
attr_reader :committer
|
38
40
|
|
39
|
-
# @return [Array<String>] A list of file paths deleted in this commit
|
40
|
-
attr_reader :deleted_files
|
41
|
-
|
42
|
-
# @return [Fixnum] The lines of code that have been deleted in this commit
|
43
|
-
attr_reader :deletions
|
44
|
-
|
45
41
|
# @return [String] The commit message of this commit
|
46
42
|
attr_reader :message
|
47
43
|
|
48
|
-
# @return [Array<
|
49
|
-
|
50
|
-
|
51
|
-
# @return [Range] The commit range this commit belongs to
|
52
|
-
attr_reader :range
|
44
|
+
# @return [Array<Object>] The unique identifiers of one or more more
|
45
|
+
# parents of this commit
|
46
|
+
attr_reader :parents
|
53
47
|
|
54
48
|
# @return [Repository] The repository this commit belongs to
|
55
49
|
attr_reader :repo
|
56
50
|
|
57
|
-
#
|
58
|
-
# commits
|
51
|
+
# Creates a new commit instance linked to the given repository and branch
|
59
52
|
#
|
60
|
-
# @param [
|
61
|
-
|
62
|
-
|
63
|
-
|
53
|
+
# @param [Repository] repo The repository this commit belongs to
|
54
|
+
def initialize(repo)
|
55
|
+
@additions = nil
|
56
|
+
@added_files = nil
|
57
|
+
@children = []
|
58
|
+
@deletions = nil
|
59
|
+
@deleted_files = nil
|
60
|
+
@modified_files = nil
|
61
|
+
@repo = repo
|
62
|
+
end
|
64
63
|
|
65
|
-
|
64
|
+
# Sets the author of this commit
|
65
|
+
#
|
66
|
+
# This also adds the commit to the commits this actor has authored.
|
67
|
+
#
|
68
|
+
# @param [Object] author The data of the author of this commit
|
69
|
+
# @see Actor#authored_commits
|
70
|
+
# @see Repository#actor
|
71
|
+
def author=(author)
|
72
|
+
@author = @repo.actor author
|
73
|
+
@author.authored_commits << self
|
74
|
+
end
|
66
75
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
active_days[day] = 1
|
75
|
-
end
|
76
|
-
end
|
76
|
+
# Adds the unique identifier of a child of this commit to the list of child
|
77
|
+
# commits
|
78
|
+
#
|
79
|
+
# @param [Object] child The unique identifier of the child commit to add
|
80
|
+
def add_child(child)
|
81
|
+
@children << child
|
82
|
+
end
|
77
83
|
|
78
|
-
|
84
|
+
# Returns the paths of all files that have been modified in this commit
|
85
|
+
#
|
86
|
+
# This will load the file stats from the repository if not done yet.
|
87
|
+
#
|
88
|
+
# @return [Array<String>] A list of file paths added in this commit
|
89
|
+
# @see #load_file_stats
|
90
|
+
def added_files
|
91
|
+
load_file_stats if @added_files.nil?
|
92
|
+
@added_files
|
93
|
+
end
|
79
94
|
|
80
|
-
|
81
|
-
|
95
|
+
# Returnes the number of lines of code added in this commit
|
96
|
+
#
|
97
|
+
# @return [Fixnum] The lines of code that have been added
|
98
|
+
# @see #load_line_stats
|
99
|
+
def additions
|
100
|
+
load_line_stats if @additions.nil?
|
101
|
+
@additions
|
102
|
+
end
|
82
103
|
|
83
|
-
|
104
|
+
# Sets the comitter of this commit
|
105
|
+
#
|
106
|
+
# This also adds the commit to the commits this actor has comitted.
|
107
|
+
#
|
108
|
+
# @param [Object] committer The data of the comitter of this commit
|
109
|
+
# @see Actor#committed_commits
|
110
|
+
# @see Repository#actor
|
111
|
+
def committer=(committer)
|
112
|
+
@committer = @repo.actor committer
|
113
|
+
@committer.committed_commits << self
|
114
|
+
end
|
84
115
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
116
|
+
# Returns the paths of all files that have been modified in this commit
|
117
|
+
#
|
118
|
+
# This will load the file stats from the repository if not done yet.
|
119
|
+
#
|
120
|
+
# @return [Array<String>] A list of file paths deleted in this commit
|
121
|
+
# @see #load_file_stats
|
122
|
+
def deleted_files
|
123
|
+
load_file_stats if @deleted_files.nil?
|
124
|
+
@deleted_files
|
125
|
+
end
|
89
126
|
|
90
|
-
|
127
|
+
# Returnes the number of lines of code deleted in this commit
|
128
|
+
#
|
129
|
+
# @return [Fixnum] The lines of code that have been deleted
|
130
|
+
# @see #load_line_stats
|
131
|
+
def deletions
|
132
|
+
load_line_stats if @deletions.nil?
|
133
|
+
@deletions
|
91
134
|
end
|
92
135
|
|
93
|
-
#
|
136
|
+
# Returns whether this commits is a merge commit
|
94
137
|
#
|
95
|
-
# @
|
96
|
-
|
97
|
-
|
98
|
-
@repo = repo
|
99
|
-
@range = range
|
138
|
+
# @return [Boolean] `true` if this commit is a merge commit
|
139
|
+
def merge?
|
140
|
+
@parents.size > 1
|
100
141
|
end
|
101
142
|
|
102
143
|
# Returns the total of changed lines in this commit
|
@@ -106,6 +147,17 @@ module Metior
|
|
106
147
|
additions + deletions
|
107
148
|
end
|
108
149
|
|
150
|
+
# Returns the paths of all files that have been modified in this commit
|
151
|
+
#
|
152
|
+
# This will load the file stats from the repository if not done yet.
|
153
|
+
#
|
154
|
+
# @return [Array<String>] A list of file paths modified in this commit
|
155
|
+
# @see #load_file_stats
|
156
|
+
def modified_files
|
157
|
+
load_file_stats if @modified_files.nil?
|
158
|
+
@modified_files
|
159
|
+
end
|
160
|
+
|
109
161
|
# Returns the subject line of the commit message, i.e. the first line
|
110
162
|
#
|
111
163
|
# @return [String] The subject of the commit
|
@@ -113,6 +165,34 @@ module Metior
|
|
113
165
|
@message.split(/$/).first
|
114
166
|
end
|
115
167
|
|
168
|
+
# Creates a string representation for this commit without recursing into
|
169
|
+
# actor and repository details
|
170
|
+
#
|
171
|
+
# @return [String] A minimal string representation for this commit
|
172
|
+
def inspect
|
173
|
+
'#<%s:0x%x: @author="%s" @committer="%s" @id="%s" @repo="%s" @subject="%s">' %
|
174
|
+
[
|
175
|
+
self.class.name, __id__ * 2, @author.id, @committer.id, @id,
|
176
|
+
@repo.path, subject
|
177
|
+
]
|
178
|
+
end
|
179
|
+
|
180
|
+
protected
|
181
|
+
|
182
|
+
# Loads the file stats for this commit from the repository
|
183
|
+
#
|
184
|
+
# @abstract Has to be implemented by VCS specific subclasses
|
185
|
+
def load_file_stats
|
186
|
+
raise NotImplementedError
|
187
|
+
end
|
188
|
+
|
189
|
+
# Loads the line stats for this commit from the repository
|
190
|
+
#
|
191
|
+
# @abstract Has to be implemented by VCS specific subclasses
|
192
|
+
def load_line_stats
|
193
|
+
raise NotImplementedError
|
194
|
+
end
|
195
|
+
|
116
196
|
end
|
117
197
|
|
118
198
|
end
|
data/lib/metior/errors.rb
CHANGED
@@ -10,8 +10,8 @@ module Metior
|
|
10
10
|
class UnsupportedError < RuntimeError
|
11
11
|
|
12
12
|
# Creates a new instance of this error
|
13
|
-
def initialize
|
14
|
-
super 'Operation not supported by the current VCS.'
|
13
|
+
def initialize(vcs)
|
14
|
+
super 'Operation not supported by the current VCS (:%s).' % vcs::NAME
|
15
15
|
end
|
16
16
|
|
17
17
|
end
|
data/lib/metior/git/commit.rb
CHANGED
@@ -4,8 +4,6 @@
|
|
4
4
|
# Copyright (c) 2011, Sebastian Staudt
|
5
5
|
|
6
6
|
require 'metior/commit'
|
7
|
-
require 'metior/git'
|
8
|
-
require 'metior/git/actor'
|
9
7
|
|
10
8
|
module Metior
|
11
9
|
|
@@ -16,51 +14,54 @@ module Metior
|
|
16
14
|
# @author Sebastian Staudt
|
17
15
|
class Commit < Metior::Commit
|
18
16
|
|
19
|
-
include Metior::Git
|
20
|
-
|
21
17
|
# Creates a new Git commit object linked to the repository and branch it
|
22
18
|
# belongs to and the data from the corresponding `Grit::Commit` object
|
23
19
|
#
|
24
20
|
# @param [Repository] repo The Git repository this commit belongs to
|
25
|
-
# @param [String] range The commit range this commits belongs to
|
26
21
|
# @param [Grit::Commit] commit The commit object from Grit
|
27
|
-
def initialize(repo,
|
28
|
-
super repo
|
22
|
+
def initialize(repo, commit)
|
23
|
+
super repo
|
29
24
|
|
30
|
-
@additions = commit.stats.additions
|
31
25
|
@authored_date = commit.authored_date
|
32
26
|
@committed_date = commit.committed_date
|
33
|
-
@deletions = commit.stats.deletions
|
34
27
|
@id = commit.id
|
35
28
|
@message = commit.message
|
29
|
+
@parents = commit.parents.map { |parent| parent.id }
|
36
30
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
@author.add_commit self
|
31
|
+
self.author = commit.author
|
32
|
+
self.committer = commit.committer
|
33
|
+
end
|
41
34
|
|
42
|
-
|
43
|
-
@committer = committers[Actor.id_for commit.committer]
|
44
|
-
@committer = Actor.new repo, commit.committer if @committer.nil?
|
45
|
-
@committer.add_commit self
|
35
|
+
end
|
46
36
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
37
|
+
# Loads the file stats for this commit from the repository
|
38
|
+
#
|
39
|
+
# @see Repository#raw_commit
|
40
|
+
def load_file_stats
|
41
|
+
@added_files = []
|
42
|
+
@modified_files = []
|
43
|
+
@deleted_files = []
|
44
|
+
@repo.raw_commit(@id).diffs.each do |diff|
|
45
|
+
if diff.new_file
|
46
|
+
@added_files << diff.b_path
|
47
|
+
elsif diff.deleted_file
|
48
|
+
@deleted_files << diff.b_path
|
49
|
+
elsif diff.renamed_file
|
50
|
+
@added_files << diff.b_path
|
51
|
+
@deleted_files << diff.a_path
|
52
|
+
else
|
53
|
+
@modified_files << diff.b_path
|
61
54
|
end
|
62
55
|
end
|
56
|
+
end
|
63
57
|
|
58
|
+
# Loads the line stats for this commit from the repository
|
59
|
+
#
|
60
|
+
# @see Repository#raw_commit
|
61
|
+
def load_line_stats
|
62
|
+
commit = @repo.raw_commit @id
|
63
|
+
@additions = commit.stats.additions
|
64
|
+
@deletions = commit.stats.deletions
|
64
65
|
end
|
65
66
|
|
66
67
|
end
|
@@ -5,8 +5,6 @@
|
|
5
5
|
|
6
6
|
require 'grit'
|
7
7
|
|
8
|
-
require 'metior/git'
|
9
|
-
require 'metior/git/commit'
|
10
8
|
require 'metior/repository'
|
11
9
|
|
12
10
|
module Metior
|
@@ -18,8 +16,6 @@ module Metior
|
|
18
16
|
# @author Sebastian Staudt
|
19
17
|
class Repository < Metior::Repository
|
20
18
|
|
21
|
-
include Metior::Git
|
22
|
-
|
23
19
|
# Creates a new Git repository based on the given path
|
24
20
|
#
|
25
21
|
# This creates a new `Grit::Repo` instance to interface with the
|
@@ -32,8 +28,41 @@ module Metior
|
|
32
28
|
@grit_repo = Grit::Repo.new(path)
|
33
29
|
end
|
34
30
|
|
31
|
+
# Retrieves a raw commit object for the given commit ID
|
32
|
+
#
|
33
|
+
# @param [String] id The ID of the commit
|
34
|
+
# @return [Grit::Commit] The commit object
|
35
|
+
# @see Grit::Repo#commit
|
36
|
+
def raw_commit(id)
|
37
|
+
@grit_repo.commit(id)
|
38
|
+
end
|
39
|
+
|
35
40
|
private
|
36
41
|
|
42
|
+
# Returns the unique identifier for the commit the given reference – like
|
43
|
+
# a branch name – is pointing to
|
44
|
+
#
|
45
|
+
# Returns the given ref name immediately if it is a full SHA1 commit ID.
|
46
|
+
#
|
47
|
+
# @param [String] ref A symbolic reference name
|
48
|
+
# @return [String] The SHA1 ID of the commit the reference is pointing to
|
49
|
+
def id_for_ref(ref)
|
50
|
+
return ref if ref.match(/[0-9a-f]{40}/)
|
51
|
+
unless @refs.key? ref
|
52
|
+
@refs[ref] = @grit_repo.git.rev_parse({}, "#{ref}^{}")
|
53
|
+
end
|
54
|
+
@refs[ref]
|
55
|
+
end
|
56
|
+
|
57
|
+
# Loads all branches and the corresponding commit IDs of this repository
|
58
|
+
#
|
59
|
+
# @return [Hash<String, String>] The names of all branches and the
|
60
|
+
# corresponding commit IDs
|
61
|
+
# @see Grit::Repo#branches
|
62
|
+
def load_branches
|
63
|
+
Hash[@grit_repo.branches.map { |b| [b.name, b.commit.id] }]
|
64
|
+
end
|
65
|
+
|
37
66
|
# This method uses Grit to load all commits from the given commit range
|
38
67
|
#
|
39
68
|
# Because of some Grit internal limitations, the commits have to be
|
@@ -47,12 +76,16 @@ module Metior
|
|
47
76
|
# (`'master..development'`), a range (`'master'..'development'`)
|
48
77
|
# or as a single ref (`'master'`). A single ref name means all
|
49
78
|
# commits reachable from that ref.
|
50
|
-
# @return [
|
79
|
+
# @return [Grit::Commit, nil] The base commit of the requested range or
|
80
|
+
# `nil` if the the range starts at the beginning of the history
|
81
|
+
# @return [Array<Grit::Commit>] All commits in the given commit range
|
51
82
|
# @see Grit::Repo#commits
|
52
83
|
def load_commits(range)
|
53
84
|
if range.first == ''
|
85
|
+
base_commit = nil
|
54
86
|
range = range.last
|
55
87
|
else
|
88
|
+
base_commit = @grit_repo.commit(range.first)
|
56
89
|
range = '%s..%s' % [range.first, range.last]
|
57
90
|
end
|
58
91
|
|
@@ -62,7 +95,39 @@ module Metior
|
|
62
95
|
commits += @grit_repo.commits(range, 500, skip)
|
63
96
|
skip += 500
|
64
97
|
end while commits.size == skip
|
65
|
-
|
98
|
+
|
99
|
+
[base_commit, commits]
|
100
|
+
end
|
101
|
+
|
102
|
+
# Loads both the name and description of the project contained in the
|
103
|
+
# repository from the description file in `GIT_DIR`. The first line of
|
104
|
+
# that file is used as the project's name, the remaining text is used as
|
105
|
+
# a description of the project.
|
106
|
+
#
|
107
|
+
# @see #description
|
108
|
+
# @see #name
|
109
|
+
# @see Grit::Repo#name
|
110
|
+
def load_name_and_description
|
111
|
+
description = @grit_repo.description
|
112
|
+
if description.start_with? 'Unnamed repository'
|
113
|
+
@name = ''
|
114
|
+
@description = ''
|
115
|
+
else
|
116
|
+
description = description.lines.to_a
|
117
|
+
@name = description.shift.strip
|
118
|
+
@description = description.join("\n").strip
|
119
|
+
end
|
120
|
+
end
|
121
|
+
alias_method :load_description, :load_name_and_description
|
122
|
+
alias_method :load_name, :load_name_and_description
|
123
|
+
|
124
|
+
# Loads all tags and the corresponding commit IDs of this repository
|
125
|
+
#
|
126
|
+
# @return [Hash<String, String>] The names of all tags and the
|
127
|
+
# corresponding commit IDs
|
128
|
+
# @see Grit::Repo#tags
|
129
|
+
def load_tags
|
130
|
+
Hash[@grit_repo.tags.map { |b| [b.name, b.commit.id] }]
|
66
131
|
end
|
67
132
|
|
68
133
|
end
|