rscm 0.1.0.1338 → 0.2.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 (47) hide show
  1. data/README +13 -28
  2. data/Rakefile +25 -24
  3. data/lib/rscm.rb +1 -6
  4. data/lib/rscm/abstract_scm.rb +36 -10
  5. data/lib/rscm/annotations.rb +50 -0
  6. data/lib/rscm/changes.rb +2 -5
  7. data/lib/rscm/logging.rb +1 -0
  8. data/lib/rscm/path_converter.rb +3 -2
  9. data/lib/rscm/{cvs → scm}/cvs.rb +16 -9
  10. data/lib/rscm/{cvs → scm}/cvs_log_parser.rb +4 -4
  11. data/lib/rscm/{darcs → scm}/darcs.rb +6 -3
  12. data/lib/rscm/scm/monotone.rb +162 -0
  13. data/lib/rscm/scm/monotone_log_parser.rb +95 -0
  14. data/lib/rscm/scm/mooky.rb +21 -0
  15. data/lib/rscm/{perforce → scm}/perforce.rb +7 -4
  16. data/lib/rscm/{starteam/starteam.rb → scm/star_team.rb} +23 -3
  17. data/lib/rscm/{svn/svn.rb → scm/subversion.rb} +17 -10
  18. data/lib/rscm/{svn/svn_log_parser.rb → scm/subversion_log_parser.rb} +8 -7
  19. data/test/rscm/abstract_scm_test.rb +21 -0
  20. data/test/rscm/annotations_test.rb +57 -0
  21. data/test/rscm/changes_fixture.rb +7 -7
  22. data/test/rscm/changes_test.rb +3 -3
  23. data/test/rscm/generic_scm_tests.rb +2 -2
  24. data/test/rscm/{cvs → scm}/cvs-dataforge.log +0 -0
  25. data/test/rscm/{cvs → scm}/cvs-test.log +0 -0
  26. data/test/rscm/{cvs → scm}/cvs_log_parser_test.rb +12 -13
  27. data/test/rscm/{cvs → scm}/cvs_test.rb +7 -7
  28. data/test/rscm/{darcs → scm}/darcs_test.rb +1 -1
  29. data/test/rscm/{monotone → scm}/keys +0 -0
  30. data/test/rscm/scm/monotone_log_parser_test.rb +109 -0
  31. data/test/rscm/{monotone → scm}/monotone_test.rb +1 -1
  32. data/test/rscm/{mooky → scm}/mooky_test.rb +1 -1
  33. data/test/rscm/{perforce → scm}/perforce_test.rb +1 -1
  34. data/test/rscm/{starteam/starteam_test.rb → scm/star_team.rb} +1 -1
  35. data/test/rscm/{svn/svn_log_parser_test.rb → scm/subversion_log_parser_test.rb} +25 -10
  36. data/test/rscm/{svn/svn_test.rb → scm/subversion_test.rb} +4 -5
  37. data/test/rscm/{svn/cargo-svn.log → scm/svn-cargo.log} +0 -0
  38. data/test/rscm/scm/svn-growl.log +875 -0
  39. data/test/rscm/scm/svn-growl2.log +30 -0
  40. data/test/rscm/{svn/proxytoys-svn.log → scm/svn-proxytoys.log} +0 -0
  41. metadata +35 -44
  42. data/lib/rscm/attr_attr.rb +0 -36
  43. data/lib/rscm/monotone/monotone.rb +0 -107
  44. data/lib/rscm/mooky/mooky.rb +0 -13
  45. data/test/actual +0 -3
  46. data/test/expected +0 -3
  47. data/test/rscm/attr_attr_test.rb +0 -32
@@ -1,12 +1,15 @@
1
- require 'rscm/abstract_scm'
2
1
  require 'tempfile'
3
- require 'rscm/path_converter'
4
2
  require 'fileutils'
3
+ require 'rscm'
5
4
 
6
5
  module RSCM
7
6
  class Darcs < AbstractSCM
7
+ register self
8
8
 
9
- def initialize(dir=nil)
9
+ ann :description => "Directory"
10
+ attr_accessor :dir
11
+
12
+ def initialize(dir=".")
10
13
  @dir = File.expand_path(dir)
11
14
  end
12
15
 
