vclog 1.8.2 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/.ruby +4 -3
  2. data/.yardopts +7 -0
  3. data/HISTORY.rdoc +207 -0
  4. data/README.rdoc +44 -27
  5. data/bin/vclog +4 -2
  6. data/bin/vclog-autotag +6 -0
  7. data/bin/vclog-bump +6 -0
  8. data/bin/vclog-formats +6 -0
  9. data/bin/vclog-version +6 -0
  10. data/lib/vclog.rb +1 -1
  11. data/lib/vclog.yml +58 -0
  12. data/lib/vclog/adapters.rb +2 -1
  13. data/lib/vclog/adapters/abstract.rb +87 -232
  14. data/lib/vclog/adapters/darcs.rb +72 -67
  15. data/lib/vclog/adapters/git.rb +166 -140
  16. data/lib/vclog/adapters/hg.rb +98 -62
  17. data/lib/vclog/adapters/svn.rb +116 -113
  18. data/lib/vclog/change.rb +110 -81
  19. data/lib/vclog/change_point.rb +77 -0
  20. data/lib/vclog/changelog.rb +58 -296
  21. data/lib/vclog/cli.rb +6 -70
  22. data/lib/vclog/cli/abstract.rb +64 -81
  23. data/lib/vclog/cli/autotag.rb +1 -3
  24. data/lib/vclog/cli/bump.rb +3 -4
  25. data/lib/vclog/cli/formats.rb +4 -4
  26. data/lib/vclog/cli/log.rb +86 -0
  27. data/lib/vclog/cli/version.rb +3 -3
  28. data/lib/vclog/{facets.rb → core_ext.rb} +0 -0
  29. data/lib/vclog/heuristics.rb +112 -38
  30. data/lib/vclog/heuristics/rule.rb +52 -12
  31. data/lib/vclog/heuristics/{label.rb → type.rb} +2 -2
  32. data/lib/vclog/history_file.rb +2 -2
  33. data/lib/vclog/metadata.rb +13 -1
  34. data/lib/vclog/release.rb +26 -12
  35. data/lib/vclog/repo.rb +191 -27
  36. data/lib/vclog/report.rb +187 -0
  37. data/lib/vclog/tag.rb +66 -39
  38. data/lib/vclog/templates/changelog.ansi.rb +9 -26
  39. data/lib/vclog/templates/changelog.atom.erb +3 -3
  40. data/lib/vclog/templates/changelog.gnu.rb +4 -11
  41. data/lib/vclog/templates/changelog.html.erb +11 -2
  42. data/lib/vclog/templates/changelog.markdown.rb +4 -4
  43. data/lib/vclog/templates/changelog.rdoc.rb +4 -4
  44. data/lib/vclog/templates/changelog.rss.erb +2 -6
  45. data/lib/vclog/templates/changelog.xml.erb +14 -2
  46. data/lib/vclog/templates/history.ansi.rb +10 -17
  47. data/lib/vclog/templates/history.atom.erb +4 -4
  48. data/lib/vclog/templates/history.gnu.rb +5 -7
  49. data/lib/vclog/templates/history.html.erb +11 -4
  50. data/lib/vclog/templates/history.json.rb +1 -1
  51. data/lib/vclog/templates/history.markdown.rb +5 -7
  52. data/lib/vclog/templates/history.rdoc.rb +5 -9
  53. data/lib/vclog/templates/history.rss.erb +3 -5
  54. data/lib/vclog/templates/history.xml.erb +15 -3
  55. data/lib/vclog/templates/history.yaml.rb +1 -1
  56. data/man/man1/vclog-autotag.1 +1 -1
  57. data/man/man1/vclog-autotag.1.html +1 -1
  58. data/man/man1/vclog-bump.1 +1 -1
  59. data/man/man1/vclog-bump.1.html +1 -1
  60. data/man/man1/vclog-version.1 +1 -1
  61. data/man/man1/vclog-version.1.html +1 -1
  62. data/man/man1/vclog.1 +25 -13
  63. data/man/man1/vclog.1.html +29 -20
  64. data/man/man1/vclog.1.ronn +31 -18
  65. data/test/unit/case_metadata.rb +1 -1
  66. metadata +48 -34
  67. data/lib/vclog/cli/changelog.rb +0 -33
  68. data/lib/vclog/cli/help.rb +0 -42
  69. data/lib/vclog/cli/history.rb +0 -39
  70. data/lib/vclog/formatter.rb +0 -123
  71. data/lib/vclog/history.rb +0 -131
  72. data/lib/vclog/kernel.rb +0 -12
  73. data/man/man1/vclog-changelog.1 +0 -47
  74. data/man/man1/vclog-changelog.1.html +0 -123
  75. data/man/man1/vclog-changelog.1.ronn +0 -39
  76. data/man/man1/vclog-history.1 +0 -44
  77. data/man/man1/vclog-history.1.html +0 -122
  78. data/man/man1/vclog-history.1.ronn +0 -38
