churn 0.0.12 → 0.0.13

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/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