rscm 0.3.14 → 0.3.15

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,5 +1,12 @@
1
1
  = RSCM Changelog
2
2
 
3
+ == Version 0.3.15
4
+
5
+ This release adds support for directory listings and fixes some incompatibilities with CVS 1.12.x
6
+
7
+ * Added support for directory listings.
8
+ * Added support for parsing of revisions for CVS 1.12.x, which uses a slightly different time format.
9
+
3
10
  == Version 0.3.14
4
11
 
5
12
  Improved error messages
data/README CHANGED
@@ -1,4 +1,4 @@
1
- = RSCM - Ruby Source Control Management (0.3.14)
1
+ = RSCM - Ruby Source Control Management (0.3.15)
2
2
 
3
3
  RSCM is to SCM what DBI/JDBC/ODBC are to databases - an SCM-independent API for accessing different SCMs. The high level features are roughly:
4
4
 
data/Rakefile CHANGED
@@ -10,7 +10,7 @@ require 'meta_project'
10
10
 
11
11
  PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
12
12
  PKG_NAME = 'rscm'
13
- PKG_VERSION = '0.3.14' + PKG_BUILD
13
+ PKG_VERSION = '0.3.15' + PKG_BUILD
14
14
  PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
15
15
 
16
16
  desc "Default Task"
@@ -7,22 +7,17 @@ module RSCM
7
7
  # TODO: make this a module and remove the attr_reader
8
8
  class AbstractLogParser
9
9
 
10
- attr_reader :io
11
-
12
10
  def initialize(io)
13
11
  @io = io
14
- @current_line_number = 0
15
- @had_error = false
16
12
  end
17
13
 
18
14
  def read_until_matching_line(regexp)
19
- return nil if io.eof?
15
+ return nil if @io.eof?
20
16
  result = ""
21
- io.each_line do |line|
22
- @current_line_number += 1
17
+ @io.each_line do |line|
23
18
  line.gsub!(/\r\n$/, "\n")
24
- break if line=~regexp
25
- result<<line
19
+ break if line =~ regexp
20
+ result << line
26
21
  end
27
22
  if result.strip == ""
28
23
  read_until_matching_line(regexp)
@@ -35,15 +30,6 @@ module RSCM
35
30
  file.gsub(/\\/, "/")
36
31
  end
37
32
 
38
- def error(msg)
39
- @had_error=true
40
- $stderr.puts(msg + "\ncurrent line: #{@current_line}\nstack trace:\n")
41
- $stderr.puts(caller.backtrace.join('\n\t'))
42
- end
43
-
44
- def had_error?
45
- @had_error
46
- end
47
33
  end
48
34
 
49
35
  end
@@ -16,6 +16,7 @@ module RSCM
16
16
  # * checked_out?
17
17
  # * diff
18
18
  # * edit
19
+ # * ls
19
20
  # * move
20
21
  # * revisions
21
22
  # * uptodate?
@@ -108,13 +109,14 @@ module RSCM
108
109
  # example if the repository is 'remote' or if it already exists).
109
110
  #
110
111
  def create_central
111
- raise "Not implemented"
112
+ raise NotImplementedError
112
113
  end
113
114
 
114
115
  # Destroys the central repository. Shuts down any server processes and deletes the repository.
115
116
  # WARNING: calling this may result in loss of data. Only call this if you really want to wipe
116
117
  # it out for good!
117
118
  def destroy_central
119
+ raise NotImplementedError
118
120
  end
119
121
 
120
122
  # Whether a repository can be created.
@@ -124,12 +126,14 @@ module RSCM
124
126
 
125
127
  # Adds +relative_filename+ to the working copy.
126
128
  def add(relative_filename)
129
+ raise NotImplementedError
127
130
  end
128
131
 
129
132
  # Schedules a move of +relative_src+ to +relative_dest+
130
133
  # Should not take effect in the central repository until
131
134
  # +commit+ is invoked.
132
135
  def move(relative_src, relative_dest)
136
+ raise NotImplementedError
133
137
  end
134
138
 
135
139
  # Recursively imports files from a +dir+ into the central scm
