vclog 1.4.0 → 1.5.0

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 (65) hide show
  1. data/HISTORY.rdoc +16 -0
  2. data/PROFILE +5 -5
  3. data/README.rdoc +11 -1
  4. data/REQUIRE +1 -0
  5. data/ROADMAP.rdoc +6 -2
  6. data/VERSION +1 -1
  7. data/features/git.feature +86 -0
  8. data/features/hg.feature +86 -0
  9. data/features/step_definitions/repo_steps.rb +71 -0
  10. data/features/support/aruba.rb +1 -0
  11. data/features/support/repo.rb +15 -0
  12. data/features/svn.feature +86 -0
  13. data/lib/plugins/syckle/vclog.rb +54 -55
  14. data/lib/vclog/{vcs.rb → adapters/abstract.rb} +39 -21
  15. data/lib/vclog/adapters/darcs.rb +83 -0
  16. data/lib/vclog/adapters/git.rb +83 -0
  17. data/lib/vclog/adapters/hg.rb +82 -0
  18. data/lib/vclog/adapters/svn.rb +139 -0
  19. data/lib/vclog/adapters.rb +29 -0
  20. data/lib/vclog/change.rb +38 -13
  21. data/lib/vclog/changelog.rb +23 -3
  22. data/lib/vclog/cli.rb +43 -50
  23. data/lib/vclog/formatter.rb +105 -0
  24. data/lib/vclog/history.rb +33 -17
  25. data/lib/vclog/metadata.rb +13 -0
  26. data/lib/vclog/release.rb +14 -3
  27. data/lib/vclog/tag.rb +17 -9
  28. data/lib/vclog/templates/changelog.atom +26 -0
  29. data/lib/vclog/templates/changelog.gnu +2 -2
  30. data/lib/vclog/templates/changelog.html +3 -3
  31. data/lib/vclog/templates/changelog.json +1 -0
  32. data/lib/vclog/templates/changelog.markdown +2 -2
  33. data/lib/vclog/templates/changelog.rdoc +2 -2
  34. data/lib/vclog/templates/changelog.xml +3 -3
  35. data/lib/vclog/templates/changelog.yaml +1 -0
  36. data/lib/vclog/templates/history.atom +46 -0
  37. data/lib/vclog/templates/history.gnu +12 -0
  38. data/lib/vclog/templates/history.html +5 -5
  39. data/lib/vclog/templates/history.json +1 -0
  40. data/lib/vclog/templates/history.markdown +4 -4
  41. data/lib/vclog/templates/history.rdoc +4 -4
  42. data/lib/vclog/templates/history.xml +5 -4
  43. data/lib/vclog/templates/history.yaml +1 -0
  44. data/lib/vclog.rb +1 -1
  45. metadata +27 -27
  46. data/features/history.feature +0 -16
  47. data/lib/vclog/vcs/darcs.rb +0 -83
  48. data/lib/vclog/vcs/git.rb +0 -83
  49. data/lib/vclog/vcs/hg.rb +0 -0
  50. data/lib/vclog/vcs/svn.rb +0 -116
  51. data/meta/authors +0 -1
  52. data/meta/contact +0 -1
  53. data/meta/created +0 -1
  54. data/meta/description +0 -1
  55. data/meta/homepage +0 -1
  56. data/meta/license +0 -1
  57. data/meta/name +0 -1
  58. data/meta/repository +0 -1
  59. data/meta/requires +0 -1
  60. data/meta/ruby +0 -2
  61. data/meta/sitemap +0 -1
  62. data/meta/suite +0 -1
  63. data/meta/summary +0 -1
  64. data/meta/title +0 -1
  65. data/meta/version +0 -1
@@ -1,6 +1,8 @@
1
1
  module VCLog
2
+ module Adapters
2
3
 
3
4
  require 'time'
5
+ require 'vclog/formatter'
4
6
  require 'vclog/changelog'
5
7
  require 'vclog/history'
6
8
  require 'vclog/change'
@@ -10,22 +12,7 @@ module VCLog
10
12
  # LOG: entries in source files?
11
13
 
12
14
  # = Version Control System
13
- class VCS
14
-
15
- def self.factory(root=nil)
16
- root = root || Dir.pwd
17
- type = read_type(root)
18
- raise ArgumentError, "Not a recognized version control system." unless type
19
- VCS.const_get(type.upcase).new(root)
20
- end
21
-
22
- def self.read_type(root)
23
- dir = nil
24
- Dir.chdir(root) do
25
- dir = Dir.glob("{.svn,.git,.hg,_darcs}").first
26
- end
27
- dir[1..-1] if dir
28
- end
15
+ class Abstract
29
16
 
