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 +2 -0
- data/Rakefile +34 -2
- data/VERSION +1 -1
- data/lib/churn/churn_calculator.rb +21 -12
- data/lib/churn/churn_history.rb +5 -1
- data/lib/churn/git_analyzer.rb +3 -49
- data/lib/churn/hg_analyzer.rb +8 -48
- data/lib/churn/location_mapping.rb +3 -0
- data/lib/churn/source_control.rb +57 -0
- data/lib/churn/svn_analyzer.rb +3 -2
- data/man/churn.1 +149 -0
- data/man/churn.html +541 -0
- metadata +7 -5
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 "
|
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", '
|
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.
|
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
|
-
|
67
|
-
|
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(
|
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[:
|
89
|
+
result += display_array(hash[:changed_files], :fields=>[:to_str], :headers=>{:to_str=>'file'})
|
85
90
|
result += "\nClasses: \n"
|
86
|
-
result += display_array(hash[:
|
91
|
+
result += display_array(hash[:changed_classes])
|
87
92
|
result += "\nMethods: \n"
|
88
|
-
result += display_array(hash[:
|
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[:
|
98
|
+
result += display_array(hash[:changes])
|
94
99
|
result += "\nClasses: \n"
|
95
|
-
class_churn = hash[:
|
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[:
|
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! {|
|
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
|
data/lib/churn/churn_history.rb
CHANGED
@@ -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') {|
|
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"
|
data/lib/churn/git_analyzer.rb
CHANGED
@@ -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
|
47
|
-
|
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
|
data/lib/churn/hg_analyzer.rb
CHANGED
@@ -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{|
|
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{|
|
10
|
+
`hg log#{date_range}`.split("\n").reject{|line| line !~ /^changeset:/}.map{|line| line[/:(\S+)$/, 1] }
|
9
11
|
end
|
10
12
|
|
11
|
-
|
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
|
23
|
-
|
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
|
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
|
data/lib/churn/source_control.rb
CHANGED
@@ -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
|
data/lib/churn/svn_analyzer.rb
CHANGED
@@ -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
|
-
|
18
|
-
|
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’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
|
-
-
|
9
|
-
version: 0.0.
|
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-
|
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:
|
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
|