churn 0.0.12 → 0.0.13

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -87,6 +87,8 @@ TODO:
87
87
  * add a filter that allows for other files besides. *.rb
88
88
  * ignore files pattern, so you can ignore things like vendor/, lib/, or docs/
89
89
  * finish adding better documenation using YARD
90
+ * better man page formatting from README (switch to markdown?)
91
+ * rake task for building manpage (currently manually run ronn -b1 README.rdoc)
90
92
 
91
93
  Executable Usage:
92
94
  * 'gem install churn'
data/Rakefile CHANGED
@@ -11,12 +11,12 @@ begin
11
11
  gem.email = "dan@devver.net"
12
12
  gem.homepage = "http://github.com/danmayer/churn"
13
13
  gem.authors = ["Dan Mayer"]
14
- gem.add_development_dependency "thoughtbot-shoulda"
14
+ gem.add_development_dependency "shoulda"
15
15
  gem.add_development_dependency "test-construct"
16
16
  gem.add_development_dependency "mocha", '~> 0.9.5'
17
17
  gem.add_dependency "main"
18
18
  gem.add_dependency "json_pure"
19
- gem.add_dependency "chronic", '~> 0.2.3'
19
+ gem.add_dependency "chronic", '>= 0.2.3'
20
20
  gem.add_dependency "sexp_processor", '~> 3.0.3'
21
21
  gem.add_dependency "ruby_parser", '~> 2.0.4'
22
22
  gem.add_dependency 'hirb'
@@ -27,6 +27,38 @@ rescue LoadError
27
27
  puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
28
28
  end
29
29
 
30
+ begin
31
+ #for additional metrics, mostly Rcov which caliper doesn't do
32
+ require 'metric_fu'
33
+
34
+ MetricFu::Configuration.run do |config|
35
+ config.metrics = [:churn, :saikuro, :roodi, :flog, :flay, :reek, :roodi, :rcov, :hotspots]
36
+ config.graphs = [:roodi, :flog, :flay, :reek, :roodi, :rcov]
37
+
38
+ config.flay = { :dirs_to_flay => ['lib'] }
39
+ config.flog = { :dirs_to_flog => ['lib'] }
40
+ config.reek = { :dirs_to_reek => ['lib'] }
41
+ config.roodi = { :dirs_to_roodi => ['lib'] }
42
+ config.saikuro = { :output_directory => 'tmp/tmp_saikuro',
43
+ :input_directory => ['lib'],
44
+ :cyclo => "",
45
+ :filter_cyclo => "0",
46
+ :warn_cyclo => "5",
47
+ :error_cyclo => "7",
48
+ :formater => "text"} #this needs to be set to "text"
49
+ config.churn = { :start_date => "3 months ago", :minimum_churn_count => 10}
50
+ config.rcov = { :test_files => ['test/unit/**/*_test.rb'],
51
+ :rcov_opts => ["--sort coverage",
52
+ "--no-html",
53
+ "--text-coverage",
54
+ "--no-color",
55
+ "--profile",
56
+ "--exclude /gems/,spec"]}
57
+ end
58
+ rescue Exception
59
+ puts "metric_fu not working install it"
60
+ end
61
+
30
62
  require 'rake/testtask'
31
63
  Rake::TestTask.new(:test) do |test|
32
64
  test.libs << 'lib' << 'test'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.12
1
+ 0.0.13
@@ -15,6 +15,9 @@ require 'churn_history'
15
15
 
16
16
  module Churn
17
17
 
18
+ # The work horse of the the churn library. This class takes user input, determins the SCM the user is using. It then determines changes
19
+ # made during this revision. Finally it reads all the changes from previous revisions and displays human readable output to the command
20
+ # line. It can also ouput a yaml format readable by other tools such as metric_fu and Caliper.
18
21
  class ChurnCalculator
19
22
 
20
23
  # intialized the churn calculator object
@@ -63,46 +66,52 @@ module Churn
63
66
  hash[:churn][:class_churn] = @class_changes
64
67
  hash[:churn][:method_churn] = @method_changes
65
68
  #detail the most recent changes made this revision
66
- if @revision_changes[@revisions.first]
67
- changes = @revision_changes[@revisions.first]
69
+ first_revision = @revisions.first
70
+ first_revision_changes = @revision_changes[first_revision]
71
+ if first_revision_changes
72
+ changes = first_revision_changes
68
73
  hash[:churn][:changed_files] = changes[:files]
69
74
  hash[:churn][:changed_classes] = changes[:classes]
70
75
  hash[:churn][:changed_methods] = changes[:methods]
71
76
  end
72
77
  #TODO crappy place to do this but save hash to revision file but while entirely under metric_fu only choice
73
- ChurnHistory.store_revision_history(@revisions.first, hash)
78
+ ChurnHistory.store_revision_history(first_revision, hash)
74
79
  hash
75
80
  end
76
81
 
77
82
  # Pretty print the data as a string for the user
78
83
  def to_s
79
- hash = to_h
84
+ hash = to_h[:churn]
80
85
  result = seperator
81
86
  result +="* Revision Changes \n"
82
87
  result += seperator
83
88
  result += "Files: \n"
84
- result += display_array(hash[:churn][:changed_files], :fields=>[:to_str], :headers=>{:to_str=>'file'})
89
+ result += display_array(hash[:changed_files], :fields=>[:to_str], :headers=>{:to_str=>'file'})
85
90
  result += "\nClasses: \n"
86
- result += display_array(hash[:churn][:changed_classes])
91
+ result += display_array(hash[:changed_classes])
87
92
  result += "\nMethods: \n"
