metior 0.1.4 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|