@@ -0,0 +1,162 @@
1
+ require 'fileutils'
2
+ require 'rscm'
3
+
4
+ module RSCM
5
+ class Monotone < AbstractSCM
6
+ register self
7
+
8
+ ann :description => "Database file"
9
+ attr_accessor :db_file
10
+
11
+ ann :description => "Branch"
12
+ attr_accessor :branch
13
+
14
+ ann :description => "Key"
15
+ attr_accessor :key
16
+
17
+ ann :description => "Passphrase"
18
+ attr_accessor :passphrase
19
+
20
+ ann :description => "Keys file"
21
+ attr_accessor :keys_file
22
+
23
+ def initialize(db_file="MT.db", branch="", key="", passphrase="", keys_file="")
24
+ @db_file = File.expand_path(db_file)
25
+ @branch = branch
26
+ @key = key
27
+ @passphrase = passphrase
28
+ @keys_file = keys_file
29
+ end
30
+
31
+ def name
32
+ "Monotone"
33
+ end
34
+
35
+ def create
36
+ FileUtils.mkdir_p(File.dirname(@db_file))
37
+ monotone("db init")
38
+ monotone("read") do |io|
39
+ io.write(File.open(@keys_file).read)
40
+ io.close_write
41
+ end
42
+ end
43
+
44
+ def transactional?
45
+ true
46
+ end
47
+
48
+ def import(dir, message)
49
+ dir = File.expand_path(dir)
50
+
51
+ # post 0.17, this can be "cd dir && cmd add ."
52
+
53
+ files = Dir["#{dir}/*"]
54
+ relative_paths_to_add = to_relative(dir, files)
55
+
56
+ with_working_dir(dir) do
57
+ monotone("add #{relative_paths_to_add.join(' ')}")
58
+ monotone("commit '#{message}'", @branch, @key) do |io|
59
+ io.puts(@passphrase)
60
+ io.close_write
61
+ io.read
62
+ end
63
+ end
64
+ end
65
+
66
+ def checked_out?(checkout_dir)
67
+ File.exists?("#{checkout_dir}/MT")
68
+ end
69
+
70
+ def uptodate?(checkout_dir, from_identifier)
71
+ if (!checked_out?(checkout_dir))
72
+ false
73
+ else
74
+ lr = local_revision(checkout_dir)
75
+ hr = head_revision(checkout_dir)
76
+ lr == hr
77
+ end
78
+ end
79
+
80
+ def local_revision(checkout_dir)
81
+ local_revision = nil
82
+ rev_file = File.expand_path("#{checkout_dir}/MT/revision")
83
+ local_revision = File.open(rev_file).read.strip
84
+ local_revision
85
+ end
86
+
87
+ def head_revision(checkout_dir)
88
+ # FIXME: this will grab last head if heads are not merged.
89
+ head_revision = nil
90
+ monotone("heads", @branch) do |stdout|
91
+ stdout.each_line do |line|
92
+ next if (line =~ /^monotone:/)
93
+ head_revision = line.split(" ")[0]
94
+ end
95
+ end
96
+ head_revision
97
+ end
98
+
99
+ def changesets(checkout_dir, from_identifier, to_identifier=Time.infinity)
100
+ from_identifier = Time.epoch if from_identifier.nil?
101
+ to_identifier = Time.infinity if to_identifier.nil?
102
+ with_working_dir(checkout_dir) do
103
+ monotone("log", @branch, @key) do |stdout|
104
+ MonotoneLogParser.new.parse_changesets(stdout, from_identifier, to_identifier)
105
+ end
106
+ end
107
+ end
108
+
109
+ def commit(checkout_dir, message)
110
+ with_working_dir(checkout_dir) do
111
+ monotone("commit '#{message}'", @branch, @key) do |io|
112
+ io.puts(@passphrase)
113
+ io.close_write
114
+ io.read
115
+ end
116
+ end
117
+ end
118
+
119
+ protected
120
+
121
+ # Checks out silently. Called by superclass' checkout.
122
+ def checkout_silent(checkout_dir, to_identifier)
123
+ checkout_dir = PathConverter.filepath_to_nativepath(checkout_dir, false)
124
+ if checked_out?(checkout_dir)
125
+ with_working_dir(checkout_dir) do
126
+ monotone("update")
127
+ end
128
+ else
129
+ monotone("checkout #{checkout_dir}", @branch, @key) do |stdout|
130
+ stdout.each_line do |line|
131
+ # TODO: checkout prints nothing to stdout - may be fixed in a future monotone.
132
+ # When/if it happens we may want to do a kosher implementation of checkout
133
+ # to get yields as checkouts happen.
134
+ yield line if block_given?
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ # Administrative files that should be ignored when counting files.
141
+ def ignore_paths
142
+ return [/MT/, /\.mt-attrs/]
143
+ end
144
+
145
+ private
146
+
147
+ def monotone(monotone_cmd, branch=nil, key=nil)
148
+ branch_opt = branch ? "--branch=\"#{branch}\"" : ""
149
+ key_opt = key ? "--key=\"#{key}\"" : ""
150
+ cmd = "monotone --db=\"#{@db_file}\" #{branch_opt} #{key_opt} #{monotone_cmd}"
151
+ safer_popen(cmd, "r+") do |io|
152
+ if(block_given?)
153
+ return(yield(io))
154
+ else
155
+ # just read stdout so we can exit
156
+ io.read
157
+ end
158
+ end
159
+ end
160
+
161
+ end
162
+ end
@@ -0,0 +1,95 @@
1
+ require 'rscm'
2
+ require 'time'
3
+ require 'stringio'
4
+
5
+ module RSCM
6
+
7
+ class MonotoneLogParser
8
+
9
+ def parse_changesets(io, from_identifier=Time.epoch, to_identifier=Time.infinity)
10
+ # skip first separator
11
+ io.readline
12
+
13
+ changesets = ChangeSets.new
14
+ changeset_string = ""
15
+
16
+ # hash of path => [array of revisions]
17
+ path_revisions = {}
18
+ io.each_line do |line|
19
+ if(line =~ /-----------------------------------------------------------------/)
20
+ changeset = parse_changeset(StringIO.new(changeset_string), path_revisions)
21
+ changesets.add(changeset)
22
+ changeset_string = ""
23
+ else
24
+ changeset_string << line
25
+ end
26
+ end
27
+ changeset = parse_changeset(StringIO.new(changeset_string), path_revisions)
28
+ if((from_identifier <= changeset.time) && (changeset.time <= to_identifier))
29
+ changesets.add(changeset)
30
+ end
31
+
32
+ # set the previous revisions. most recent is at index 0.
33
+ changesets.each do |changeset|
34
+ changeset.each do |change|
35
+ current_index = path_revisions[change.path].index(change.revision)
36
+ change.previous_revision = path_revisions[change.path][current_index + 1]
37
+ end
38
+ end
39
+ changesets
40
+ end
41
+
42
+ def parse_changeset(changeset_io, path_revisions)
43
+ changeset = ChangeSet.new
44
+ state = nil
45
+ changeset_io.each_line do |line|
46
+ if(line =~ /^Revision: (.*)$/ && changeset.revision.nil?)
47
+ changeset.revision = $1
48
+ elsif(line =~ /^Author: (.*)$/ && changeset.developer.nil?)
49
+ changeset.developer = $1
50
+ elsif(line =~ /^Date: (.*)$/ && changeset.time.nil?)
51
+ changeset.time = Time.utc(
52
+ $1[0..3].to_i,
53
+ $1[5..6].to_i,
54
+ $1[8..9].to_i,
55
+ $1[11..12].to_i,
56
+ $1[14..15].to_i,
57
+ $1[17..18].to_i
58
+ )
59
+ elsif(line =~ /^ChangeLog:$/ && changeset.message.nil?)
60
+ state = :message
61
+ elsif(state == :message && changeset.message.nil?)
62
+ changeset.message = ""
63
+ elsif(state == :message && changeset.message)
64
+ changeset.message << line
65
+ elsif(line =~ /^Added files:$/)
66
+ state = :added
67
+ elsif(state == :added)
68
+ add_changes(changeset, line, Change::ADDED, path_revisions)
69
+ elsif(line =~ /^Modified files:$/)
70
+ state = :modified
71
+ elsif(state == :modified)
72
+ add_changes(changeset, line, Change::MODIFIED, path_revisions)
73
+ end
74
+ end
75
+ changeset.message.chomp!
76
+ changeset
77
+ end
78
+
79
+ private
80
+
81
+ def add_changes(changeset, line, state, path_revisions)
82
+ paths = line.split(" ")
83
+ paths.each do |path|
84
+ changeset << Change.new(path, state, changeset.developer, nil, changeset.revision, changeset.time)
85
+
86
+ # now record path revisions so we can keep track of previous rev for each path
87
+ # doesn't work for moved files, and have no idea how to make it work either.
88
+ path_revisions[path] ||= []
89
+ path_revisions[path] << changeset.revision
90
+ end
91
+
92
+ end
93
+ end
94
+
95
+ end
@@ -0,0 +1,21 @@
1
+ require 'rscm/abstract_scm'
2
+
3
+ module RSCM
4
+ class Mooky < AbstractSCM
5
+ register self
6
+
7
+ ann :description => "The Foo", :tip => "Foo is nonsense"
8
+ attr_accessor :foo
9
+
10
+ ann :description => "Le Bar", :tip => "Bar toi!"
11
+ attr_accessor :bar
12
+
13
+ def initialize(foo="", bar="chocolate bar")
14
+ end
15
+
16
+ def name
17
+ "Mooky"
18
+ end
19
+
20
+ end
21
+ end
@@ -15,11 +15,14 @@ module RSCM
15
15
  # You need the p4/p4d executable on the PATH in order for it to work.