88
- result += display_array(hash[:churn][:changed_methods]) + "\n"
93
+ result += display_array(hash[:changed_methods]) + "\n"
89
94
  result += seperator
90
95
  result +="* Project Churn \n"
91
96
  result += seperator
92
97
  result += "Files: \n"
93
- result += display_array(hash[:churn][:changes])
98
+ result += display_array(hash[:changes])
94
99
  result += "\nClasses: \n"
95
- class_churn = hash[:churn][:class_churn].map {|e| (e.delete('klass') || {}).merge(e) }
100
+ class_churn = collect_items(hash[:class_churn], 'klass')
96
101
  result += display_array(class_churn)
97
102
  result += "\nMethods: \n"
98
- method_churn = hash[:churn][:method_churn].map {|e| (e.delete('method') || {}).merge(e) }
103
+ method_churn = collect_items(hash[:method_churn], 'method')
99
104
  result += display_array(method_churn)
100
105
  end
101
106
 
102
107
  private
108
+
109
+ def collect_items(collection, match)
110
+ collection.map {|item| (item.delete(match) || {}).merge(item) }
111
+ end
103
112
 
104
113
  def sort_changes(changes)
105
- changes.to_a.sort! {|x,y| y[1] <=> x[1]}
114
+ changes.to_a.sort! {|first,second| second[1] <=> first[1]}
106
115
  end
107
116
 
108
117
  def filters
@@ -226,7 +235,7 @@ module Churn
226
235
  end
227
236
 
228
237
  def parse_logs_for_updated_files(revision, revisions)
229
- #SVN doesn't support this
238
+ #TODO SVN doesn't support this
230
239
  return {} unless @source_control.respond_to?(:get_updated_files_change_info)
231
240
  @source_control.get_updated_files_change_info(revision, revisions)
232
241
  end
@@ -1,12 +1,16 @@
1
1
  module Churn
2
2
 
3
+ # responcible for storing the churn history to json,
4
+ # and for loading old churn history data from json.
3
5
  class ChurnHistory
4
6
 
7
+ #takes current revision and it's hash_data and stores it
5
8
  def self.store_revision_history(revision, hash_data)
6
9
  FileUtils.mkdir 'tmp' unless File.directory?('tmp')
7
- File.open("tmp/#{revision}.json", 'w') {|f| f.write(hash_data.to_json) }
10
+ File.open("tmp/#{revision}.json", 'w') {|file| file.write(hash_data.to_json) }
8
11
  end
9
12
 
13
+ #given a previous project revision find and load the churn data from a json file
10
14
  def self.load_revision_data(revision)
11
15
  #load revision data from scratch folder if it exists
12
16
  filename = "tmp/#{revision}.json"
@@ -1,5 +1,6 @@
1
1
  module Churn
2
2
 
3
+ #analizes git SCM to find recently changed files, and what lines have been altered
3
4
  class GitAnalyzer < SourceControl
4
5
  def get_logs
5
6
  `git log #{date_range} --name-only --pretty=format:`.split(/\n/).reject{|line| line == ""}
@@ -9,56 +10,10 @@ module Churn
9
10
  `git log #{date_range} --pretty=format:"%H"`.split(/\n/).reject{|line| line == ""}
10
11
  end
11
12
 
12
- def get_updated_files_from_log(revision, revisions)
13
- current_index = revisions.index(revision)
14
- previous_index = current_index+1
15
- previous_revision = revisions[previous_index] unless revisions.length < previous_index
16
- if revision && previous_revision
17
- `git diff #{revision} #{previous_revision} --unified=0`.split(/\n/).select{|line| line.match(/^@@/) || line.match(/^---/) || line.match(/^\+\+\+/) }
18
- else
19
- []
20
- end
21
- end
22
-
23
- def get_updated_files_change_info(revision, revisions)
24
- updated = {}
25
- logs = get_updated_files_from_log(revision, revisions)
26
- recent_file = nil
27
- logs.each do |line|
28
- if line.match(/^---/) || line.match(/^\+\+\+/)
29
- recent_file = get_recent_file(line)
30
- updated[recent_file] = [] unless updated.include?(recent_file)
31
- elsif line.match(/^@@/)
32
- removed_range = get_changed_range(line, '-')
33
- added_range = get_changed_range(line, '\+')
34
- updated[recent_file] << removed_range
35
- updated[recent_file] << added_range
36
- else
37
- puts line.match(/^---/)
38
- raise "git diff lines that don't match the two patterns aren't expected: '#{line}'"
39
- end
40
- end
41
- updated
42
- end
43
-
44
13
  private
45
14
 