30
17
  attr :root
31
18
 
@@ -62,6 +49,12 @@ module VCLog
62
49
  @history ||= History.new(self, opts)
63
50
  end
64
51
 
52
+ #
53
+ def display(type, format, options={})
54
+ formatter = Formatter.new(self)
55
+ formatter.display(type, format, options)
56
+ end
57
+
65
58
  # Provides a bumped version number.
66
59
  def bump(part=nil)
67
60
  return part unless ['major', 'minor', 'patch', ''].include?(part.to_s)
@@ -97,6 +90,12 @@ module VCLog
97
90
 
98
91
  private
99
92
 
93
+ #
94
+ def version_tag?(tag_name)
95
+ /(v|\d)/i =~ tag_name
96
+ end
97
+
98
+ =begin
100
99
  # Looks for a "[type]" indicator at the end of the commit message.
101
100
  # If that is not found, it looks at front of message for
102
101
  # "[type]" or "[type]:". Failing that it tries just "type:".
@@ -118,6 +117,7 @@ module VCLog
118
117
  n.gsub!(/^\s*?\n/m,'') # remove blank lines
119
118
  return n, t
120
119
  end
120
+ =end
121
121
 
122
122
  =begin
123
123
  # Write the ChangeLog to file.
@@ -157,12 +157,30 @@ module VCLog
157
157
  end
158
158
  =end
159
159
 
160
- end
160
+ public
161
161
 
162
- require 'vclog/vcs/svn'
163
- require 'vclog/vcs/git'
164
- #require 'vclog/vcs/hg'
165
- #require 'vclog/vcs/darcs'
162
+ #
163
+ def user
164
+ ENV['USER']
165
+ end
166
+
167
+ #
168
+ def email
169
+ ENV['EMAIL']
170
+ end
171
+
172
+ #
173
+ def repository
174
+ nil
175
+ end
176
+
177
+ #
178
+ def uuid
179
+ nil
180
+ end
166
181
 
182
+ end
183
+
184
+ end
167
185
  end
168
186
 