16
16
  #
17
17
  class Perforce < AbstractSCM
18
+ register self
19
+
18
20
  include FileUtils
19
21
 
22
+ ann :description => "Depot path", :tip => "The path to the Perforce depot"
20
23
  attr_accessor :depotpath
21
24
 
22
- def initialize(repository_root_dir = nil)
25
+ def initialize(repository_root_dir = "")
23
26
  @clients = {}
24
27
  @depotpath = repository_root_dir
25
28
  end
@@ -55,8 +58,8 @@ module RSCM
55
58
  client(checkout_dir).submit(message, &proc)
56
59
  end
57
60
 
58
- def changesets(checkout_dir, from_identifier, to_identifier = nil, files = nil)
59
- client(checkout_dir).changesets(from_identifier, to_identifier, files)
61
+ def changesets(checkout_dir, from_identifier, to_identifier=Time.infinity)
62
+ client(checkout_dir).changesets(from_identifier, to_identifier)
60
63
  end
61
64
 
62
65
  def uptodate?(checkout_dir, from_identifier)
@@ -251,7 +254,7 @@ module RSCM
251
254
  p4("sync -n").empty?
252
255
  end
253
256
 
254
- def changesets(from_identifier, to_identifier, files)
257
+ def changesets(from_identifier, to_identifier)
255
258
  changesets = changelists(from_identifier, to_identifier).collect {|changelist| to_changeset(changelist)}
