rubycut-vclog 1.9.4

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.
Files changed (76) hide show
  1. data/.yardopts +7 -0
  2. data/History.md +249 -0
  3. data/License.txt +23 -0
  4. data/README.md +133 -0
  5. data/bin/vclog +6 -0
  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-news +6 -0
  10. data/bin/vclog-version +6 -0
  11. data/lib/vclog.rb +6 -0
  12. data/lib/vclog.yml +68 -0
  13. data/lib/vclog/adapters.rb +12 -0
  14. data/lib/vclog/adapters/abstract.rb +131 -0
  15. data/lib/vclog/adapters/darcs.rb +93 -0
  16. data/lib/vclog/adapters/git.rb +190 -0
  17. data/lib/vclog/adapters/hg.rb +129 -0
  18. data/lib/vclog/adapters/svn.rb +155 -0
  19. data/lib/vclog/change.rb +207 -0
  20. data/lib/vclog/change_point.rb +77 -0
  21. data/lib/vclog/changelog.rb +233 -0
  22. data/lib/vclog/cli.rb +8 -0
  23. data/lib/vclog/cli/abstract.rb +92 -0
  24. data/lib/vclog/cli/autotag.rb +36 -0
  25. data/lib/vclog/cli/bump.rb +29 -0
  26. data/lib/vclog/cli/formats.rb +28 -0
  27. data/lib/vclog/cli/log.rb +86 -0
  28. data/lib/vclog/cli/news.rb +29 -0
  29. data/lib/vclog/cli/version.rb +30 -0
  30. data/lib/vclog/config.rb +143 -0
  31. data/lib/vclog/core_ext.rb +11 -0
  32. data/lib/vclog/heuristics.rb +192 -0
  33. data/lib/vclog/heuristics/rule.rb +73 -0
  34. data/lib/vclog/heuristics/type.rb +29 -0
  35. data/lib/vclog/history_file.rb +69 -0
  36. data/lib/vclog/metadata.rb +16 -0
  37. data/lib/vclog/rc.rb +9 -0
  38. data/lib/vclog/release.rb +67 -0
  39. data/lib/vclog/repo.rb +298 -0
  40. data/lib/vclog/report.rb +200 -0
  41. data/lib/vclog/tag.rb +151 -0
  42. data/lib/vclog/templates/changelog.ansi.rb +35 -0
  43. data/lib/vclog/templates/changelog.atom.erb +52 -0
  44. data/lib/vclog/templates/changelog.gnu.rb +24 -0
  45. data/lib/vclog/templates/changelog.html.erb +49 -0
  46. data/lib/vclog/templates/changelog.json.rb +1 -0
  47. data/lib/vclog/templates/changelog.markdown.rb +30 -0
  48. data/lib/vclog/templates/changelog.rdoc.rb +30 -0
  49. data/lib/vclog/templates/changelog.rss.erb +54 -0
  50. data/lib/vclog/templates/changelog.xml.erb +28 -0
  51. data/lib/vclog/templates/changelog.xsl +34 -0
  52. data/lib/vclog/templates/changelog.yaml.rb +1 -0
  53. data/lib/vclog/templates/history.ansi.rb +57 -0
  54. data/lib/vclog/templates/history.atom.erb +84 -0
  55. data/lib/vclog/templates/history.gnu.rb +39 -0
  56. data/lib/vclog/templates/history.html.erb +60 -0
  57. data/lib/vclog/templates/history.json.rb +1 -0
  58. data/lib/vclog/templates/history.markdown.rb +38 -0
  59. data/lib/vclog/templates/history.rdoc.rb +36 -0
  60. data/lib/vclog/templates/history.rss.erb +84 -0
  61. data/lib/vclog/templates/history.xml.erb +43 -0
  62. data/lib/vclog/templates/history.yaml.rb +1 -0
  63. data/man/man1/index.txt +9 -0
  64. data/man/man1/vclog-autotag.1.ronn +29 -0
  65. data/man/man1/vclog-bump.1.ronn +21 -0
  66. data/man/man1/vclog-news.1.ronn +25 -0
  67. data/man/man1/vclog-version.1.ronn +14 -0
  68. data/man/man1/vclog.1.ronn +49 -0
  69. data/spec/feature_git_changes.rb +58 -0
  70. data/spec/feature_git_history.rb +58 -0
  71. data/spec/feature_hg_changes.rb +58 -0
  72. data/spec/feature_hg_history.rb +58 -0
  73. data/spec/featurettes/repo_creation.rb +64 -0
  74. data/spec/featurettes/shellout.rb +16 -0
  75. data/test/case_metadata.rb +10 -0
  76. metadata +265 -0
