vclog 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
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.