@@ -0,0 +1,83 @@
1
+ require 'vclog/adapters/abstract'
2
+
3
+ module VCLog
4
+ module Adapters
5
+
6
+ # = Darcs
7
+ #
8
+ # Provide Darcs SCM revision tools.
9
+ #
10
+ # TODO: This needs to be fixed.
11
+ #
12
+ class Darcs < Abstract
13
+
14
+ ### Is a darcs repository?
15
+ def repository?
16
+ File.directory?('_darcs')
17
+ end
18
+
19
+ ### This is also a module function.
20
+ module_function :repository?
21
+
22
+ ### Cached Changelog.
23
+ def changelog
24
+ @changelog ||= generate_changelog
25
+ end
26
+
27
+ ### Generate Changelog object.
28
+ def generate_changelog
29
+ raise "not a darcs repository" unless repository?
30
+
31
+ log = Changelog.new
32
+
33
+ txt = `darcs changes` #--repo=#{@repository}`
34
+
35
+ txt.each_line do |line|
36
+ case line
37
+ when /^\s*$/
38
+ when /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun)/
39
+ when /^\s*tagged/
40
+ log << $'
41
+ log << "\n"
42
+ else
43
+ log << line
44
+ log << "\n"
45
+ end
46
+ end
47
+
48
+ return log
49
+ end
50
+
51
+ ### Retrieve the "revision number" from the darcs tree.
52
+ def calculate_version
53
+ raise "not a darcs repository" unless repository?
54
+
55
+ status = info.status
56
+
57
+ changes = `darcs changes`
58
+ count = 0
59
+ tag = "0.0"
60
+
61
+ changes.each("\n\n") do |change|
62
+ head, title, desc = change.split("\n", 3)
63
+ if title =~ /^ \*/
64
+ # Normal change.
65
+ count += 1
66
+ elsif title =~ /tagged (.*)/
67
+ # Tag. We look for these.
68
+ tag = $1
69
+ break
70
+ else
71
+ warn "Unparsable change: #{change}"
72
+ end
73
+ end
74
+ ver = "#{tag}.#{count.to_s}"
75
+
76
+ return ver
77
+ #format_version_stamp(ver, status) # ,released)
78
+ end
79
+
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,83 @@
1
+ require 'vclog/adapters/abstract'
2
+
3
+ module VCLog
4
+ module Adapters
5
+
6
+ # = GIT Adapter
7
+ #
8
+ class Git < Abstract
9
+
10
+ # Collect changes.
11
+ #
12
+ def extract_changes
13
+ list = []
14
+ changelog = `git log --pretty=format:"---%ci|~|%aN|~|%H|~|%s"`.strip
15
+ changes = changelog.split("---")
16
+ #changes = changelog.split(/^commit/m)
17
+ changes.shift # throw the first (empty) entry away
18
+ changes.each do |entry|
19
+ date, who, rev, msg = entry.split('|~|')
20
+ date = Time.parse(date)
21
+ list << [rev, date, who, msg]
22
+ end
23
+ list
24
+ end
25
+
26
+ # Collect tags.
27
+ #
28
+ # `git show 1.0` produces:
29
+ #
30
+ # tag 1.0
31
+ # Tagger: 7rans <transfire@gmail.com>
32
+ # Date: Sun Oct 25 09:27:58 2009 -0400
33
+ #
34
+ # version 1.0
35
+ # commit
36
+ # ...
37
+ #
38
+ def extract_tags
39
+ list = []
40
+ tags = `git tag -l`
41
+ tags.split(/\s+/).each do |tag|
42
+ next unless version_tag?(tag) # only version tags
43
+ info = `git show #{tag}`
44
+ md = /\Atag(.*?)\n(.*?)^commit/m.match(info)
45
+ who, date, *msg = *md[2].split(/\n/)
46
+ who = who.split(':')[1].strip
47
+ date = date[date.index(':')+1..-1].strip
48
+ msg = msg.join("\n")
49
+
50
+ info = `git show #{tag}^ --pretty=format:"%ci|~|%H|~|"`
51
+ date, rev, *_ = *info.split('|~|')
52
+
53
+ #md = /\Atag(.*?)\n(.*?)^commit/m.match(info)
54
+ #_who, _date, *_msg = *md[2].split(/\n/)
55
+ #_who = _who.split(':')[1].strip
56
+ #_date = _date[_date.index(':')+1..-1].strip
57
+ #_msg = _msg.join("\n")
58
+
59
+ list << [tag, rev, date, who, msg]
60
+ end
61
+ list
62
+ end
63
+
64
+ #
65
+ def user
66
+ @email ||= `git config user.name`.strip
67
+ end
68
+
69
+ #
70
+ def email
71
+ @email ||= `git config user.email`.strip
72
+ end
73
+
74
+ #
75
+ def repository
76
+ @repository ||= `git config remote.origin.url`.strip
77
+ end
78
+
79
+ end#class Git
80
+
81
+ end
82
+ end
83
+
@@ -0,0 +1,82 @@
1
+ require 'vclog/adapters/abstract'
2
+
3
+ module VCLog
4
+ module Adapters
5
+
6
+ # = Mercurial Adapter
7
+ #
8
+ class Hg < Abstract
9
+
10
+ # Collect changes.
11
+ #
12
+ def extract_changes
13
+ list = []
14
+ changelog = `hg log -v`.strip
15
+ changes = changelog.split("\n\n\n")
16
+ changes.each do |entry|
17
+ list << parse_entry(entry)
18
+ end
19
+ list
20
+ end
21
+
22
+ # Collect tags.
23
+ #
24
+ def extract_tags
25
+ list = []
26
+ if File.exist?('.hgtags')
27
+ File.readlines('.hgtags').each do |line|
28
+ rev, tag = line.strip.split(' ')
29
+ entry = `hg log -v -r #{rev}`.strip
30
+ rev, date, who, msg, type = parse_entry(entry)
31
+ list << [tag, rev, date, who, msg]
32
+ end
33
+ end
34
+ list
35
+ end
36
+
37
+ # TODO: check .hgrc
38
+ def user
39
+ ENV['HGUSER'] || ENV['USER']
40
+ end
41
+
42
+ #
43
+ def email
44
+ ENV['HGEMAIL'] || ENV['EMAIL']
45
+ end
46
+
47
+ #
48
+ def repository
49
+ @repository ||= `hg showconfig paths.default`.strip
50
+ end
51
+
52
+ #
53
+ def uuid
54
+ nil
55
+ end
56
+
57
+ private
58
+
59
+ def parse_entry(entry)
60
+ rev, date, who, msg = nil, nil, nil, nil
61
+ entry.strip!
62
+ if md = /^changeset:(.*?)$/.match(entry)
63
+ rev = md[1].strip
64
+ end
65
+ if md = /^date:(.*?)$/.match(entry)
66
+ date = md[1].strip
67
+ end
68
+ if md = /^user:(.*?)$/.match(entry)
69
+ who = md[1].strip
70
+ end
71
+ if md = /^description:(.*?)\Z/m.match(entry)
72
+ msg = md[1].strip
73
+ end
74
+ date = Time.parse(date)
75
+ #msg, type = *split_type(msg)
76
+ return rev, date, who, msg
77
+ end
78
+
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,139 @@
1
+ require 'vclog/adapters/abstract'
2
+
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
+ "VCLog requires xmlsimple for SVN support"
20
+ end
21
+ super(root)
22
+ end
23
+
24
+ #
25
+ def extract_changes
26
+ log = []
27
+
28
+ xml = `svn log --xml`.strip
29
+
30
+ commits = XmlSimple.xml_in(xml, {'KeyAttr' => 'rev'})
31
+ commits = commits['logentry']
32
+
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
38
+
39
+ next if msg.empty?
40
+ next if msg == "*** empty log message ***"
41
+
42
+ date = Time.parse(date)
43
+
44
+ log << [rev, date, who, msg]
45
+ end
46
+
47
+ log
48
+ end
49
+
50
+ #
51
+ def extract_tags
52
+ list = []
53
+ tagdir = tag_directory
54
+
55
+ if tagdir
56
+ tags = Dir.entries(tagdir).select{ |e| e.index('.') != 0 && e =~ /\d(.*)$/ }
57
+ else
58
+ tags = []
59
+ end
60
+
61
+ tags.each do |path|
62
+ dir = File.join(tagdir, path)
63
+
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']
72
+
73
+ # get last commit
74
+ xml = `svn log -l1 --xml #{dir}`
75
+ commits = XmlSimple.xml_in(xml, {'KeyAttr' => 'rev'})
76
+ commit = commits['logentry'].first
77
+
78
+ msg = [commit["msg"]].flatten.compact.join('').strip
79
+ date = [commit["date"]].flatten.compact.join('').strip
80
+
81
+ list << [name, rev, date, who, msg]
82
+ end
83
+ list
84
+ end
85
+
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'))
97
+ else
98
+ entries = entries.reject{ |e| e.index('.') == 0 }
99
+ entries = entries.reject{ |e| e !~ /\d$/ }
100
+ break(fnd = dir) unless entries.empty?
101
+ end
102
+ else
103
+ break(fnd=nil)
104
+ end
105
+ dir = File.dirname(dir)
106
+ end
107
+ fnd
108
+ end
109
+
110
+ #
111
+ def user
112
+ @email ||= `svn propget svn:author`.strip
113
+ end
114
+
115
+ # TODO: Best solution to SVN email?
116
+ def email
117
+ @email ||= ENV['EMAIL']
118
+ end
119
+
120
+ #
121
+ def repository
122
+ info['Repository Root']
123
+ end
124
+
125
+ #
126
+ def uuid
127
+ info['Repository UUID']
128
+ end
129
+
130
+ #
131
+ def info
132
+ @info ||= YAML.load(`svn info`.strip)
133
+ end
134
+
135
+ end
136
+
137
+ end
138
+ end
139
+
@@ -0,0 +1,29 @@
1
+ require 'vclog/adapters/svn'
2
+ require 'vclog/adapters/git'
3
+ require 'vclog/adapters/hg'
4
+ #require 'vclog/vcs/darcs'
5
+
6
+ module VCLog
7
+
8
+ module Adapters
9
+
10
+ #
11
+ def self.factory(root=nil)
12
+ root = root || Dir.pwd
13
+ type = read_type(root)
14
+ raise ArgumentError, "Not a recognized version control system." unless type
15
+ const_get(type.capitalize).new(root)
16
+ end
17
+
18
+ #
19
+ def self.read_type(root)
20
+ dir = nil
21
+ Dir.chdir(root) do
22
+ dir = Dir.glob("{.svn,.git,.hg,_darcs}").first
23
+ end
24
+ dir[1..-1] if dir
25
+ end
26
+
27
+ end
28
+
29
+ end
data/lib/vclog/change.rb CHANGED
@@ -13,12 +13,12 @@ module VCLog
13
13
  attr_accessor :type