@@ -1,152 +1,155 @@
1
1
  require 'vclog/adapters/abstract'
2
2
 
3
3
  module VCLog
4
- module Adapters
5
-
6
- # = SVN
7
- #
8
- # NOTE: Unfortunately the SVN adapater is very slow. If hits the server
9
- # every time the 'svn log' command is issued. When generating a History
10
- # that's one hit for every tag. If anyone knows a better way please have
11
- # at it --maybe future versions of SVN will improve the situation.
12
- #
13
- class Svn < Abstract
14
-
15
- def initialize(root)
16
- begin
17
- require 'xmlsimple'
18
- rescue LoadError
19
- raise LoadError, "VCLog requires xml-simple for SVN support."
20
- end
21
- super(root)
22
- end
23
4
 
24
- # Extract changes.
25
- def extract_changes
26
- log = []
5
+ module Adapters
6
+
7
+ # SVN Adapter.
8
+ #
9
+ # NOTE: Unfortunately the SVN adapater is very slow. If hits the server
10
+ # every time the 'svn log' command is issued. When generating a History
11
+ # that's two hits for every tag. If anyone knows a better way please have
12
+ # at it --maybe future versions of SVN will improve the situation.
13
+ #
14
+ class Svn < Abstract
27
15
 
28
- xml = `svn log --xml`.strip
16
+ def initialize(root)
17
+ begin
18
+ require 'xmlsimple'
19
+ rescue LoadError
20
+ raise LoadError, "VCLog requires xml-simple for SVN support."
21
+ end
22
+ super(root)
23
+ end
29
24
 
30
- commits = XmlSimple.xml_in(xml, {'KeyAttr' => 'rev'})
31
- commits = commits['logentry']
25
+ # Extract changes.
26
+ def extract_changes
27
+ log = []
32
28
 
33
- commits.each do |com|
34
- rev = com["revision"]
35
- msg = [com["msg"]].flatten.compact.join('').strip
36
- who = [com["author"]].flatten.compact.join('').strip
37
- date = [com["date"]].flatten.compact.join('').strip
29
+ xml = `svn log -v --xml`.strip
38
30
 
39
- next if msg.empty?
40
- next if msg == "*** empty log message ***"
31
+ commits = XmlSimple.xml_in(xml, {'KeyAttr' => 'rev'})
32
+ commits = commits['logentry']
41
33
 
42
- date = Time.parse(date)
34
+ commits.each do |com|
35
+ rev = com['revision']
36
+ msg = [com['msg']].flatten.compact.join('').strip
37
+ who = [com['author']].flatten.compact.join('').strip
38
+ date = [com['date']].flatten.compact.join('').strip
43
39
 
44
- log << Change.new(:id=>rev, :date=>date, :who=>who, :msg=>msg)
45
- end
40
+ files = com['paths'].map{ |h| h['path'].map{ |y| y['content'] } }.flatten
46
41
 
47
- log
48
- end
42
+ next if msg.empty?
43
+ next if msg == "*** empty log message ***"
49
44
 
50
- # Extract tags.
51
- def extract_tags
52
- list = []
53
- tagdir = tag_directory
45
+ date = Time.parse(date)
54
46
 
55
- if tagdir
56
- tags = Dir.entries(tagdir).select{ |e| e.index('.') != 0 && e =~ /\d(.*)$/ }
57
- else
58
- tags = []
47
+ log << Change.new(:id=>rev, :date=>date, :who=>who, :msg=>msg, :files=>files)
48
+ end
49
+
50
+ log
59
51
  end
60
52
 
61
- tags.each do |path|
62
- dir = File.join(tagdir, path)
53
+ # Extract tags.
54
+ def extract_tags
55
+ list = []
56
+ tagdir = tag_directory
63
57
 
64
- # using yaml, but maybe use xml instead?
65
- info = `svn info #{dir}`
66
- info = YAML.load(info)
67
- md = /(\d.*)$/.match(info['Path'])
68
- name = md ? md[1] : path
69
- date = info['Last Changed Date']
70
- who = info['Last Changed Author']
71
- rev = info['Revision']
58
+ if tagdir
59
+ tags = Dir.entries(tagdir).select{ |e| e.index('.') != 0 && e =~ /\d(.*)$/ }
60
+ else
61
+ tags = []
62
+ end
72
63
 