@@ -0,0 +1,129 @@
1
+ require 'vclog/adapters/abstract'
2
+
3
+ module VCLog
4
+
5
+ module Adapters
6
+
7
+ # Mercurial Adapter
8
+ #
9
+ class Hg < Abstract
10
+
11
+ #
12
+ # Collect changes.
13
+ #
14
+ def extract_changes
15
+ list = []
16
+ changelog = `hg log -v`.strip
17
+ changes = changelog.split("\n\n\n")
18
+ changes.each do |entry|
19
+ settings = parse_entry(entry)
20
+ list << Change.new(settings)
21
+ end
22
+ list
23
+ end
24
+
25
+ #
26
+ # Collect tags.
27
+ #
28
+ # @todo Extract first commit prior to tag and provide it with Tag object.
29
+ #
30
+ def extract_tags
31
+ list = []
32
+ if File.exist?('.hgtags')
33
+ File.readlines('.hgtags').each do |line|
34
+ rev, tag = line.strip.split(' ')
35
+ entry = `hg log -v -r #{rev}`.strip
36
+ settings = parse_entry(entry)
37
+ settings[:name] = tag
38
+ list << Tag.new(settings)
39
+ end
40
+ end
41
+ list
42
+ end
43
+
44
+ #
45
+ # Username.
46
+ #
47
+ # @todo check .hgrc for user.
48
+ #
49
+ def user
50
+ ENV['HGUSER'] || ENV['USER']
51
+ end
52
+
53
+ #
54
+ # User's email address.
55
+ #
56
+ # @todo check .hgrc for email.
57
+ #
58
+ def email
59
+ ENV['HGEMAIL'] || ENV['EMAIL']
60
+ end
61
+
62
+ #
63
+ # URI of repository.
64
+ #
65
+ def uri
66
+ @uri ||= `hg showconfig paths.default`.strip
67
+ end
68
+
69
+ # @deprecated
70
+ alias_method :repository, :uri
71
+
72
+ #
73
+ #
74
+ #
75
+ def uuid
76
+ nil
77
+ end
78
+
79
+ #
80
+ # TODO: Will multi-line messages work okay this way?
81
+ #
82
+ def tag(ref, label, date, msg)
83
+ file = tempfile("message", msg)
84
+ date = date.strftime('%Y-%m-%d') unless String===date
85
+
86
+ cmd = %[hg tag -r #{ref} -d #{date} -m "$(cat #{file})" #{label}]
87
+
88
+ puts cmd if $DEBUG
89
+ `#{cmd}` unless $DRYRUN
90
+ end
91
+
92
+ private
93
+
94
+ #
95
+ # Parse log entry.
96
+ #
97
+ def parse_entry(entry)
98
+ settings = {}
99
+
100
+ entry.strip!
101
+
102
+ if md = /^changeset:(.*?)$/.match(entry)
103
+ settings[:id] = md[1].strip
104
+ end
105
+
106
+ if md = /^date:(.*?)$/.match(entry)
107
+ settings[:date] = Time.parse(md[1].strip)
108
+ end
109
+
110
+ if md = /^user:(.*?)$/.match(entry)
111
+ settings[:who] = md[1].strip
112
+ end
113
+
114
+ if md = /^files:(.*?)$/.match(entry)
115
+ settings[:files] = md[1].strip.split(' ')
116
+ end
117
+
118
+ if md = /^description:(.*?)\Z/m.match(entry)
119
+ settings[:msg] = md[1].strip
120
+ end
121
+
122
+ return settings
123
+ end
124
+
125
+ end
126
+
127
+ end
128
+
129
+ end
@@ -0,0 +1,155 @@
1
+ require 'vclog/adapters/abstract'
2
+
3
+ module VCLog
4
+
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
15
+
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
24
+
25
+ # Extract changes.
26
+ def extract_changes
27
+ log = []
28
+
29
+ xml = `svn log -v --xml`.strip
30
+
31
+ commits = XmlSimple.xml_in(xml, {'KeyAttr' => 'rev'})
32
+ commits = commits['logentry']
33
+
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
39
+
40
+ files = com['paths'].map{ |h| h['path'].map{ |y| y['content'] } }.flatten
41
+
42
+ next if msg.empty?
43
+ next if msg == "*** empty log message ***"
44
+
45
+ date = Time.parse(date)
46
+
47
+ log << Change.new(:id=>rev, :date=>date, :who=>who, :msg=>msg, :files=>files)
48
+ end
49
+
50
+ log
51
+ end
52
+
53
+ # Extract tags.
54
+ def extract_tags
55
+ list = []
56
+ tagdir = tag_directory
57
+
58
+ if tagdir
59
+ tags = Dir.entries(tagdir).select{ |e| e.index('.') != 0 && e =~ /\d(.*)$/ }
60
+ else
61
+ tags = []
62
+ end
63
+
64
+ tags.each do |path|
65
+ dir = File.join(tagdir, path)
66
+
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']
75
+
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
87
+ end
88
+
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
105
+ else
106
+ break(fnd=nil)
107
+ end
108
+ dir = File.dirname(dir)
109
+ end
110
+ fnd
111
+ end
112
+
113
+ #
114
+ def user
115
+ @email ||= `svn propget svn:author`.strip
116
+ end
117
+
118
+ # TODO: Best solution to SVN email?
119
+ def email
120
+ @email ||= ENV['EMAIL']
121
+ end
122
+
123
+ #
124
+ def repository
125
+ info['Repository Root']
126
+ end
127
+
128
+ #
129
+ def uuid
130
+ info['Repository UUID']
131
+ end
132
+
133
+ # TODO: Need to effect svn tag date. How?
134
+ def tag(ref, label, date, message)
135
+ file = tempfile("message", message)
136
+
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
142
+ end
143
+
144
+ private
145
+
146
+ #
147
+ def info
148
+ @info ||= YAML.load(`svn info`.strip)
149
+ end
150
+
151
+ end
152
+
153
+ end
154
+
155
+ end
@@ -0,0 +1,207 @@
1
+ require 'vclog/change_point'
2
+
3
+ module VCLog
4
+
5
+ # The Change class models an entry in a change log.
6
+ #
7
+ class Change
8
+
9
+ include Comparable
10
+
11
+ # Commit revision/reference id.
12
+ attr_accessor :id
13
+
14
+ # Date/time of commit.
15
+ attr_accessor :date
16
+
17
+ # Committer.
18
+ attr_accessor :author
19
+
20
+ # Commit message.
21
+ attr_accessor :message
22
+
23
+ # List of files changed in the commit.
24
+ attr_accessor :files
25
+
26
+ # Type of change, as assigned by hueristics.
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.
32
+ attr_accessor :level
33
+
34
+ # The descriptive label of this change, as assigned by hueristics.
35
+ attr_accessor :label
36
+
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.
43
+ def initialize(data={})
44
+ @type = :default
45
+ @level = nil
46
+ @label = nil
47
+ @color = []
48
+
49
+ data.each do |k,v|
50
+ __send__("#{k}=", v) if respond_to?("#{k}=")
51
+ end
52
+ end
53
+
54
+ #
55
+ # Set authors attributes, ensuring the value is stripped of white space.
56
+ #
57
+ def author=(author)
58
+ @author = author.strip
59
+ end
60
+
61
+ #
62
+ # Set date attribute, converting vale given to an instance of Time.
63
+ #
64
+ def date=(date)
65
+ @date = parse_date(date)
66
+ end
67
+
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
90
+
91
+ #
92
+ def type=(type)
93
+ @type = type.to_sym
94
+ end
95
+
96
+ # Alternate name for id.
97
+ alias_method :rev, :id
98
+ alias_method :rev=, :id=
99
+
100
+ # Alternate name for id.
101
+ alias_method :revision, :id
102
+ alias_method :revision=, :id=
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
+
112
+ # Alternate name for message.
113
+ alias_method :msg, :message
114
+ alias_method :msg=, :message=
115
+
116
+ # Alias for author.
117
+ alias_method :who, :author
118
+ alias_method :who=, :author=
119
+
120
+ attr_reader :summary
121
+
122
+ attr_reader :details
123
+
124
+ #
125
+ # Compare changes by date.
126
+ #
127
+ def <=>(other)
128
+ other.date <=> date
129
+ end
130
+
131
+ #
132
+ # Inspection string of change object.
133
+ #
134
+ def inspect
135
+ "#<Change:#{object_id} #{date}>"
136
+ end
137
+
138
+ #
139
+ # Convert to Hash.
140
+ #
141
+ def to_h
142
+ { 'author' => self.author,
143
+ 'date' => self.date,
144
+ 'id' => self.id,
145
+ 'message' => self.message,
146
+ 'type' => self.type
147
+ }
148
+ end
149
+
150
+ #
151
+ # Apply heuristic rules to change.
152
+ #
153
+ def apply_heuristics(heuristics)
154
+ heuristics.apply(self)
155
+ end
156
+
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
163
+ end
164
+
165
+ #
166
+ # Output message with optional adjustments.
167
+ #
168
+ def to_s(opts={})
169
+ if opts[:summary]
170
+ summary
171
+ else
172
+ message.strip
173
+ end
174
+ end
175
+
176
+ private
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
+ #
184
+ def parse_date(date)
185
+ case date
186
+ when Time
187
+ date
188
+ else
189
+ Time.parse(date.to_s)
190
+ end
191
+ end
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
+
205
+ end
206
+
207
+ end