14
14
 
15
15
  #
16
- def initialize(rev, date, author, message, type=nil)
17
- self.revision = rev #opts[:revison] || opts[:rev]
18
- self.date = date #opts[:date] || opts[:when]
19
- self.author = author #opts[:author] || opts[:who]
20
- self.type = type #opts[:type]
21
- self.message = message #opts[:message] || opts[:msg]
16
+ def initialize(rev, date, author, message)
17
+ self.revision = rev
18
+ self.date = date
19
+ self.author = author
20
+ self.type = type
21
+ self.message = message
22
22
  end
23
23
 
24
24
  #
@@ -26,8 +26,9 @@ module VCLog
26
26
  @author = author.strip
27
27
  end
28
28
 
29
+ #
29
30
  def message=(note)
30
- @message = note.strip
31
+ @message, @type = split_type(note)
31
32
  end
32
33
 
33
34
  #def clean_type(type)
@@ -89,18 +90,42 @@ module VCLog
89
90
  def to_h
90
91
  { 'author' => @author,
91
92
  'date' => @date,
92
- 'revision' => @revison,
93
+ 'revision' => @revision,
93
94
  'message' => @message,
94
95
  'type' => @type
95
96
  }
96
97
  end
97
98
 
98
- def to_json
99
- to_h.to_json
100
- end
99
+ #def to_json
100
+ # to_h.to_json
101
+ #end
102
+
103
+ #def to_yaml(*args)
104
+ # to_h.to_yaml(*args)
105
+ #end
101
106
 