@@ -143,6 +147,7 @@ module RSCM
143
147
 
144
148
  # Commit (check in) modified files.
145
149
  def commit(message)
150
+ raise NotImplementedError
146
151
  end
147
152
 
148
153
  # Checks out or updates contents from a central SCM to +checkout_dir+ - a local working copy.
@@ -190,27 +195,22 @@ module RSCM
190
195
  # revisions pertaining to that path.
191
196
  #
192
197
  def revisions(from_identifier, to_identifier=Time.infinity, relative_path=nil)
193
- # Should be overridden by subclasses
194
- revisions = Revisions.new
195
- revisions.add(
196
- Revision.new(
197
- "up/the/chimney",
198
- Revision::DELETED,
199
- "DamageControl",
200
- "The #{name} class doesn't\n" +
201
- "correctly implement the revisions method. This is\n" +
202
- "not a real revision, but a hint to the developer to go and implement it.\n\n" +
203
- "Do It Now!",
204
- "999",
205
- Time.now.utc
206
- )
207
- )
208
- revisions
198
+ raise NotImplementedError
199
+ end
200
+
201
+ # Returns the HistoricFile representing the root of the repo
202
+ def rootdir
203
+ file("", true)
209
204
  end
210
205
 
211
206
  # Returns a HistoricFile for +relative_path+
212
- def file(relative_path)
213
- HistoricFile.new(relative_path, self)
207
+ def file(relative_path, dir)
208
+ HistoricFile.new(relative_path, dir, self)
209
+ end
210
+
211
+ # Returns an Array of the children under +relative_path+
212
+ def ls(relative_path)
213
+ raise NotImplementedError
214
214
  end
215
215
 
216
216
  # Whether the working copy is in synch with the central
@@ -243,7 +243,7 @@ module RSCM
243
243
 
244
244
  # Descriptive name of the trigger mechanism
245
245
  def trigger_mechanism
246
- "Unknown"
246
+ raise NotImplementedError
247
247
  end
248
248
 
249
249
  # Installs +trigger_command+ in the SCM.
@@ -253,37 +253,37 @@ module RSCM
253
253
  # Most implementations will ignore this parameter.
254
254
  #
255
255
  def install_trigger(trigger_command, install_dir)
256
- raise "Not implemented"
256
+ raise NotImplementedError
257
257
  end
258
258
 
259
259
  # Uninstalls +trigger_command+ from the SCM.
260
260
  #
261
261
  def uninstall_trigger(trigger_command, install_dir)
262
- raise "Not implemented"
262
+ raise NotImplementedError
263
263
  end
264
264
 
265
265
  # Whether the command denoted by +trigger_command+ is installed in the SCM.
266
266
  #
267
267
  def trigger_installed?(trigger_command, install_dir)
268
- raise "Not implemented"
268
+ raise NotImplementedError
269
269
  end
270
270
 
271
271
  # The command line to run in order to check out a fresh working copy.
272
272
  #
273
273
  def checkout_commandline(to_identifier=Time.infinity)
274
- raise "Not implemented"
274
+ raise NotImplementedError
275
275
  end
276
276
 
277
277
  # The command line to run in order to update a working copy.
278
278
  #
279
279
  def update_commandline(to_identifier=Time.infinity)
280
- raise "Not implemented"
280
+ raise NotImplementedError
281
281
  end
282
282
 
283
283
  # Returns/yields an IO containing the unified diff of the change.
284
284
  # Also see RevisionFile#diff
285
285
  def diff(change, &block)
286
- return(yield("Not implemented"))
286
+ raise NotImplementedError
287
287
  end
288
288
 
289
289
  def ==(other_scm)
@@ -1,8 +1,14 @@
1
1
  module RSCM
2
- # Represents the full history of a single file
2
+ # Represents the full history of a single file or directory.
3
3
  class HistoricFile
4
- def initialize(relative_path, scm)
5
- @relative_path, @scm = relative_path, scm
4
+ attr_reader :relative_path
5
+
6
+ def initialize(relative_path, directory, scm)
7
+ @relative_path, @directory, @scm = relative_path, directory, scm
8
+ end
9
+
10
+ def directory?
11
+ @directory
6
12
  end
