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
@@ -0,0 +1,43 @@
|
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2011, Sebastian Staudt
|
5
|
+
|
6
|
+
module Metior
|
7
|
+
|
8
|
+
# This module should be included by all classes that have VCS implementation
|
9
|
+
# specific subclasses, like {Repository}.
|
10
|
+
#
|
11
|
+
# @author Sebastian Staudt
|
12
|
+
module AutoIncludeVCS
|
13
|
+
|
14
|
+
# Will automatically include the class method `inherited`
|
15
|
+
#
|
16
|
+
# @see ClassMethods
|
17
|
+
def self.included(mod)
|
18
|
+
mod.extend ClassMethods
|
19
|
+
end
|
20
|
+
|
21
|
+
# This module implements the class method `inherited` that will handle the
|
22
|
+
# automatic inclusion of the VCS implementation `Module`
|
23
|
+
#
|
24
|
+
# @author Sebastian Staudt
|
25
|
+
module ClassMethods
|
26
|
+
|
27
|
+
# This method will automatically include the VCS implementation `Module`
|
28
|
+
# corresponding to the subclass that has just been defined
|
29
|
+
#
|
30
|
+
# @param [Class] subclass The subclass that has been defined
|
31
|
+
def inherited(subclass)
|
32
|
+
vcs = Object
|
33
|
+
subclass.to_s.split('::')[0..-2].each do |mod|
|
34
|
+
vcs = vcs.const_get mod.to_sym
|
35
|
+
end
|
36
|
+
subclass.send :include, vcs if vcs.ancestors.include? VCS
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2011, Sebastian Staudt
|
5
|
+
|
6
|
+
require 'metior/collections/collection'
|
7
|
+
require 'metior/collections/commit_collection'
|
8
|
+
|
9
|
+
module Metior
|
10
|
+
|
11
|
+
# This class implements a collection of actors and provides functionality
|
12
|
+
# specific to actors.
|
13
|
+
#
|
14
|
+
# @author Sebastian Staudt
|
15
|
+
# @see Actor
|
16
|
+
class ActorCollection < Collection
|
17
|
+
|
18
|
+
# Returns the commits authored by all or a specific actor in this
|
19
|
+
# collection
|
20
|
+
#
|
21
|
+
# @param [Object] actor_id The ID of the actor, if only the commits of a
|
22
|
+
# specific actor should be returned
|
23
|
+
# @return [CommitCollection] All commits authored by the actors in this
|
24
|
+
# collection or by a specific actor
|
25
|
+
def authored_commits(actor_id = nil)
|
26
|
+
load_commits :authored_commits, actor_id
|
27
|
+
end
|
28
|
+
alias_method :commits, :authored_commits
|
29
|
+
|
30
|
+
# Returns the commits committed by all or a specific actor in this
|
31
|
+
# collection
|
32
|
+
#
|
33
|
+
# @param [Object] actor_id The ID of the actor, if only the commits of a
|
34
|
+
# specific actor should be returned
|
35
|
+
# @return [CommitCollection] All commits committed by the actors in this
|
36
|
+
# collection or by a specific actor
|
37
|
+
def committed_commits(actor_id = nil)
|
38
|
+
load_commits :committed_commits, actor_id
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns up to the given number of actors in this collection with the
|
42
|
+
# biggest impact on the repository, i.e. changing the most code
|
43
|
+
#
|
44
|
+
# @param [Numeric] count The number of actors to return
|
45
|
+
# @return [ActorCollection] The given number of actors ordered by impact
|
46
|
+
# @see Actor#modifications
|
47
|
+
def most_significant(count = 3)
|
48
|
+
first.support! :line_stats
|
49
|
+
|
50
|
+
authors = ActorCollection.new
|
51
|
+
sort_by { |author| -author.modifications }.each do |author|
|
52
|
+
authors << author
|
53
|
+
break if authors.size == count
|
54
|
+
end
|
55
|
+
authors
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns up to the given number of actors in this collection with the
|
59
|
+
# most commits
|
60
|
+
#
|
61
|
+
# @param [Numeric] count The number of actors to return
|
62
|
+
# @return [ActorCollection] The given number of actors ordered by commit
|
63
|
+
# count
|
64
|
+
# @see Actor#commits
|
65
|
+
def top(count = 3)
|
66
|
+
authors = ActorCollection.new
|
67
|
+
sort_by { |author| -author.authored_commits.size }.each do |author|
|
68
|
+
authors << author
|
69
|
+
break if authors.size == count
|
70
|
+
end
|
71
|
+
authors
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
# Loads the commits authored or committed by all actors in this collection
|
77
|
+
# or a specific actor
|
78
|
+
#
|
79
|
+
# @param [:authored_commits, :committed_commits] commit_type The type of
|
80
|
+
# commits to load
|
81
|
+
# @param [Object] actor_id The ID of the actor, if only the commits of a
|
82
|
+
# specific actor should be returned
|
83
|
+
# @return [CommitCollection] All commits authored or committed by the
|
84
|
+
# actors in this collection or by a specific actor
|
85
|
+
def load_commits(commit_type, actor_id = nil)
|
86
|
+
commits = CommitCollection.new
|
87
|
+
if actor_id.nil?
|
88
|
+
each { |actor| commits.merge! actor.send(commit_type) }
|
89
|
+
elsif key? actor_id
|
90
|
+
commits = self[actor_id].send commit_type
|
91
|
+
end
|
92
|
+
commits
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2011, Sebastian Staudt
|
5
|
+
|
6
|
+
HASH_CLASS = if RUBY_VERSION.match(/^1\.8/)
|
7
|
+
require 'hashery/ordered_hash'
|
8
|
+
OrderedHash
|
9
|
+
else
|
10
|
+
Hash
|
11
|
+
end
|
12
|
+
|
13
|
+
module Metior
|
14
|
+
|
15
|
+
# Represents a hash retaining insertion order
|
16
|
+
#
|
17
|
+
# On Ruby 1.9 this is a subclass of `Hash` because Ruby 1.9's hashes are
|
18
|
+
# already retaining insertion order. For Ruby 1.8 this needs a special
|
19
|
+
# parent class `OrderedHash` provided by the Hashery gem.
|
20
|
+
#
|
21
|
+
# Additionally, it provides some shortcuts to make its interface more
|
22
|
+
# array-like.
|
23
|
+
#
|
24
|
+
# @author Sebastian Staudt
|
25
|
+
# @see Hash
|
26
|
+
# @see OrderedHash
|
27
|
+
class Collection < HASH_CLASS
|
28
|
+
|
29
|
+
# Creates a new collection with the given objects
|
30
|
+
#
|
31
|
+
# @param [Array<Object>] objects The objects that should be initially
|
32
|
+
# inserted into the collection
|
33
|
+
def initialize(objects = [])
|
34
|
+
super()
|
35
|
+
|
36
|
+
objects.each { |obj| self << obj }
|
37
|
+
end
|
38
|
+
|
39
|
+
# Adds an object to this collection
|
40
|
+
#
|
41
|
+
# The object should provide a `#id` method to generate a key for this
|
42
|
+
# object.
|
43
|
+
#
|
44
|
+
# @param [Object] object The object to add to the collection
|
45
|
+
# @return [Collection] The collection itself
|
46
|
+
# @see Array#<<
|
47
|
+
def <<(object)
|
48
|
+
self[object.id] = object
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
# Evaluates the block for each element of the collection
|
53
|
+
#
|
54
|
+
# @return [Collection] The collection itself
|
55
|
+
# @yield [element] Each of the elements of this collection
|
56
|
+
# @yieldparam [Object] element The current element of the collection
|
57
|
+
def each(&block)
|
58
|
+
each_value(&block)
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns the element that has been added last to this collection
|
63
|
+
#
|
64
|
+
# @return [Object] The last element of the collection
|
65
|
+
# @see Enumerable#last
|
66
|
+
def last
|
67
|
+
values.last
|
68
|
+
end
|
69
|
+
|
70
|
+
if superclass != Hash
|
71
|
+
# Adds all elements of another collection to this one
|
72
|
+
#
|
73
|
+
# @param [Collection] other_collection The collection to merge into this
|
74
|
+
# one
|
75
|
+
# @return [Collection] The merged collection
|
76
|
+
def merge!(other_collection)
|
77
|
+
other_collection.each { |obj| self << obj }
|
78
|
+
self
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,309 @@
|
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2011, Sebastian Staudt
|
5
|
+
|
6
|
+
require 'time'
|
7
|
+
|
8
|
+
require 'metior/collections/actor_collection'
|
9
|
+
require 'metior/collections/collection'
|
10
|
+
|
11
|
+
module Metior
|
12
|
+
|
13
|
+
# This class implements a collection of commits and provides functionality
|
14
|
+
# specific to commits.
|
15
|
+
#
|
16
|
+
# @author Sebastian Staudt
|
17
|
+
# @see Commit
|
18
|
+
class CommitCollection < Collection
|
19
|
+
|
20
|
+
# Creates a new collection with the given commits
|
21
|
+
#
|
22
|
+
# @param [Array<Commit>] commits The commits that should be initially
|
23
|
+
# inserted into the collection
|
24
|
+
def initialize(commits = [])
|
25
|
+
@additions = nil
|
26
|
+
@deletions = nil
|
27
|
+
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
# Adds a commit to this collection
|
32
|
+
#
|
33
|
+
# @param [Commit] commit The commit to add to this collection
|
34
|
+
# @return [CommitCollection] The collection itself
|
35
|
+
def <<(commit)
|
36
|
+
return self if key? commit.id
|
37
|
+
|
38
|
+
unless @additions.nil?
|
39
|
+
@additions += commit.additions
|
40
|
+
@deletions += commit.deletions
|
41
|
+
end
|
42
|
+
|
43
|
+
super
|
44
|
+
end
|
45
|
+
|
46
|
+
# Calculate some predefined activity statistics for the commits in this
|
47
|
+
# collection
|
48
|
+
#
|
49
|
+
# @return [Hash<Symbol, Object>] The calculated statistics for the commits
|
50
|
+
# in this collection
|
51
|
+
# @see Commit#committed_date
|
52
|
+
def activity
|
53
|
+
activity = {}
|
54
|
+
commit_count = values.size
|
55
|
+
|
56
|
+
active_days = {}
|
57
|
+
each do |commit|
|
58
|
+
date = commit.committed_date.utc
|
59
|
+
day = Time.utc(date.year, date.month, date.day)
|
60
|
+
if active_days.key? day
|
61
|
+
active_days[day] += 1
|
62
|
+
else
|
63
|
+
active_days[day] = 1
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
most_active_day = active_days.sort_by { |day, count| count }.last.first
|
68
|
+
|
69
|
+
activity[:first_commit_date] = last.committed_date
|
70
|
+
activity[:last_commit_date] = first.committed_date
|
71
|
+
|
72
|
+
age_in_days = (Time.now - activity[:first_commit_date]) / 86400.0
|
73
|
+
|
74
|
+
activity[:active_days] = active_days
|
75
|
+
activity[:most_active_day] = most_active_day
|
76
|
+
activity[:commits_per_day] = commit_count / age_in_days
|
77
|
+
activity[:commits_per_active_day] = commit_count.to_f / active_days.size
|
78
|
+
|
79
|
+
activity
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns the lines of code that have been added by the commits in this
|
83
|
+
# collection
|
84
|
+
#
|
85
|
+
# This will load the line stats from the commits if not done yet.
|
86
|
+
#
|
87
|
+
# @return [Fixnum] The lines of code that have been added
|
88
|
+
# @see #load_line_stats
|
89
|
+
def additions
|
90
|
+
first.support! :line_stats
|
91
|
+
|
92
|
+
load_line_stats if @additions.nil?
|
93
|
+
@additions
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns the commits in this collection that have been committed after the
|
97
|
+
# given time
|
98
|
+
#
|
99
|
+
# @param [Time, Date, DateTime, String] date The time to use as the lower
|
100
|
+
# limit to filter the commits
|
101
|
+
# @return [CommitCollection] The commits that have been committed after the
|
102
|
+
# given date
|
103
|
+
# @see Commit#committed_date
|
104
|
+
# @see Time.parse
|
105
|
+
def after(date)
|
106
|
+
date = Time.parse date if date.is_a? String
|
107
|
+
commits = CommitCollection.new
|
108
|
+
each do |commit|
|
109
|
+
commits << commit if commit.committed_date > date
|
110
|
+
end
|
111
|
+
commits
|
112
|
+
end
|
113
|
+
alias_method :newer, :after
|
114
|
+
|
115
|
+
# Returns the authors of all or a specific commit in this collection
|
116
|
+
#
|
117
|
+
# @param [Object] commit_id The ID of the commit, if only the author of a
|
118
|
+
# specific commit should be returned
|
119
|
+
# @return [ActorCollection] All authors of the commits in this collection
|
120
|
+
# or the author of a specific commit
|
121
|
+
# @see Commit#author
|
122
|
+
def authors(commit_id = nil)
|
123
|
+
authors = ActorCollection.new
|
124
|
+
if commit_id.nil?
|
125
|
+
each { |commit| authors << commit.author }
|
126
|
+
elsif key? commit_id
|
127
|
+
authors << self[commit_id].author
|
128
|
+
end
|
129
|
+
authors
|
130
|
+
end
|
131
|
+
|
132
|
+
# Returns the commits in this collection that have been committed before
|
133
|
+
# the given time
|
134
|
+
#
|
135
|
+
# @param [Time, Date, DateTime, String] date The time to use as the upper
|
136
|
+
# limit to filter the commits
|
137
|
+
# @return [CommitCollection] The commits that have been committed after the
|
138
|
+
# given date
|
139
|
+
# @see Commit#committed_date
|
140
|
+
# @see Time.parse
|
141
|
+
def before(date)
|
142
|
+
date = Time.parse date if date.is_a? String
|
143
|
+
commits = CommitCollection.new
|
144
|
+
each do |commit|
|
145
|
+
commits << commit if commit.committed_date < date
|
146
|
+
end
|
147
|
+
commits
|
148
|
+
end
|
149
|
+
alias_method :older, :before
|
150
|
+
|
151
|
+
# Returns the list of commits that have been authored by the given authors
|
152
|
+
#
|
153
|
+
# @param [Array<Actor, Object>] author_ids One or more actual `Actor`
|
154
|
+
# instances or IDs of the authors that the commits should be
|
155
|
+
# filtered by
|
156
|
+
# @return [CommitCollection] The commits that have been authored by the
|
157
|
+
# given authors
|
158
|
+
# @see Commit#author
|
159
|
+
def by(*author_ids)
|
160
|
+
author_ids = author_ids.flatten.map do |author_id|
|
161
|
+
author_id.is_a?(Actor) ? author_id.id : author_id
|
162
|
+
end
|
163
|
+
commits = CommitCollection.new
|
164
|
+
each do |commit|
|
165
|
+
commits << commit if author_ids.include? commit.author.id
|
166
|
+
end
|
167
|
+
commits
|
168
|
+
end
|
169
|
+
|
170
|
+
# Returns the commits in this collection that change any of the given files
|
171
|
+
#
|
172
|
+
# @param [Array<String>] files The path of the files to filter commits by
|
173
|
+
# @return [CommitCollection] The commits that contain changes to the given
|
174
|
+
# files
|
175
|
+
# @see Commit#added_files
|
176
|
+
# @see Commit#deleted_files
|
177
|
+
# @see Commit#modified_files
|
178
|
+
def changing(*files)
|
179
|
+
first.support! :file_stats
|
180
|
+
|
181
|
+
commits = CommitCollection.new
|
182
|
+
each do |commit|
|
183
|
+
commit_files = commit.added_files + commit.deleted_files + commit.modified_files
|
184
|
+
commits << commit unless (commit_files & files).empty?
|
185
|
+
end
|
186
|
+
commits
|
187
|
+
end
|
188
|
+
alias_method :touching, :changing
|
189
|
+
|
190
|
+
# Returns the committers of all or a specific commit in this collection
|
191
|
+
#
|
192
|
+
# @param [Object] commit_id The ID of the commit, if only the committer of
|
193
|
+
# a specific commit should be returned
|
194
|
+
# @return [ActorCollection] All committers of the commits in this
|
195
|
+
# collection or the committer of a specific commit
|
196
|
+
# @see Commit#committer
|
197
|
+
def committers(commit_id = nil)
|
198
|
+
committers = ActorCollection.new
|
199
|
+
if commit_id.nil?
|
200
|
+
each { |commit| committers << commit.committer }
|
201
|
+
elsif key? commit_id
|
202
|
+
committers << self[commit_id].committer
|
203
|
+
end
|
204
|
+
committers
|
205
|
+
end
|
206
|
+
|
207
|
+
# Returns the lines of code that have been deleted by the commits in this
|
208
|
+
# collection
|
209
|
+
#
|
210
|
+
# This will load the line stats from the commits if not done yet.
|
211
|
+
#
|
212
|
+
# @return [Fixnum] The lines of code that have been deleted
|
213
|
+
# @see #load_line_stats
|
214
|
+
def deletions
|
215
|
+
first.support! :line_stats
|
216
|
+
|
217
|
+
load_line_stats if @deletions.nil?
|
218
|
+
@deletions
|
219
|
+
end
|
220
|
+
|
221
|
+
# This evaluates the changed lines in each commit of this collection
|
222
|
+
#
|
223
|
+
# For easier use, the values are stored in separate arrays where each
|
224
|
+
# number represents the number of changed (i.e. added or deleted) lines in
|
225
|
+
# one commit.
|
226
|
+
#
|
227
|
+
# @example
|
228
|
+
# commits.line_history
|
229
|
+
# => { :additions => [10, 5, 0], :deletions => [0, -2, -1] }
|
230
|
+
# @return [Hash<Symbol, Array>] Added lines are returned in an `Array`
|
231
|
+
# assigned to key `:additions`, deleted lines are assigned to
|
232
|
+
# `:deletions`
|
233
|
+
# @see Commit#additions
|
234
|
+
# @see Commit#deletions
|
235
|
+
def line_history
|
236
|
+
first.support! :line_stats
|
237
|
+
|
238
|
+
history = { :additions => [], :deletions => [] }
|
239
|
+
values.reverse.each do |commit|
|
240
|
+
history[:additions] << commit.additions
|
241
|
+
history[:deletions] << -commit.deletions
|
242
|
+
end
|
243
|
+
|
244
|
+
history
|
245
|
+
end
|
246
|
+
|
247
|
+
# Returns the total of lines changed by the commits in this collection
|
248
|
+
#
|
249
|
+
# @return [Fixnum] The total number of lines changed
|
250
|
+
# @see #additions
|
251
|
+
# @see #deletions
|
252
|
+
def modifications
|
253
|
+
additions + deletions
|
254
|
+
end
|
255
|
+
|
256
|
+
# Returns the given number of commits with most line changes on the
|
257
|
+
# repository
|
258
|
+
#
|
259
|
+
# @param [Numeric] count The number of commits to return
|
260
|
+
# @return [CommitCollection] The given number of commits ordered by impact
|
261
|
+
# @see Commit#modifications
|
262
|
+
def most_significant(count = 10)
|
263
|
+
first.support! :line_stats
|
264
|
+
|
265
|
+
commits = CommitCollection.new
|
266
|
+
sort_by { |commit| -commit.modifications }.each do |commit|
|
267
|
+
commits << commit
|
268
|
+
break if commits.size == count
|
269
|
+
end
|
270
|
+
commits
|
271
|
+
end
|
272
|
+
alias_method :top, :most_significant
|
273
|
+
|
274
|
+
# Returns the commits in this collection that change at least the given
|
275
|
+
# number of lines
|
276
|
+
#
|
277
|
+
# @param [Numeric] line_count The number of lines that should be
|
278
|
+
# changed at least by the commits
|
279
|
+
# @return [CommitCollection] The commits that change at least the given
|
280
|
+
# number of lines
|
281
|
+
# @see Commit#modifications
|
282
|
+
def with_impact(line_count)
|
283
|
+
first.support! :line_stats
|
284
|
+
|
285
|
+
commits = CommitCollection.new
|
286
|
+
each do |commit|
|
287
|
+
commits << commit if commit.modifications >= line_count
|
288
|
+
end
|
289
|
+
commits
|
290
|
+
end
|
291
|
+
|
292
|
+
private
|
293
|
+
|
294
|
+
# Loads the line stats for all commits in this collection
|
295
|
+
#
|
296
|
+
# @see Commit#additions
|
297
|
+
# @see Commit#deletions
|
298
|
+
def load_line_stats
|
299
|
+
@additions = 0
|
300
|
+
@deletions = 0
|
301
|
+
each do |commit|
|
302
|
+
@additions += commit.additions
|
303
|
+
@deletions += commit.deletions
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
end
|
308
|
+
|
309
|
+
end
|