256
259
  ChangeSets.new(changesets)
257
260
  end
@@ -18,10 +18,30 @@ module RSCM
18
18
  # * Apache Ant (http://ant.apache.org/)
19
19
  #
20
20
  class StarTeam < AbstractSCM
21
+ register self
21
22
 
22
- attr_accessor :user_name, :password, :server_name, :server_port, :project_name, :view_name, :folder_name
23
+ ann :description => "User name"
24
+ attr_accessor :user_name
23
25
 
24
- def initialize(user_name=nil, password=nil, server_name=nil, server_port=nil, project_name=nil, view_name=nil, folder_name=nil)
26
+ ann :description => "Password"
27
+ attr_accessor :password
28
+
29
+ ann :description => "Server name"
30
+ attr_accessor :server_name
31
+
32
+ ann :description => "Server port"
33
+ attr_accessor :server_port
34
+
35
+ ann :description => "Project name"
36
+ attr_accessor :project_name
37
+
38
+ ann :description => "View name"
39
+ attr_accessor :view_name
40
+
41
+ ann :description => "Folder name"
42
+ attr_accessor :folder_name
43
+
44
+ def initialize(user_name="", password="", server_name="", server_port="", project_name="", view_name="", folder_name="")
25
45
  @user_name, @password, @server_name, @server_port, @project_name, @view_name, @folder_name = user_name, password, server_name, server_port, project_name, view_name, folder_name
26
46
  end
27
47
 
@@ -29,7 +49,7 @@ module RSCM
29
49
  "StarTeam"
30
50
  end
31
51
 
32
- def changesets(checkout_dir, from_identifier=Time.epoch, to_identifier=Time.infinity, files=nil, &proc)
52
+ def changesets(checkout_dir, from_identifier=Time.epoch, to_identifier=Time.infinity, &proc)
33
53
  # just assuming it is a Time for now, may support labels later.
34
54
  # the java class really wants rfc822 and not rfc2822, but this works ok anyway.
35
55
  from = from_identifier.to_rfc2822
@@ -1,7 +1,7 @@
1
1
  require 'rscm/abstract_scm'
2
2
  require 'rscm/path_converter'
3
3
  require 'rscm/line_editor'
4
- require 'rscm/svn/svn_log_parser'
4
+ require 'rscm/scm/subversion_log_parser'
5
5
 
6
6
  module RSCM
7
7
 