7
13
 
8
14
  # Returns an Array of RevisionFile - from Time.epoch until Time.infinity (now)
@@ -18,5 +24,10 @@ module RSCM
18
24
  revision.files[0]
19
25
  end
20
26
  end
27
+
28
+ def children
29
+ raise "Not a directory" unless directory?
30
+ @scm.ls(@relative_path)
31
+ end
21
32
  end
22
33
  end
@@ -1,4 +1,3 @@
1
- require 'xmlrpc/utils'
2
1
  require 'rscm/time_ext'
3
2
  require 'rscm/revision_file'
4
3
 
@@ -7,7 +6,6 @@ module RSCM
7
6
  # A collection of Revision.
8
7
  class Revisions
9
8
  include Enumerable
10
- include XMLRPC::Marshallable
11
9
 
12
10
  attr_accessor :revisions
13
11
 
@@ -115,7 +113,6 @@ module RSCM
115
113
  # same commit message, and within a "reasonably" small timespan.
116
114
  class Revision
117
115
  include Enumerable
118
- include XMLRPC::Marshallable
119
116
 
120
117
  attr_reader :files
121
118
  attr_accessor :identifier
@@ -167,8 +164,7 @@ module RSCM
167
164
  end
168
165
 
169
166
  def ==(other)
170
- return false if !other.is_a?(self.class)
171
- @files == other.files
167
+ other.is_a?(self.class) && @files == other.files
172
168
  end
173
169
 
174
170
  def <=>(other)
@@ -177,7 +173,7 @@ module RSCM
177
173
 
178
174
  # Whether this instance can contain a File. Used
179
175
  # by non-transactional SCMs.
180
- def can_contain?(file)
176
+ def can_contain?(file) #:nodoc:
181
177
  self.developer == file.developer &&
182
178
  self.message == file.message &&
183
179
  (self.time - file.time).abs < 60
@@ -2,7 +2,6 @@ module RSCM
2
2
  # Represents a file within a Revision, and also information about how this file
3
3
  # was modified compared with the previous revision.
4
4
  class RevisionFile
5
- include XMLRPC::Marshallable
6
5
 
7
6
  MODIFIED = "MODIFIED"
8
7
  DELETED = "DELETED"
@@ -106,12 +106,36 @@ module RSCM
106
106
 
107
107
  def open(revision_file, &block)
108
108
  with_working_dir(@checkout_dir) do
109
- diff_cmd = "cvs -Q update -p -r #{revision_file.native_revision_identifier} #{revision_file.path}"
110
- Better.popen(diff_cmd) do |io|
109
+ cmd = "cvs -Q update -p -r #{revision_file.native_revision_identifier} #{revision_file.path}"
110
+ Better.popen(cmd) do |io|
111
111
  block.call io
112
112
  end
113
113
  end
114
114
  end
115
+
116
+ def ls(relative_path)
117
+ prefix = relative_path == "" ? relative_path : "#{relative_path}/"
118
+ with_working_dir(@checkout_dir) do
119
+ cmd = "cvs -Q ls -l #{relative_path}"
120
+ Better.popen(cmd) do |io|
121
+ parse_ls_log(io, prefix)
122
+ end
123
+ end
124
+ end
125
+
126
+ def parse_ls_log(io, prefix) #:nodoc:
127
+ io.collect do |line|
128
+ line.strip!
129
+ if(
130
+ (line =~ /(d)... \d\d\d\d\-\d\d\-\d\d \d\d:\d\d:\d\d \-\d\d\d\d (.*)/) ||
131
+ (line =~ /(.)... \d\d\d\d\-\d\d\-\d\d \d\d:\d\d:\d\d \-\d\d\d\d \d[\.\d]+ (.*)/)
132
+ )
133
+ directory = $1 == "d"
134
+ name = $2.strip
135
+ HistoricFile.new("#{prefix}#{name}", directory, self)
136
+ end
137
+ end
138
+ end
115
139
 
116
140
  def apply_label(label)
117
141
  cvs(@checkout_dir, "tag -c #{label}")