73
- # get last commit
74
- xml = `svn log -l1 --xml #{dir}`
75
- commits = XmlSimple.xml_in(xml, {'KeyAttr' => 'rev'})
76
- commit = commits['logentry'].first
64
+ tags.each do |path|
65
+ dir = File.join(tagdir, path)
77
66
 
78
- msg = [commit["msg"]].flatten.compact.join('').strip
79
- date = [commit["date"]].flatten.compact.join('').strip
67
+ # using yaml, but maybe use xml instead?
68
+ info = `svn info #{dir}`
69
+ info = YAML.load(info)
70
+ md = /(\d.*)$/.match(info['Path'])
71
+ name = md ? md[1] : path
72
+ date = info['Last Changed Date']
73
+ who = info['Last Changed Author']
74
+ rev = info['Revision']
80
75
 
81
- list << Tag.new(:name=>name, :id=>rev, :date=>date, :who=>who, :msg=>msg)
76
+ # get last commit
77
+ xml = `svn log -l1 --xml #{dir}`
78
+ commits = XmlSimple.xml_in(xml, {'KeyAttr' => 'rev'})
79
+ commit = commits['logentry'].first
80
+
81
+ msg = [commit["msg"]].flatten.compact.join('').strip
82
+ date = [commit["date"]].flatten.compact.join('').strip
83
+
84
+ list << Tag.new(:name=>name, :id=>rev, :date=>date, :who=>who, :msg=>msg)
85
+ end
86
+ list
82
87
  end
83
- list
84
- end
85
88
 
86
- # This isn't perfect, but is there really anyway for it to be?
87
- # It ascends up the current directory tree looking for the
88
- # best candidate for a tags directory.
89
- def tag_directory
90
- fnd = nil
91
- dir = root
92
- while dir != '/' do
93
- entries = Dir.entries(dir)
94
- if entries.include?('.svn')
95
- if entries.include?('tags')
96
- break(fnd = File.join(dir, 'tags'))
89
+ # This isn't perfect, but is there really anyway for it to be?
90
+ # It ascends up the current directory tree looking for the
91
+ # best candidate for a tags directory.
92
+ def tag_directory
93
+ fnd = nil
94
+ dir = root
95
+ while dir != '/' do
96
+ entries = Dir.entries(dir)
97
+ if entries.include?('.svn')
98
+ if entries.include?('tags')
99
+ break(fnd = File.join(dir, 'tags'))
100
+ else
101
+ entries = entries.reject{ |e| e.index('.') == 0 }
102
+ entries = entries.reject{ |e| e !~ /\d$/ }
103
+ break(fnd = dir) unless entries.empty?
104
+ end
97
105
  else
98
- entries = entries.reject{ |e| e.index('.') == 0 }
99
- entries = entries.reject{ |e| e !~ /\d$/ }
100
- break(fnd = dir) unless entries.empty?
106
+ break(fnd=nil)
101
107
  end
102
- else
103
- break(fnd=nil)
108
+ dir = File.dirname(dir)
104
109
  end
105
- dir = File.dirname(dir)
110
+ fnd
106
111
  end
107
- fnd
108
- end
109
112
 
110
- #
111
- def user
112
- @email ||= `svn propget svn:author`.strip
113
- end
113
+ #
114
+ def user
115
+ @email ||= `svn propget svn:author`.strip
116
+ end
114
117
 
115
- # TODO: Best solution to SVN email?
116
- def email
117
- @email ||= ENV['EMAIL']
118
- end
118
+ # TODO: Best solution to SVN email?
119
+ def email
120
+ @email ||= ENV['EMAIL']
121
+ end
119
122
 
120
- #
121
- def repository
122
- info['Repository Root']
123
- end
123
+ #
124
+ def repository
125
+ info['Repository Root']
126
+ end
124
127
 
125
- #
126
- def uuid
127
- info['Repository UUID']
128
- end
128
+ #
129
+ def uuid
130
+ info['Repository UUID']
131
+ end
129
132
 
130
- # TODO: Need to effect svn tag date. How?
131
- def tag(ref, label, date, message)
132
- file = tempfile("message", message)
133
+ # TODO: Need to effect svn tag date. How?
134
+ def tag(ref, label, date, message)
135
+ file = tempfile("message", message)
133
136
 