@@ -10,14 +10,21 @@ module RSCM
10
10
  # You need the svn/svnadmin executable on the PATH in order for it to work.
11
11
  #
12
12
  # NOTE: On Cygwin these have to be the win32 builds of svn/svnadmin and not the Cygwin ones.
13
- class SVN < AbstractSCM
13
+ class Subversion < AbstractSCM
14
+ register self
15
+
14
16
  include FileUtils
15
17
  include PathConverter
16
18
 
19
+ ann :description => "Repository URL"
20
+ ann :tip => "If you specify a local URL (starting with file://) DamageControl can create the repository for you after you save (unless the repository already exists).<br>Using a file:// URL will also give you the option to have DamageControl install a trigger in Subversion, so that you don't have to use polling to detect changes.<br>On Windows, file URLs must look like file:///C:/jupiter/mars"
17
21
  attr_accessor :url
22
+
23
+ ann :description => "Path"
24
+ ann :tip => "This is the relative path from the start of the repository <br>to the end of the URL. For example, if your URL is <br>svn://your.server/path/to/repository/path/within/repository <br>then this value should be path/within/repository."
18
25
  attr_accessor :path
19
26
 
20
- def initialize(url=nil, path="")
27
+ def initialize(url="", path="trunk")
21
28
  @url, @path = url, path
22
29
  end
23
30
 
@@ -98,7 +105,7 @@ module RSCM
98
105
  cmd = "svn log #{repourl} -r HEAD"
99
106
  with_working_dir(checkout_dir) do
100
107
  safer_popen(cmd) do |stdout|
101
- parser = SVNLogParser.new(stdout, path, checkout_dir)
108
+ parser = SubversionLogParser.new(stdout, path, checkout_dir)
102
109
  changesets = parser.parse_changesets
103
110
  changesets[0].revision.to_i
104
111
  end
@@ -117,7 +124,7 @@ module RSCM
117
124
 
118
125
  def diff(checkout_dir, change, &block)
119
126
  with_working_dir(checkout_dir) do
120
- cmd = "svn diff -r #{change.previous_revision}:#{change.revision} #{url}/#{change.path}"
127
+ cmd = "svn diff -r #{change.previous_revision}:#{change.revision} \"#{url}/#{change.path}\""
121
128
  safer_popen(cmd) do |io|
122
129
  return(yield(io))
123
130
  end
@@ -179,18 +186,18 @@ module RSCM
179
186
  svn(dir, import_cmd)
180
187
  end
181
188
 
182
- def changesets(checkout_dir, from_identifier, to_identifier=Time.infinity, files=nil)
189
+ def changesets(checkout_dir, from_identifier, to_identifier=Time.infinity)
183
190
  # Return empty changeset if the requested revision doesn't exist yet.
184
191
  return ChangeSets.new if(from_identifier.is_a?(Integer) && head_revision(checkout_dir) < from_identifier)
185
192
 
186
193
  checkout_dir = PathConverter.filepath_to_nativepath(checkout_dir, false)
187
194
  changesets = nil
188
- command = "svn #{changes_command(from_identifier, to_identifier, files)}"
195
+ command = "svn #{changes_command(from_identifier, to_identifier)}"
189
196
  yield command if block_given?
190
197
 
191
198
  with_working_dir(checkout_dir) do
192
199
  safer_popen(command) do |stdout|
193
- parser = SVNLogParser.new(stdout, path, checkout_dir)
200
+ parser = SubversionLogParser.new(stdout, path, checkout_dir)
194
201
  changesets = parser.parse_changesets
195
202
  end
196
203
  end
@@ -274,7 +281,7 @@ module RSCM
274
281
  "update #{revision_option(nil,to_identifier)}"
275
282
  end
276
283
 
277
- def changes_command(from_identifier, to_identifier, files)
284
+ def changes_command(from_identifier, to_identifier)
278
285
  # http://svnbook.red-bean.com/svnbook-1.1/svn-book.html#svn-ch-3-sect-3.3
279
286
  # file_list = files.join('\n')
280
287
  # WEIRD cygwin bug garbles this!?!?!?!
@@ -312,7 +319,7 @@ module RSCM
312
319
 
313
320
  def svndate(time)
314
321
  return nil unless time
315
- time.utc.strftime("\"{%Y-%m-%d %H:%M:%S\"}")
322
+ time.utc.strftime("{\"%Y-%m-%d %H:%M:%S\"}")
316
323
  end
317
324
 
318
325
  def commit_command(message)