46
- def get_changed_range(line, matcher)
47
- change_start = line.match(/#{matcher}[0-9]+/)
48
- change_end = line.match(/#{matcher}[0-9]+,[0-9]+/)
49
- change_start = change_start.to_s.gsub(/#{matcher}/,'')
50
- change_end = change_end.to_s.gsub(/.*,/,'')
51
-
52
- range = if change_end && change_end!=''
53
- (change_start.to_i..(change_start.to_i+change_end.to_i))
54
- else
55
- (change_start.to_i..change_start.to_i)
56
- end
57
- range
58
- end
59
-
60
- def get_recent_file(line)
61
- line = line.gsub(/^--- /,'').gsub(/^\+\+\+ /,'').gsub(/^a\//,'').gsub(/^b\//,'')
15
+ def get_diff(revision, previous_revision)
16
+ `git diff #{revision} #{previous_revision} --unified=0`.split(/\n/).select{|line| line.match(/^@@/) || line.match(/^---/) || line.match(/^\+\+\+/) }
62
17
  end
63
18
 
64
19
  def date_range
@@ -69,5 +24,4 @@ module Churn
69
24
  end
70
25
 
71
26
  end
72
-
73
27
  end
@@ -1,48 +1,21 @@
1
1
  module Churn
2
+
3
+ #analizes Hg / Mercurial SCM to find recently changed files, and what lines have been altered
2
4
  class HgAnalyzer < SourceControl
3
5
  def get_logs
4
- `hg log -v#{date_range}`.split("\n").reject{|line| line !~ /^files:/}.map{|l| l.split(" ")[1..-1]}.flatten
6
+ `hg log -v#{date_range}`.split("\n").reject{|line| line !~ /^files:/}.map{|line| line.split(" ")[1..-1]}.flatten
5
7
  end
6
8
 
7
9
  def get_revisions
8
- `hg log#{date_range}`.split("\n").reject{|line| line !~ /^changeset:/}.map{|l| l[/:(\S+)$/, 1] }
10
+ `hg log#{date_range}`.split("\n").reject{|line| line !~ /^changeset:/}.map{|line| line[/:(\S+)$/, 1] }
9
11
  end
10
12
 
11
- def get_updated_files_from_log(revision, revisions)
12
- current_index = revisions.index(revision)
13
- previous_index = current_index+1
14
- previous_revision = revisions[previous_index] unless revisions.length < previous_index
15
- if revision && previous_revision
16
- `hg diff -r #{revision}:#{previous_revision} -U 0`.split(/\n/).select{|line| line.match(/^@@/) || line.match(/^---/) || line.match(/^\+\+\+/) }
17
- else
18
- []
19
- end
20
- end
13
+ private
21
14
 
22
- def get_updated_files_change_info(revision, revisions)
23
- updated = {}
24
- logs = get_updated_files_from_log(revision, revisions)
25
- recent_file = nil
26
- logs.each do |line|
27
- if line.match(/^---/) || line.match(/^\+\+\+/)
28
- # Remove the --- a/ and +++ b/ if present
29
- recent_file = get_recent_file(line)
30
- updated[recent_file] = [] unless updated.include?(recent_file)
31
- elsif line.match(/^@@/)
32
- # Now add the added/removed ranges for the line
33
- removed_range = get_changed_range(line, '-')
34
- added_range = get_changed_range(line, '\+')
35
- updated[recent_file] << removed_range
36
- updated[recent_file] << added_range
37
- else
38
- puts line.match(/^---/)
39
- raise "hg diff lines that don't match the two patterns aren't expected: '#{line}'"
40
- end
41
- end
42
- updated
15
+ def get_diff(revision, previous_revision)
16
+ `hg diff -r #{revision}:#{previous_revision} -U 0`.split(/\n/).select{|line| line.match(/^@@/) || line.match(/^---/) || line.match(/^\+\+\+/) }
43
17
  end
44
18
 
45
- private
46
19
  def date_range
47
20
  if @start_date
48
21
  date = Chronic.parse(@start_date)
@@ -51,21 +24,8 @@ module Churn
51
24
  end
52
25
 
53
26
  def get_recent_file(line)
54
- line = line.gsub(/^--- /,'').gsub(/^\+\+\+ /,'').gsub(/^a\//,'').gsub(/^b\//,'').split("\t")[0]
27
+ super(line).split("\t")[0]
55
28
  end
56
29
 
57
- def get_changed_range(line, matcher)
58
- change_start = line.match(/#{matcher}[0-9]+/)
59
- change_end = line.match(/#{matcher}[0-9]+,[0-9]+/)
60
- change_start = change_start.to_s.gsub(/#{matcher}/,'')
61
- change_end = change_end.to_s.gsub(/.*,/,'')
62
-
63
- range = if change_end && change_end!=''
64
- (change_start.to_i..(change_start.to_i+change_end.to_i))
65
- else
66
- (change_start.to_i..change_start.to_i)
67
- end
68
- range
69
- end
70
30
  end
71
31
  end
@@ -1,5 +1,8 @@
1
1
  module Churn
2
2
 
3
+ # Given a ruby file, map the klass and methods to a range of line numbers
4
+ # The klass and method to line numbers mappings, are stored in
5
+ # @klasses_collection and @methods_collection
3
6
  class LocationMapping < SexpProcessor
4
7
 
5
8
  attr_reader :klasses_collection, :methods_collection
@@ -1,9 +1,66 @@
1
1
  module Churn
2
2
 
3
+ # Base clase for analyzing various SCM systems like git, HG, and SVN
3
4
  class SourceControl
4
5
  def initialize(start_date=nil)
5
6
  @start_date = start_date
6
7
  end
8
+
9
+ def get_updated_files_change_info(revision, revisions)
10
+ updated = {}
11
+ logs = get_updated_files_from_log(revision, revisions)
12
+ recent_file = nil
13
+ logs.each do |line|
14
+ if line.match(/^---/) || line.match(/^\+\+\+/)
15
+ # Remove the --- a/ and +++ b/ if present
16
+ recent_file = get_recent_file(line)
17
+ updated[recent_file] = [] unless updated.include?(recent_file)
18
+ elsif line.match(/^@@/)
19
+ # Now add the added/removed ranges for the line
20
+ removed_range = get_changed_range(line, '-')
21
+ added_range = get_changed_range(line, '\+')
22
+ updated[recent_file] << removed_range
23
+ updated[recent_file] << added_range
24
+ else
25
+ puts line.match(/^---/)
26
+ raise "diff lines that don't match the two patterns aren't expected: '#{line}'"
27
+ end
28
+ end
29
+ updated
30
+ end
31
+
32
+ def get_updated_files_from_log(revision, revisions)
33
+ current_index = revisions.index(revision)
34
+ previous_index = current_index+1
35
+ previous_revision = revisions[previous_index] unless revisions.length < previous_index
36
+ if revision && previous_revision
37
+ get_diff(revision, previous_revision)
38
+ else
39
+ []
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def get_changed_range(line, matcher)
46
+ change_start = line.match(/#{matcher}[0-9]+/)
47
+ change_end = line.match(/#{matcher}[0-9]+,[0-9]+/)
48
+ change_start = change_start.to_s.gsub(/#{matcher}/,'')
49
+ change_end = change_end.to_s.gsub(/.*,/,'')
50
+
51
+ change_start_num = change_start.to_i
52
+ range = if change_end && change_end!=''
53
+ (change_start_num..(change_start_num+change_end.to_i))
54
+ else
55
+ (change_start_num..change_start_num)
56
+ end
57
+ range
58
+ end
59
+
60
+ def get_recent_file(line)
61
+ line = line.gsub(/^--- /,'').gsub(/^\+\+\+ /,'').gsub(/^a\//,'').gsub(/^b\//,'')
62
+ end
63
+
7
64
  end
8
65
 
9
66
  end
@@ -1,5 +1,6 @@
1
1
  module Churn
2
2
 
3
+ #analizes SVN SCM to find recently changed files, and what lines have been altered
3
4
  class SvnAnalyzer < SourceControl
4
5
  def get_logs
5
6
  `svn log #{date_range} --verbose`.split(/\n/).map { |line| clean_up_svn_line(line) }.compact
@@ -14,8 +15,8 @@ module Churn
14
15
  end
15
16
 
16
17
  def clean_up_svn_line(line)
17
- m = line.match(/\W*[A,M]\W+(\/.*)\b/)
18
- m ? m[1] : nil
18
+ match = line.match(/\W*[A,M]\W+(\/.*)\b/)
19
+ match ? match[1] : nil
19
20
  end
20
21
  end
21
22
 
data/man/churn.1 ADDED
@@ -0,0 +1,149 @@
1
+ .\" generated with Ronn/v0.4.1
2
+ .\" http://github.com/rtomayko/ronn/
3
+ .
4
+ .TH "README" "" "January 2010" "" ""
5
+ = churn
6
+ .
7
+ .P
8
+ A Project to give the churn file, class, and method for a project for a given checkin
9
+ Over time the tool adds up the history of chruns to give the number of times a file, class, or method is changing during the life of a project.
10
+ Churn for files is immediate, but classes and methods requires buildings up a history using churn between revisions. The history is stored in ./tmp
11
+ .
12
+ .P
13
+ Currently has Full Git and Mercurial (hg) support, and partial SVN support (supports only file level churn currnetly)
14
+ .
15
+ .P
16
+ Authors:
17
+ * danmayer
18
+ * ajwalters
19
+ * cldwalker
20
+ .
21
+ .P
22
+ == Example Output
23
+ .
24
+ .IP "\(bu" 4
25
+ Revision Changes
26
+ .
27
+ .IP "" 0
28
+ .
29
+ .P
30
+ Files:
31
+ +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
32
+ | file |
33
+ +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
34
+ | Rakefile |
35
+ | lib/churn/churn_calculator.rb |
36
+ +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
37
+ .
38
+ .P
39
+ Classes:
40
+ +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
41
+ | file | klass |
42
+ +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
43
+ | lib/churn/churn_calculator.rb | ChurnCalculator |
44
+ +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
45
+ .
46
+ .P
47
+ Methods:
48
+ +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
49
+ | file | klass | method |
50
+ +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
51
+ | lib/churn/churn_calculator.rb | ChurnCalculator | ChurnCalculator#filters |
52
+ | lib/churn/churn_calculator.rb | ChurnCalculator | ChurnCalculator#display_array |
53
+ | lib/churn/churn_calculator.rb | ChurnCalculator | ChurnCalculator#to_s |
54
+ +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
55
+ .
56
+ .IP "\(bu" 4
57
+ Project Churn
58
+ .
59
+ .IP "" 0
60
+ .
61
+ .P
62
+ Files:
63
+ +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
64
+ | file_path | times_changed |
65
+ +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
66
+ | lib/churn/churn_calculator.rb | 14 |
67
+ | README.rdoc | 7 |
68
+ | lib/tasks/churn_tasks.rb | 6 |
69
+ | Rakefile | 6 |
70
+ | lib/churn/git_analyzer.rb | 4 |
71
+ | VERSION | 4 |
72
+ | test/test_helper.rb | 4 |
73
+ | test/unit/churn_calculator_test.rb | 3 |
74
+ | test/churn_test.rb | 3 |
75
+ +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
76
+ .
77
+ .P
78
+ Classes:
79
+ +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
80
+ | file | klass | times_changed |
81
+ +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
82
+ | lib/churn/churn_calculator.rb | ChurnCalculator | 1 |
83
+ | lib/churn/churn_calculator.rb | ChurnCalculator | 1 |
84
+ +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
85
+ .
86
+ .P
87
+ Methods:
88
+ +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
89
+ | file | klass | method | times_changed |
90
+ +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
91
+ | lib/churn/churn_calculator.rb | ChurnCalculator | ChurnCalculator#to_s | 1 |
92
+ | lib/churn/churn_calculator.rb | ChurnCalculator | ChurnCalculator#display_array | 1 |
93
+ | lib/churn/churn_calculator.rb | ChurnCalculator | ChurnCalculator#calculate_revision_data | 1 |
94
+ | lib/churn/churn_calculator.rb | ChurnCalculator | ChurnCalculator#filters | 1 |
95
+ | lib/churn/churn_calculator.rb | ChurnCalculator | ChurnCalculator#initialize | 1 |
96
+ | lib/churn/churn_calculator.rb | ChurnCalculator | ChurnCalculator#filters | 1 |
97
+ | lib/churn/churn_calculator.rb | ChurnCalculator | ChurnCalculator#to_s | 1 |
98
+ +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
99
+ .
100
+ .P
101
+ TODO:
102
+ * SVN only supports file, add full SVN support
103
+ * support bazaar, cvs, and darcs
104
+ * make storage directory configurable instead of using tmp
105
+ * allow passing in directories to churn, directories to ignore
106
+ * add a filter that allows for other files besides. *.rb
107
+ * ignore files pattern, so you can ignore things like vendor/, lib/, or docs/
108
+ * finish adding better documenation using YARD
109
+ .
110
+ .P
111
+ Executable Usage:
112
+ * 'gem install churn'
113
+ * go to project root run 'churn'
114
+ .
115
+ .P
116
+ Rake Usage:
117
+ * 'gem install churn'
118
+ * on any project you want to use churn, add "require 'churn'" to your rake file
119
+ * run 'rake churn' to view the current output, file churn history is immediate, class and method churn builds up a history as it is run on each revision
120
+ * temporary files with class / method churn history are stored in /tmp, to clear churn history delete them
121
+ .
122
+ .P
123
+ == Note on Patches/Pull Requests
124
+ .
125
+ .IP "\(bu" 4
126
+ Fork the project.
127
+ .
128
+ .IP "\(bu" 4
129
+ Make your feature addition or bug fix.
130
+ .
131
+ .IP "\(bu" 4
132
+ Add tests for it. This is important so I don't break it in a
133
+ future version unintentionally.
134
+ .
135
+ .IP "\(bu" 4
136
+ Commit, do not mess with rakefile, version, or history.
137
+ (if you want to have your own version, that is fine but
138
+ bump version in a commit by itself I can ignore when I pull)
139
+ .
140
+ .IP "\(bu" 4
141
+ Send me a pull request. Bonus points for topic branches.
142
+ .
143
+ .IP "" 0
144
+ .
145
+ .P
146
+ == Copyright
147
+ .
148
+ .P
149
+ Copyright (c) 2010 Dan Mayer. See LICENSE for details.
data/man/churn.html ADDED
@@ -0,0 +1,541 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
2
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
4
+ <head>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
6
+ <meta name="generator" content="AsciiDoc 8.4.4" />
7
+ <title>churn</title>
8
+ <style type="text/css">
9
+ /* Debug borders */
10
+ p, li, dt, dd, div, pre, h1, h2, h3, h4, h5, h6 {
11
+ /*
12
+ border: 1px solid red;
13
+ */
14
+ }
15
+
16
+ body {
17
+ margin: 1em 5% 1em 5%;
18
+ }
19
+
20
+ a {
21
+ color: blue;
22
+ text-decoration: underline;
23
+ }
24
+ a:visited {
25
+ color: fuchsia;
26
+ }
27
+
28
+ em {
29
+ font-style: italic;
30
+ color: navy;
31
+ }
32
+
33
+ strong {
34
+ font-weight: bold;
35
+ color: #083194;
36
+ }
37
+
38
+ tt {
39
+ color: navy;
40
+ }
41
+
42
+ h1, h2, h3, h4, h5, h6 {
43
+ color: #527bbd;
44
+ font-family: sans-serif;
45
+ margin-top: 1.2em;
46
+ margin-bottom: 0.5em;
47
+ line-height: 1.3;
48
+ }
49
+
50
+ h1, h2, h3 {
51
+ border-bottom: 2px solid silver;
52
+ }
53
+ h2 {
54
+ padding-top: 0.5em;
55
+ }
56
+ h3 {
57
+ float: left;
58
+ }
59
+ h3 + * {
60
+ clear: left;
61
+ }
62
+
63
+ div.sectionbody {
64
+ font-family: serif;
65
+ margin-left: 0;
66
+ }
67
+
68
+ hr {
69
+ border: 1px solid silver;
70
+ }
71
+
72
+ p {
73
+ margin-top: 0.5em;
74
+ margin-bottom: 0.5em;
75
+ }
76
+
77
+ ul, ol, li > p {
78
+ margin-top: 0;
79
+ }
80
+
81
+ pre {
82
+ padding: 0;
83
+ margin: 0;
84
+ }
85
+
86
+ span#author {
87
+ color: #527bbd;
88
+ font-family: sans-serif;
89
+ font-weight: bold;
90
+ font-size: 1.1em;
91
+ }
92
+ span#email {
93
+ }
94
+ span#revision {
95
+ font-family: sans-serif;
96
+ }
97
+
98
+ div#footer {
99
+ font-family: sans-serif;
100
+ font-size: small;
101
+ border-top: 2px solid silver;
102
+ padding-top: 0.5em;
103
+ margin-top: 4.0em;
104
+ }
105
+ div#footer-text {
106
+ float: left;
107
+ padding-bottom: 0.5em;
108
+ }
109
+ div#footer-badges {
110
+ float: right;
111
+ padding-bottom: 0.5em;
112
+ }
113
+
114
+ div#preamble {
115
+ margin-top: 1.5em;
116
+ margin-bottom: 1.5em;
117
+ }
118
+ div.tableblock, div.imageblock, div.exampleblock, div.verseblock,
119
+ div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
120
+ div.admonitionblock {
121
+ margin-top: 1.5em;
122
+ margin-bottom: 1.5em;
123
+ }
124
+ div.admonitionblock {
125
+ margin-top: 2.5em;
126
+ margin-bottom: 2.5em;
127
+ }
128
+
129
+ div.content { /* Block element content. */
130
+ padding: 0;
131
+ }
132
+
133
+ /* Block element titles. */
134
+ div.title, caption.title {
135
+ color: #527bbd;
136
+ font-family: sans-serif;
137
+ font-weight: bold;
138
+ text-align: left;
139
+ margin-top: 1.0em;
140
+ margin-bottom: 0.5em;
141
+ }
142
+ div.title + * {
143
+ margin-top: 0;
144
+ }
145
+
146
+ td div.title:first-child {
147
+ margin-top: 0.0em;
148
+ }
149
+ div.content div.title:first-child {
150
+ margin-top: 0.0em;
151
+ }
152
+ div.content + div.title {
153
+ margin-top: 0.0em;
154
+ }
155
+
156
+ div.sidebarblock > div.content {
157
+ background: #ffffee;
158
+ border: 1px solid silver;
159
+ padding: 0.5em;
160
+ }
161
+
162
+ div.listingblock > div.content {
163
+ border: 1px solid silver;
164
+ background: #f4f4f4;
165
+ padding: 0.5em;
166
+ }
167
+
168
+ div.quoteblock {
169
+ padding-left: 2.0em;
170
+ margin-right: 10%;
171
+ }
172
+ div.quoteblock > div.attribution {
173
+ padding-top: 0.5em;
174
+ text-align: right;
175
+ }
176
+
177
+ div.verseblock {
178
+ padding-left: 2.0em;
179
+ margin-right: 10%;
180
+ }
181
+ div.verseblock > div.content {
182
+ white-space: pre;
183
+ }
184
+ div.verseblock > div.attribution {
185
+ padding-top: 0.75em;
186
+ text-align: left;
187
+ }
188
+ /* DEPRECATED: Pre version 8.2.7 verse style literal block. */
189
+ div.verseblock + div.attribution {
190
+ text-align: left;
191
+ }
192
+
193
+ div.admonitionblock .icon {
194
+ vertical-align: top;
195
+ font-size: 1.1em;
196
+ font-weight: bold;
197
+ text-decoration: underline;
198
+ color: #527bbd;
199
+ padding-right: 0.5em;
200
+ }
201
+ div.admonitionblock td.content {
202
+ padding-left: 0.5em;
203
+ border-left: 2px solid silver;
204
+ }
205
+
206
+ div.exampleblock > div.content {
207
+ border-left: 2px solid silver;
208
+ padding: 0.5em;
209
+ }
210
+
211
+ div.imageblock div.content { padding-left: 0; }
212
+ div.imageblock img { border: 1px solid silver; }
213
+ span.image img { border-style: none; }
214
+
215
+ dl {
216
+ margin-top: 0.8em;
217
+ margin-bottom: 0.8em;
218
+ }
219
+ dt {
220
+ margin-top: 0.5em;
221
+ margin-bottom: 0;
222
+ font-style: normal;
223
+ color: navy;
224
+ }
225
+ dd > *:first-child {
226
+ margin-top: 0.1em;
227
+ }
228
+
229
+ ul, ol {
230
+ list-style-position: outside;
231
+ }
232
+ ol.arabic {
233
+ list-style-type: decimal;
234
+ }
235
+ ol.loweralpha {
236
+ list-style-type: lower-alpha;
237
+ }
238
+ ol.upperalpha {
239
+ list-style-type: upper-alpha;
240
+ }
241
+ ol.lowerroman {
242
+ list-style-type: lower-roman;
243
+ }
244
+ ol.upperroman {
245
+ list-style-type: upper-roman;
246
+ }
247
+
248
+ div.compact ul, div.compact ol,
249
+ div.compact p, div.compact p,
250
+ div.compact div, div.compact div {
251
+ margin-top: 0.1em;
252
+ margin-bottom: 0.1em;
253
+ }
254
+
255
+ div.tableblock > table {
256
+ border: 3px solid #527bbd;
257
+ }
258
+ thead {
259
+ font-family: sans-serif;
260
+ font-weight: bold;
261
+ }
262
+ tfoot {
263
+ font-weight: bold;
264
+ }
265
+ td > div.verse {
266
+ white-space: pre;
267
+ }
268
+ p.table {
269
+ margin-top: 0;
270
+ }
271
+ /* Because the table frame attribute is overriden by CSS in most browsers. */
272
+ div.tableblock > table[frame="void"] {
273
+ border-style: none;
274
+ }
275
+ div.tableblock > table[frame="hsides"] {
276
+ border-left-style: none;
277
+ border-right-style: none;
278
+ }
279
+ div.tableblock > table[frame="vsides"] {
280
+ border-top-style: none;
281
+ border-bottom-style: none;
282
+ }
283
+
284
+
285
+ div.hdlist {
286
+ margin-top: 0.8em;
287
+ margin-bottom: 0.8em;
288
+ }
289
+ div.hdlist tr {
290
+ padding-bottom: 15px;
291
+ }
292
+ dt.hdlist1.strong, td.hdlist1.strong {
293
+ font-weight: bold;
294
+ }
295
+ td.hdlist1 {
296
+ vertical-align: top;
297
+ font-style: normal;
298
+ padding-right: 0.8em;
299
+ color: navy;
300
+ }
301
+ td.hdlist2 {
302
+ vertical-align: top;
303
+ }
304
+ div.hdlist.compact tr {
305
+ margin: 0;
306
+ padding-bottom: 0;
307
+ }
308
+
309
+ .comment {
310
+ background: yellow;
311
+ }
312
+
313
+ @media print {
314
+ div#footer-badges { display: none; }
315
+ }
316
+
317
+ div#toctitle {
318
+ color: #527bbd;
319
+ font-family: sans-serif;
320
+ font-size: 1.1em;
321
+ font-weight: bold;
322
+ margin-top: 1.0em;
323
+ margin-bottom: 0.1em;
324
+ }
325
+
326
+ div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
327
+ margin-top: 0;
328
+ margin-bottom: 0;
329
+ }
330
+ div.toclevel2 {
331
+ margin-left: 2em;
332
+ font-size: 0.9em;
333
+ }
334
+ div.toclevel3 {
335
+ margin-left: 4em;
336
+ font-size: 0.9em;
337
+ }
338
+ div.toclevel4 {
339
+ margin-left: 6em;
340
+ font-size: 0.9em;
341
+ }
342
+ /* Workarounds for IE6's broken and incomplete CSS2. */
343
+
344
+ div.sidebar-content {
345
+ background: #ffffee;
346
+ border: 1px solid silver;
347
+ padding: 0.5em;
348
+ }
349
+ div.sidebar-title, div.image-title {
350
+ color: #527bbd;
351
+ font-family: sans-serif;
352
+ font-weight: bold;
353
+ margin-top: 0.0em;
354
+ margin-bottom: 0.5em;
355
+ }
356
+
357
+ div.listingblock div.content {
358
+ border: 1px solid silver;
359
+ background: #f4f4f4;
360
+ padding: 0.5em;
361
+ }
362
+
363
+ div.quoteblock-attribution {
364
+ padding-top: 0.5em;
365
+ text-align: right;
366
+ }
367
+
368
+ div.verseblock-content {
369
+ white-space: pre;
370
+ }
371
+ div.verseblock-attribution {
372
+ padding-top: 0.75em;
373
+ text-align: left;
374
+ }
375
+
376
+ div.exampleblock-content {
377
+ border-left: 2px solid silver;
378
+ padding-left: 0.5em;
379
+ }
380
+
381
+ /* IE6 sets dynamically generated links as visited. */
382
+ div#toc a:visited { color: blue; }
383
+ </style>
384
+ </head>
385
+ <body>
386
+ <div id="header">
387
+ <h1>churn</h1>
388
+ </div>
389
+ <div id="preamble">
390
+ <div class="sectionbody">
391
+ <div class="paragraph"><p>A Project to give the churn file, class, and method for a project for a given checkin
392
+ Over time the tool adds up the history of chruns to give the number of times a file, class, or method is changing during the life of a project.
393
+ Churn for files is immediate, but classes and methods requires buildings up a history using churn between revisions. The history is stored in ./tmp</p></div>
394
+ <div class="paragraph"><p>Currently has Full Git and Mercurial (hg) support, and partial SVN support (supports only file level churn currnetly)</p></div>
395
+ <div class="paragraph"><p>Authors:
396
+ * danmayer
397
+ * ajwalters
398
+ * cldwalker</p></div>
399
+ </div>
400
+ </div>
401
+ <h2 id="_example_output">Example Output</h2>
402
+ <div class="sectionbody">
403
+ <div class="literalblock">
404
+ <div class="content">
405
+ <pre><tt>**********************************************************************
406
+ * Revision Changes
407
+ **********************************************************************
408
+ Files:
409
+ +-------------------------------+
410
+ | file |
411
+ +-------------------------------+
412
+ | Rakefile |
413
+ | lib/churn/churn_calculator.rb |
414
+ +-------------------------------+</tt></pre>
415
+ </div></div>
416
+ <div class="literalblock">
417
+ <div class="content">
418
+ <pre><tt>Classes:
419
+ +-------------------------------+-----------------+
420
+ | file | klass |
421
+ +-------------------------------+-----------------+
422
+ | lib/churn/churn_calculator.rb | ChurnCalculator |
423
+ +-------------------------------+-----------------+</tt></pre>
424
+ </div></div>
425
+ <div class="literalblock">
426
+ <div class="content">
427
+ <pre><tt>Methods:
428
+ +-------------------------------+-----------------+-------------------------------+
429
+ | file | klass | method |
430
+ +-------------------------------+-----------------+-------------------------------+
431
+ | lib/churn/churn_calculator.rb | ChurnCalculator | ChurnCalculator#filters |
432
+ | lib/churn/churn_calculator.rb | ChurnCalculator | ChurnCalculator#display_array |
433
+ | lib/churn/churn_calculator.rb | ChurnCalculator | ChurnCalculator#to_s |
434
+ +-------------------------------+-----------------+-------------------------------+</tt></pre>
435
+ </div></div>
436
+ <div class="literalblock">
437
+ <div class="content">
438
+ <pre><tt>**********************************************************************
439
+ * Project Churn
440
+ **********************************************************************
441
+ Files:
442
+ +------------------------------------+---------------+
443
+ | file_path | times_changed |
444
+ +------------------------------------+---------------+
445
+ | lib/churn/churn_calculator.rb | 14 |
446
+ | README.rdoc | 7 |
447
+ | lib/tasks/churn_tasks.rb | 6 |
448
+ | Rakefile | 6 |
449
+ | lib/churn/git_analyzer.rb | 4 |
450
+ | VERSION | 4 |
451
+ | test/test_helper.rb | 4 |
452
+ | test/unit/churn_calculator_test.rb | 3 |
453
+ | test/churn_test.rb | 3 |
454
+ +------------------------------------+---------------+</tt></pre>
455
+ </div></div>
456
+ <div class="literalblock">
457
+ <div class="content">
458
+ <pre><tt>Classes:
459
+ +-------------------------------+-----------------+---------------+
460
+ | file | klass | times_changed |
461
+ +-------------------------------+-----------------+---------------+
462
+ | lib/churn/churn_calculator.rb | ChurnCalculator | 1 |
463
+ | lib/churn/churn_calculator.rb | ChurnCalculator | 1 |
464
+ +-------------------------------+-----------------+---------------+</tt></pre>
465
+ </div></div>
466
+ <div class="literalblock">
467
+ <div class="content">
468
+ <pre><tt>Methods:
469
+ +-------------------------------+-----------------+-----------------------------------------+---------------+
470
+ | file | klass | method | times_changed |
471
+ +-------------------------------+-----------------+-----------------------------------------+---------------+
472
+ | lib/churn/churn_calculator.rb | ChurnCalculator | ChurnCalculator#to_s | 1 |
473
+ | lib/churn/churn_calculator.rb | ChurnCalculator | ChurnCalculator#display_array | 1 |
474
+ | lib/churn/churn_calculator.rb | ChurnCalculator | ChurnCalculator#calculate_revision_data | 1 |
475
+ | lib/churn/churn_calculator.rb | ChurnCalculator | ChurnCalculator#filters | 1 |
476
+ | lib/churn/churn_calculator.rb | ChurnCalculator | ChurnCalculator#initialize | 1 |
477
+ | lib/churn/churn_calculator.rb | ChurnCalculator | ChurnCalculator#filters | 1 |
478
+ | lib/churn/churn_calculator.rb | ChurnCalculator | ChurnCalculator#to_s | 1 |
479
+ +-------------------------------+-----------------+-----------------------------------------+---------------+</tt></pre>
480
+ </div></div>
481
+ <div class="paragraph"><p>TODO:
482
+ * SVN only supports file, add full SVN support
483
+ * support bazaar, cvs, and darcs
484
+ * make storage directory configurable instead of using tmp
485
+ * allow passing in directories to churn, directories to ignore
486
+ * add a filter that allows for other files besides. *.rb
487
+ * ignore files pattern, so you can ignore things like vendor/, lib/, or docs/
488
+ * finish adding better documenation using YARD</p></div>
489
+ <div class="paragraph"><p>Executable Usage:
490
+ * <em>gem install churn</em>
491
+ * go to project root run <em>churn</em></p></div>
492
+ <div class="paragraph"><p>Rake Usage:
493
+ * <em>gem install churn</em>
494
+ * on any project you want to use churn, add "require <em>churn</em>" to your rake file
495
+ * run <em>rake churn</em> to view the current output, file churn history is immediate, class and method churn builds up a history as it is run on each revision
496
+ * temporary files with class / method churn history are stored in /tmp, to clear churn history delete them</p></div>
497
+ </div>
498
+ <h2 id="_note_on_patches_pull_requests">Note on Patches/Pull Requests</h2>
499
+ <div class="sectionbody">
500
+ <div class="ulist"><ul>
501
+ <li>
502
+ <p>
503
+ Fork the project.
504
+ </p>
505
+ </li>
506
+ <li>
507
+ <p>
508
+ Make your feature addition or bug fix.
509
+ </p>
510
+ </li>
511
+ <li>
512
+ <p>
513
+ Add tests for it. This is important so I don&#8217;t break it in a
514
+ future version unintentionally.
515
+ </p>
516
+ </li>
517
+ <li>
518
+ <p>
519
+ Commit, do not mess with rakefile, version, or history.
520
+ (if you want to have your own version, that is fine but
521
+ bump version in a commit by itself I can ignore when I pull)
522
+ </p>
523
+ </li>
524
+ <li>
525
+ <p>
526
+ Send me a pull request. Bonus points for topic branches.
527
+ </p>
528
+ </li>
529
+ </ul></div>
530
+ </div>
531
+ <h2 id="_copyright">Copyright</h2>
532
+ <div class="sectionbody">
533
+ <div class="paragraph"><p>Copyright (c) 2010 Dan Mayer. See LICENSE for details.</p></div>
534
+ </div>
535
+ <div id="footer">
536
+ <div id="footer-text">
537
+ Last updated 2010-01-20 10:38:32 EDT
538
+ </div>
539
+ </div>
540
+ </body>
541
+ </html>
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 12
9
- version: 0.0.12
8
+ - 13
9
+ version: 0.0.13
10
10
  platform: ruby
11
11
  authors:
12
12
  - Dan Mayer
@@ -14,11 +14,11 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-02-23 00:00:00 -05:00
17
+ date: 2010-11-30 00:00:00 -05:00
18
18
  default_executable: churn
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
- name: thoughtbot-shoulda
21
+ name: shoulda
22
22
  prerelease: false
23
23
  requirement: &id001 !ruby/object:Gem::Requirement
24
24
  requirements:
@@ -84,7 +84,7 @@ dependencies:
84
84
  prerelease: false
85
85
  requirement: &id006 !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ~>
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  segments:
90
90
  - 0
@@ -159,6 +159,8 @@ files:
159
159
  - lib/churn/source_control.rb
160
160
  - lib/churn/svn_analyzer.rb
161
161
  - lib/tasks/churn_tasks.rb
162
+ - man/churn.1
163
+ - man/churn.html
162
164
  - test/data/churn_calculator.rb
163
165
  - test/data/test_helper.rb
164
166
  - test/test_helper.rb