134
- Dir.chdir(root) do
135
- cmd = %[svn copy -r #{ref} -F "#{mfile.path}" . #{tag_directory}/#{label}]
136
- puts cmd if $DEBUG
137
- `#{cmd}` unless $DRYRUN
137
+ Dir.chdir(root) do
138
+ cmd = %[svn copy -r #{ref} -F "#{mfile.path}" . #{tag_directory}/#{label}]
139
+ puts cmd if $DEBUG
140
+ `#{cmd}` unless $DRYRUN
141
+ end
138
142
  end
139
- end
140
143
 
141
- private
144
+ private
145
+
146
+ #
147
+ def info
148
+ @info ||= YAML.load(`svn info`.strip)
149
+ end
142
150
 
143
- #
144
- def info
145
- @info ||= YAML.load(`svn info`.strip)
146
151
  end
147
152
 
148
153
  end
149
154
 
150
155
  end
151
- end
152
-
@@ -1,4 +1,4 @@
1
- require 'vclog/kernel'
1
+ require 'vclog/change_point'
2
2
 
3
3
  module VCLog
4
4
 
@@ -8,7 +8,7 @@ module VCLog
8
8
 
9
9
  include Comparable
10
10
 
11
- # Commit reference id.
11
+ # Commit revision/reference id.
12
12
  attr_accessor :id
13
13
 
14
14
  # Date/time of commit.
@@ -23,45 +23,92 @@ module VCLog
23
23
  # List of files changed in the commit.
24
24
  attr_accessor :files
25
25
 
26
+ # Type of change, as assigned by hueristics.
26
27
  attr_accessor :type
28
+
29
+ # The priority level of this change, as assigned by hueristics.
30
+ # This can be `nil`, as Heuristics will always make sure a
31
+ # commit has an inteer level before going out to template.
27
32
  attr_accessor :level
33
+
34
+ # The descriptive label of this change, as assigned by hueristics.
28
35
  attr_accessor :label
29
36
 
30
- #
37
+ # ANSI color to apply. Actually this can be a list
38
+ # of any support ansi gem terms, but usually it's
39
+ # just the color term, such as `:red`.
40
+ attr_accessor :color
41
+
42
+ # Setup new Change instance.
31
43
  def initialize(data={})
44
+ @type = :default
45
+ @level = nil
46
+ @label = nil
47
+ @color = []
48
+
32
49
  data.each do |k,v|
33
- __send__("#{k}=", v)
50
+ __send__("#{k}=", v) if respond_to?("#{k}=")
34
51
  end
35
52
  end
36
53
 
54
+ #
55
+ # Set authors attributes, ensuring the value is stripped of white space.
37
56
  #
38
57
  def author=(author)
39
58
  @author = author.strip
40
59
  end
41
60
 
61
+ #
62
+ # Set date attribute, converting vale given to an instance of Time.
42
63
  #
43
64
  def date=(date)
44
65
  @date = parse_date(date)
45
66
  end
46
67
 
47
- # Alternate name for id.
48
- alias_method :ref, :id
49
- alias_method :ref=, :id=
50
- alias_method :reference, :id
51
- alias_method :reference=, :id=
68
+ #
69
+ # Set the commit message.
70
+ #
71
+ def message=(msg)
72
+ @message = msg
73
+
74
+ lines = msg.lines.to_a
75
+ @summary = lines.first.strip
76
+ @details = lines[1..-1].join('').strip
77
+
78
+ msg
79
+ end
80
+
81
+ #
82
+ # Set the ANSI color terms.
83
+ #
84
+ # @param [Symbol,Array<Symbol>] code
85
+ # An ANSI gem recognized term, or array of such.
86
+ #
87
+ def color=(code)
88
+ @color = [code].flatten
89
+ end
52
90
 
53
- # Alternate name for id.
54
91
  #
55
- # @deprecated
92
+ def type=(type)
93
+ @type = type.to_sym
94
+ end
95
+
96
+ # Alternate name for id.
56
97
  alias_method :rev, :id
57
98
  alias_method :rev=, :id=
58
99
 
59
100
  # Alternate name for id.
60
- #
61
- # @deprecated
62
101
  alias_method :revision, :id
63
102
  alias_method :revision=, :id=
64
103
 
104
+ # Alternate name for id.
105
+ alias_method :ref, :id
106
+ alias_method :ref=, :id=
107
+
108
+ # Alternate name for id.
109
+ alias_method :reference, :id
110
+ alias_method :reference=, :id=
111
+
65
112
  # Alternate name for message.
66
113
  alias_method :msg, :message
67
114
  alias_method :msg=, :message=
@@ -70,100 +117,70 @@ module VCLog
70
117
  alias_method :who, :author
71
118
  alias_method :who=, :author=
72
119
 
120
+ attr_reader :summary
73
121
 
74
- #def clean_type(type)
75
- # case type.to_s
76
- # when 'maj', 'major' then :major
77
- # when 'min', 'minor' then :minor
78
- # when 'bug' then :bug
79
- # when '' then :other
80
- # else
81
- # type.to_sym
82
- # end
83
- #end
122
+ attr_reader :details
84
123
 
124
+ #
125
+ # Compare changes by date.
85
126
  #
86
127
  def <=>(other)
87
128
  other.date <=> date
88
129
  end
89
130
 
131
+ #
132
+ # Inspection string of change object.
133
+ #
90
134
  def inspect
91
135
  "#<Change:#{object_id} #{date}>"
92
136
  end
93
137
 
94
- # TODO: Rename revision to `referece` or `ref`.
95
-
138
+ #
139
+ # Convert to Hash.
96
140
  #
97
141
  def to_h
98
- { 'author' => @author,
99
- 'date' => @date,
100
- 'revision' => @revision,
101
- 'message' => @message,
102
- 'type' => @type
142
+ { 'author' => self.author,
143
+ 'date' => self.date,
144
+ 'id' => self.id,
145
+ 'message' => self.message,
146
+ 'type' => self.type
103
147
  }
104
148
  end
105
149
 
106
- #def to_json
107
- # to_h.to_json
108
- #end
109
-
110
- #def to_yaml(*args)
111
- # to_h.to_yaml(*args)
112
- #end
113
-
150
+ #
151
+ # Apply heuristic rules to change.
152
+ #
114
153
  def apply_heuristics(heuristics)
115
- type, level, label, msg = *heuristics.lookup(message)
116
-
117
- self.type = type
118
- self.level = level
119
- self.label = label
120
- self.message = msg || @message
154
+ heuristics.apply(self)
121
155
  end
122
156
 
123
- =begin
124
- #
125
- def type_phrase
126
- case type.to_s
127
- when 'maj', 'major'
128
- 'Major Enhancements'
129
- when 'min', 'minor'
130
- 'Minor Enhancements'
131
- when 'bug'
132
- 'Bug Fixes'
133
- when ''
134
- 'General Enhancements'
135
- when '-'
136
- 'Administrative Changes'
137
- else
138
- "#{type.to_s.capitalize} Enhancements"
139
- end
157
+ #
158
+ # Parse point entries from commit message. Point entries
159
+ # are outlined changes via line that start with an asterisk.
160
+ #
161
+ def points
162
+ @points ||= parse_points
140
163
  end
141
164
 
142
- # Looks for a "[type]" indicator at the end of the commit message.
143
- # If that is not found, it looks at front of message for
144
- # "[type]" or "[type]:". Failing that it tries just "type:".
145
- #
146
- def split_type(note)
147
- note = note.to_s.strip
148
- if md = /\[(.*?)\]\Z/.match(note)
149
- t = md[1].strip.downcase
150
- n = note[0...(md.begin(0))]
151
- elsif md = /\A\[(.*?)\]\:?/.match(note)
152
- t = md[1].strip.downcase
153
- n = note[md.end(0)..-1]
154
- elsif md = /\A(\w+?)\:/.match(note)
155
- t = md[1].strip.downcase
156
- n = note[md.end(0)..-1]
165
+ #
166
+ # Output message with optional adjustments.
167
+ #
168
+ def to_s(opts={})
169
+ if opts[:summary]
170
+ summary
157
171
  else
158
- n, t = note, nil
172
+ message.strip
159
173
  end
160
- n.gsub!(/^\s*?\n/m,'') # remove blank lines
161
- return n, t
162
174
  end
163
- =end
164
175
 
165
- private
176
+ private
166
177
 
178
+ #
179
+ # Convert given +date+ into Time instance.
180
+ #
181
+ # @param [String,Data,Time] date
182
+ # A valid data/time string or object.
183
+ #
167
184
  def parse_date(date)
168
185
  case date
169
186
  when Time
@@ -173,6 +190,18 @@ module VCLog
173
190
  end
174
191
  end
175
192
 
193
+ #
194
+ # Split message into individual points.
195
+ #
196
+ # @todo Improve the parsing of point messages.
197
+ #
198
+ def parse_points
199
+ point_messages = message.split(/^\*/)
200
+ point_messages.map do |msg|
201
+ ChangePoint.new(self, msg)
202
+ end
203
+ end
204
+
176
205
  end
177
206
 
178
207
  end