@@ -236,9 +260,6 @@ module RSCM
236
260
  end
237
261
 
238
262
  def parse_log(cmd, &proc)
239
- logged_command_line = command_line(cmd, hidden_password)
240
- yield logged_command_line if block_given?
241
-
242
263
  execed_command_line = command_line(cmd, password)
243
264
  revisions = nil
244
265
  with_working_dir(@checkout_dir) do
@@ -274,14 +295,6 @@ module RSCM
274
295
  "checkout #{branch_option} -d #{target_dir} #{revision_option(to_identifier)} #{mod}"
275
296
  end
276
297
 
277
- def hidden_password
278
- if(password && password != "")
279
- "********"
280
- else
281
- ""
282
- end
283
- end
284
-
285
298
  def period_option(from_identifier, to_identifier)
286
299
  if(from_identifier.nil? && to_identifier.nil?)
287
300
  ""
@@ -1,7 +1,7 @@
1
1
  require 'rscm/revision'
2
2
  require 'rscm/abstract_log_parser'
3
-
4
3
  require 'ftools'
4
+ require 'time'
5
5
 
6
6
  module RSCM
7
7
 
@@ -14,14 +14,11 @@ module RSCM
14
14
 
15
15
  def initialize(io)
16
16
  super(io)
17
- @log = ""
18
17
  end
19
18
 
20
19
  def parse_revisions
21
20
  revisions = Revisions.new
22
21
  while(log_entry = next_log_entry)
23
- @log<<log_entry
24
- @log<<""
25
22
  begin
26
23
  parse_files(log_entry, revisions)
27
24
  rescue Exception => e
@@ -91,7 +88,13 @@ module RSCM
91
88
  file.native_revision_identifier = extract_match(file_entry_lines[0], /revision (.*)$/)
92
89
 
93
90
  file.previous_native_revision_identifier = determine_previous_native_revision_identifier(file.native_revision_identifier)
94
- file.time = parse_cvs_time(extract_required_match(file_entry_lines[1], /date: (.*?)(;|$)/))
91
+
92
+ time = extract_required_match(file_entry_lines[1], /date: (.*?)(;|$)/)
93
+ if(time.strip.length == 19)
94
+ # CVS 1.11.x doesn't specify timezone (but assumes UTC), so we'll add it here.
95
+ time += " +0000"
96
+ end
97
+ file.time = Time.parse(time).utc
95
98
  file.developer = extract_match(file_entry_lines[1], /author: (.*?);/)
96
99
 
97
100
  state = extract_match(file_entry_lines[1], /state: (.*?);/)
@@ -128,13 +131,8 @@ module RSCM
128
131
  end
129
132
  end
130
133
 
131
- def parse_cvs_time(time)
132
- # 2003/11/09 15:39:25
133
- Time.utc(time[0..3], time[5..6], time[8..9], time[11..12], time[14..15], time[17..18])
134
- end
135
-
136
134
  def extract_required_match(string, regexp)
137
- if string=~regexp
135
+ if(string =~ regexp)
138
136
  return($1)
139
137
  else
140
138
  $stderr.puts("can't parse: '#{string}'\nexpected to match regexp: #{regexp.to_s}")
@@ -104,6 +104,22 @@ module RSCM
104
104
  end
105
105
  end
106
106
 
107
+ def ls(relative_path)
108
+ prefix = relative_path == "" ? relative_path : "#{relative_path}/"
109
+ cmd = "svn ls #{url}/#{relative_path}"
110
+ Better.popen(cmd) do |io|
111
+ io.collect do |line|
112
+ name = line.strip
113
+ dir = false
114
+ if(name =~ /(.*)\/$/)
115
+ name = $1
116
+ dir = true
117
+ end
118
+ HistoricFile.new("#{prefix}#{name}", dir, self)
119
+ end
120
+ end
121
+ end
122
+
107
123
  def can_create_central?
108
124
  local?
109
125
  end
@@ -11,8 +11,8 @@ module RSCM
11
11
  def teardown
12
12
  if @scm
13
13
  begin