102
- def to_yaml(*args)
103
- to_h.to_yaml(*args)
107
+ private
108
+
109
+ # Looks for a "[type]" indicator at the end of the commit message.
110
+ # If that is not found, it looks at front of message for
111
+ # "[type]" or "[type]:". Failing that it tries just "type:".
112
+ #
113
+ def split_type(note)
114
+ note = note.to_s.strip
115
+ if md = /\[(.*?)\]\Z/.match(note)
116
+ t = md[1].strip.downcase
117
+ n = note[0...(md.begin(0))]
118
+ elsif md = /\A\[(.*?)\]\:?/.match(note)
119
+ t = md[1].strip.downcase
120
+ n = note[md.end(0)..-1]
121
+ elsif md = /\A(\w+?)\:/.match(note)
122
+ t = md[1].strip.downcase
123
+ n = note[md.end(0)..-1]
124
+ else
125
+ n, t = note, nil
126
+ end
127
+ n.gsub!(/^\s*?\n/m,'') # remove blank lines
128
+ return n, t
104
129
  end
105
130
 
106
131
  end #class Entry
@@ -55,9 +55,20 @@ module VCLog
55
55
  @changes << Change.new(rev, date, who, note, type)
56
56
  end
57
57
 
58
- def each(&block) ; changes.each(&block) ; end
59
- def empty? ; changes.empty? ; end
60
- def size ; changes.size ; end
58
+ #
59
+ def each(&block)
60
+ changes.each(&block)
61
+ end
62
+
63
+ #
64
+ def empty?
65
+ changes.empty?
66
+ end
67
+
68
+ #
69
+ def size
70
+ changes.size
71
+ end
61
72
 
62
73
  #
63
74
  def <<(entry)
@@ -126,6 +137,11 @@ module VCLog
126
137
  # mapped
127
138
  #end
128
139
 
140
+ def to_h
141
+ map{ |change| change.to_h }
142
+ end
143
+
144
+
129
145
  # O U T P U T F O R M A T S
130
146
 
131
147
  #
@@ -152,6 +168,7 @@ module VCLog
152
168
  # return x.join("\n")
153
169
  # end
154
170
 
171
+ =begin
155
172
  #
156
173
 
157
174
  def to_gnu(rev=false)
@@ -173,7 +190,9 @@ module VCLog
173
190
  require 'json'
174
191
  changes.to_json
175
192
  end
193
+ =end
176
194
 
195
+ =begin
177
196
  #
178
197
  alias_method :to_s, :to_gnu
179
198
 
@@ -209,6 +228,7 @@ module VCLog
209
228
  erb = ERB.new(tmp)
210
229
  erb.result(binding)
211
230
  end
231
+ =end
212
232
 
213
233
  =begin
214
234
  # Create an XML formated changelog.