14
- @scm.destroy_working_copy
15
- @scm.destroy_central
14
+ # @scm.destroy_working_copy
15
+ # @scm.destroy_central
16
16
  rescue => e
17
17
  # Fails on windows with TortoiseCVS' cvs because of resident cvslock.exe
18
18
  STDERR.puts "Couldn't destroy central #{@scm.class.name}: #{e.message}"
@@ -40,6 +40,7 @@ module RSCM
40
40
  # 16) Verify that OtherWorkingCopy is now uptodate
41
41
  # 17) Add and commit a file in WorkingCopy
42
42
  # 18) Verify that the revision (since last revision) for CheckoutHereToo contains only one file
43
+ # 19) Get directory listings
43
44
  def test_basics
44
45
  work_dir = RSCM.new_temp_dir("basics")
45
46
  checkout_dir = "#{work_dir}/WorkingCopy"
@@ -140,13 +141,29 @@ module RSCM
140
141
 
141
142
  # 16
142
143
  assert(other_scm.uptodate?(nil))
144
+
145
+ # 17
143
146
  add_or_edit_and_commit_file(scm, checkout_dir, "src/java/com/thoughtworks/damagecontrolled/Hello.txt", "Bla bla")
144
147
  assert(!other_scm.uptodate?(nil))
145
148
  revisions = other_scm.revisions(revisions.latest.identifier)
149
+
150
+ # 18
146
151
  assert_equal(1, revisions.length)
147
152
  assert_equal(1, revisions[0].length)
148
- assert("src/java/com/thoughtworks/damagecontrolled/Hello.txt", revisions[0][0].path)
149
- assert("src/java/com/thoughtworks/damagecontrolled/Hello.txt", other_scm.checkout.sort[0])
153
+ assert_equal("src/java/com/thoughtworks/damagecontrolled/Hello.txt", revisions[0][0].path)
154
+
155
+ # 19
156
+ root_children = scm.rootdir.children
157
+ assert_equal "build.xml", root_children[0].relative_path
158
+ assert !root_children[0].directory?
159
+ assert_equal "project.xml", root_children[1].relative_path
160
+ assert !root_children[1].directory?
161
+ assert_equal "src", root_children[2].relative_path
162
+ assert root_children[2].directory?
163
+
164
+ src_children = root_children[2].children
165
+ assert_equal "src/java", src_children[0].relative_path
166
+ assert src_children[0].directory?
150
167
  end
151
168
 
152
169
  def test_create_destroy
@@ -310,7 +327,7 @@ EOF
310
327
  assert(got_diff)
311
328
 
312
329
  # TODO: make separate test. Make helper method for the cumbersome setup!
313
- historic_afile = scm.file("afile.txt")
330
+ historic_afile = scm.file("afile.txt", false)
314
331
  revision_files = historic_afile.revision_files
315
332
  assert_equal(Array, revision_files.class)
316
333
  assert(revision_files.length >= 2)
@@ -14,10 +14,6 @@ module RSCM
14
14
  @parser.cvsmodule = "damagecontrol"
15
15
  end
16
16
 
17
- def teardown
18
- assert(!@parser.had_error?, "parser had errors") unless @parser.nil?
19
- end
20
-
21
17
  def test_read_log_entry
22
18
  assert_equal(nil, CvsLogParser.new(StringIO.new("")).next_log_entry)
23
19
  end
@@ -330,8 +326,6 @@ EOF
330
326
  assert_equal("website/templates/logo.gif", @parser.parse_path(LOG_ENTRY_FROM_06_07_2004_19_25_2))
331
327
  end
332
328
 
333
- # stand in picocontainer's java folder
334
- # cvs log -d"2003/07/25 12:38:41<=2004/07/08 12:38:41" build.xml
335
329
  LOG_WITH_DELETIONS= <<EOF
336
330
  RCS file: /home/projects/picocontainer/scm/java/Attic/build.xml,v
337
331
  Working file: build.xml
@@ -348,7 +342,7 @@ total revisions: 11; selected revisions: 2
348
342
  description:
349
343
  ----------------------------
350
344
  revision 1.11
351
- date: 2003/10/13 00:04:54; author: rinkrank; state: dead; lines: +0 -0
345
+ date: 2003/10/13 00:04:54 -0500; author: rinkrank; state: dead; lines: +0 -0
352
346
  Obsolete
353
347
  ----------------------------
354
348
  revision 1.10
@@ -363,8 +357,7 @@ EOF
363
357
  assert_equal(2, revisions.length)
364
358
 
365
359
  revision_delete = revisions[1]
366
- # assert_equal("MAIN:rinkrank:20031013000454", revision_delete.revision)
367
- assert_equal(Time.utc(2003,10,13,00,04,54,0), revision_delete.time)
360
+ assert_equal(Time.utc(2003,10,13,05,04,54,0), revision_delete.time)
368
361
  assert_equal("Obsolete", revision_delete.message)
369
362
  assert_equal("rinkrank", revision_delete.developer)
370
363
  assert_equal(1, revision_delete.length)
@@ -374,7 +367,6 @@ EOF
374
367
  assert(RevisionFile::DELETED, revision_delete[0].status)
375
368
 
376
369
  revision_fix_url = revisions[0]
377
- # assert_equal("MAIN:rinkrank:20030725163239", revision_fix_url.revision)
378
370
  assert_equal(Time.utc(2003,07,25,16,32,39,0), revision_fix_url.time)
379
371
  assert_equal("fixed broken url (NANO-8)", revision_fix_url.message)
380
372
  assert_equal("rinkrank", revision_fix_url.developer)
@@ -472,8 +464,8 @@ EOF
472
464
  # https://sitemesh.dev.java.net/source/browse/sitemesh/.cvsignore
473
465
  # The default commit message probably showed up in vi, and the committer
474
466
  # probably just left it there. Not sure why CVS kept it this way
475
- # (lines starting with CVS: should be ignored in commit message afik).
476
- # Anyway, the parser nw knows how to deal with this. (AH)
467
+ # (lines starting with CVS: should be ignored in commit message afaik).
468
+ # Anyway, the parser now knows how to deal with this. (AH)
477
469
  LOG_WITH_WEIRD_CVS_AND_MANY_DASHES = <<EOF
478
470
  ? log.txt
479
471
 
@@ -2,6 +2,7 @@ require 'test/unit'
2
2
  require 'rscm/path_converter'
3
3
  require 'rscm'
4
4
  require 'rscm/generic_scm_tests'
5
+ require 'stringio'
5
6
 
6
7
  module RSCM
7
8
 
@@ -28,5 +29,24 @@ module RSCM
28
29
  end
29
30
  end
30
31
 
32
+ LS_LOG = <<-EOF
33
+ ---- 2005-11-22 21:24:40 -0500 1.1 afile
34
+ ---- 2005-11-22 22:04:20 -0500 1.2 build.xml
35
+ ---- 2005-11-22 22:12:43 -0500 1.1 foo bar
36
+ ---- 2005-11-22 21:24:37 -0500 1.1.1.1 1.1 project.xml
37
+ d--- 2005-11-22 22:12:43 -0500 1.1 src
38
+ d--- 2005-11-22 22:12:43 -0500 togo
39
+ EOF
40
+ def test_should_parse_ls_log
41
+ history_files = Cvs.new.parse_ls_log(StringIO.new(LS_LOG), "")
42
+ assert_equal("afile", history_files[0].relative_path)
43
+ assert_equal("foo bar", history_files[2].relative_path)
44
+ assert_equal("1.1 project.xml", history_files[3].relative_path)
45
+ assert(!history_files[3].directory?)
46
+ assert_equal("1.1 src", history_files[4].relative_path)
47
+ assert(history_files[4].directory?)
48
+ assert_equal("togo", history_files[5].relative_path)
49
+ assert(history_files[5].directory?)
50
+ end
31
51
  end
32
52
  end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.10
3
3
  specification_version: 1
4
4
  name: rscm
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.3.14
7
- date: 2005-11-16
6
+ version: 0.3.15
7
+ date: 2005-11-23
8
8
  summary: "RSCM - Ruby Source Control Management"
9
9
  require_paths:
10
10
  - lib