rscm-accurev 0.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 (52) hide show
  1. data/LICENSE +25 -0
  2. data/README +9 -0
  3. data/Rakefile +137 -0
  4. data/STATUS +63 -0
  5. data/TODO +43 -0
  6. data/apitest.rb +21 -0
  7. data/bumprelease.sh +13 -0
  8. data/lib/rscm/accurev.rb +18 -0
  9. data/lib/rscm/scm/accurev/api.rb +411 -0
  10. data/lib/rscm/scm/accurev/api.rb.mine +382 -0
  11. data/lib/rscm/scm/accurev/api.rb.r263 +364 -0
  12. data/lib/rscm/scm/accurev/api.rb.r265 +393 -0
  13. data/lib/rscm/scm/accurev/command.rb +151 -0
  14. data/lib/rscm/scm/accurev/exception.rb +38 -0
  15. data/lib/rscm/scm/accurev/filterio.rb +57 -0
  16. data/lib/rscm/scm/accurev/xml.rb +224 -0
  17. data/lib/test/unit/ui/xml/testrunner.rb +165 -0
  18. data/lib/test/unit/ui/xml/xmltestrunner.xslt +79 -0
  19. data/test/acreplay.rb +22 -0
  20. data/test/coverage/analyzer.rb +127 -0
  21. data/test/coverage/c_loader.rb +34 -0
  22. data/test/coverage/cover.rb +91 -0
  23. data/test/coverage/coverage_loader.rb +21 -0
  24. data/test/coverage/coveragetask.rb +38 -0
  25. data/test/coverage/index_tmpl.html +42 -0
  26. data/test/coverage/template.html +36 -0
  27. data/test/eg/ac-files.xml +172 -0
  28. data/test/eg/ac-pop.txt +195 -0
  29. data/test/eg/files-various-states.xml +188 -0
  30. data/test/eg/hist-oneweek-all.xml +1483 -0
  31. data/test/eg/hist-oneweek-external.xml +246 -0
  32. data/test/eg/hist-oneweek-promotes.xml +1092 -0
  33. data/test/eg/info.txt +14 -0
  34. data/test/eg/stat-a-various.xml +1789 -0
  35. data/test/eg/stat-m.xml +13 -0
  36. data/test/eg/stat-overlap.xml +13 -0
  37. data/test/eg/stat-x.xml +20 -0
  38. data/test/eg/update-i-mods-and-updates-and-overlap.xml +73 -0
  39. data/test/eg/update-i-nochanges.xml +8 -0
  40. data/test/eg/update-i-stale.xml +0 -0
  41. data/test/eg/update-i-updates.xml +125 -0
  42. data/test/eg/update-newwksp.xml +183 -0
  43. data/test/eg/update-nochanges.xml +7 -0
  44. data/test/eg/update-stale.xml +12 -0
  45. data/test/eg/update-updates.xml +147 -0
  46. data/test/t_api.rb +163 -0
  47. data/test/t_command.rb +85 -0
  48. data/test/t_filterio.rb +60 -0
  49. data/test/t_load.rb +11 -0
  50. data/test/t_scrubio.rb +117 -0
  51. data/test/t_xmlmapper.rb +75 -0
  52. metadata +106 -0
@@ -0,0 +1,382 @@
1
+ # -*- ruby -*-
2
+ #
3
+ # = api.rb
4
+ #
5
+ # API for interacting with accurev workspaces.
6
+ #
7
+ # Includes:
8
+ #
9
+ # * API
10
+ #
11
+ require 'stringio'
12
+ require 'rscm/base'
13
+ require 'rscm/path_converter'
14
+ require 'rexml/document'
15
+ require 'rscm/scm/accurev/xml'
16
+ require 'rscm/scm/accurev/filterio'
17
+ require 'rscm/scm/accurev/command'
18
+ require 'rscm/scm/accurev/exception'
19
+
20
+ # need to define module within module before you can use module X::Y
21
+ module RSCM; module Accurev; end; end
22
+
23
+ module RSCM::Accurev
24
+
25
+ #
26
+ # RSCM implementation for Accurev (http://www.accurev.com).
27
+ #
28
+ # Requires an accurev cli executable on the path (see also
29
+ # RSCM::Accurev::Command), and the correct environment
30
+ # configuration for ac server/principal/password.
31
+ #
32
+ # ---
33
+ #
34
+ # === Example
35
+ #
36
+ # api = RSCM::Accurev::API.new( "./ws/proj-1", "proj", "proj-1" )
37
+ # api.checkout() each do |file|
38
+ # puts "Updated #{file}..."
39
+ # end
40
+ #
41
+ class API < RSCM::Base
42
+ register self
43
+
44
+ ann :description => "Accurev Depot"
45
+ attr_accessor :depot
46
+
47
+ ann :description => "Backing Stream (autodetected)"
48
+ attr_accessor :backing_stream
49
+
50
+ # If workspace stream is nil (the default), checkout/update will be
51
+ # done using `accurev pop`. This is faster and more appropriate for
52
+ # read-only use; however a workspace is required for commits.
53
+ ann :description => "Workspace Stream"
54
+ attr_accessor :workspace_stream
55
+
56
+ # [defined in Base]
57
+ # ann :description => "Filesystem Path to Workspace"
58
+ # attr_accessor :checkout_dir
59
+
60
+ ann :description => "Overwrite Mode: if true, co overwrites existing"
61
+ attr_accessor :overwrite
62
+
63
+ def initialize(checkout_dir=nil, backing_stream=nil, workspace_stream=nil)
64
+ @depot = nil # will be pulled from files cmd output
65
+ @workspace_stream = workspace_stream
66
+ @backing_stream = backing_stream
67
+ @checkout_dir = checkout_dir
68
+ @overwrite = false
69
+ end
70
+
71
+ def name()
72
+ return "Accurev"
73
+ end
74
+
75
+ # Accurev operations are atomic: returns true.
76
+ def transactional?
77
+ return true
78
+ end
79
+
80
+ # "Adds +relative_filename+ to the working copy."
81
+ def add( relative_filename )
82
+ raise "XXX implement me"
83
+ end
84
+
85
+ def edit( relative_filename )
86
+ # NOP - not required for ac
87
+ end
88
+
89
+ # "Returns a Revisions object for the period specified ... (inclusive)."
90
+ #
91
+ # list txns (promotes to bs) from hist within the period
92
+ #
93
+ def revisions( from_identifier, to_identifier=Time.infinity )
94
+ raise "XXX implement me"
95
+ end
96
+
97
+ # default impl depends on a correct impl of revisions()
98
+ # def uptodate?( identifier=nil )
99
+ #
100
+ # end
101
+
102
+ # def checked_out? - base
103
+
104
+ # accurev actually does have triggers, but I haven't implemented that yet
105
+ def supports_trigger?
106
+ false
107
+ end
108
+
109
+ # def diff() XXX
110
+
111
+
112
+ # --- ac specific
113
+
114
+ # remember: ac_files is non-recursive
115
+ def ac_files( relative_path )
116
+ cmd = Command.instance
117
+ ret = []
118
+ with_working_dir( co_filepath ) do
119
+ acresponse = cmd.accurev( "files", relative_path )
120
+ if acresponse['element'].nil?
121
+ return []
122
+ end
123
+ acresponse['element'].each do |fd|
124
+ yield fd if block_given?
125
+ ret << fd
126
+ end
127
+ end
128
+ return ret
129
+ end
130
+
131
+ def ac_stat( flag="-a" )
132
+ cmd = Command.instance
133
+ ret = []
134
+ with_working_dir( co_filepath ) do
135
+ acresponse = cmd.accurev( "stat", flag )
136
+ return [] if acresponse['element'].nil?
137
+ acresponse['element'].each do |fd|
138
+ yield fd if block_given?
139
+ ret << fd
140
+ end
141
+ end
142
+ return ret
143
+ end
144
+
145
+ def ac_keep( files=[], message="" )
146
+ raise "XXX implement me"
147
+ end
148
+
149
+ def ac_promote( files=[], message="" )
150
+ raise "XXX implement me"
151
+ end
152
+
153
+ def ac_update( relative_path="." )
154
+ d = accurev( "update", relative_path )
155
+ if xxx_error_stale
156
+ raise StaleWorkspaceError.new( "#{relative_path} is stale -- keep/anchor all changes and re-update" )
157
+ end
158
+ end
159
+
160
+ def ac_purge( files=[] )
161
+ raise "XXX implement me"
162
+ end
163
+
164
+ def ac_revert( files=[] )
165
+ raise "XXX implement me"
166
+ end
167
+
168
+ def ac_move( current_file, new_file )
169
+ raise "XXX implement me"
170
+ end
171
+
172
+ #
173
+ # Performs an "accurev info" command in the workspace.
174
+ # Returns a map of
175
+ #
176
+ # ac info: shows eg, backing stream
177
+ # but doesn't support -fx!
178
+ #
179
+ def ac_info()
180
+ co = RSCM::PathConverter.nativepath_to_filepath( @checkout_dir )
181
+ unless File.exists?( co )
182
+ raise AccurevException.new( "Checkout dir #{co} does not exist" )
183
+ end
184
+ info = {}
185
+ with_working_dir( co ) do
186
+ cmd = Command.instance
187
+ io = StringIO.new( cmd.accurev_nofx( "info" ) )
188
+ io.each_line do |line|
189
+ next unless line =~ /\S/
190
+ if line =~ /^(.*?):\s+(.*)$/
191
+ info[$1] = $2
192
+ end
193
+ end
194
+ end
195
+ return info
196
+ end
197
+
198
+ # ac mkws
199
+ # -b orbitz-host-gt3-0-impl
200
+ # -w orbitz-host-gt3-0-impl-test2_gfast
201
+ # -l `pwd`
202
+ # - does not support fx (argh!@)
203
+ # - does not pop
204
+ #
205
+
206
+ # diff: /usr/local/bin/acdiff (!) --fx
207
+ # (after an ac cat on the backed file)
208
+ # (eg, no in-process diffing ala cvs diff)
209
+ # lists modified lines in xml
210
+
211
+ #
212
+ # "Checks out or updates[!] contents from a central SCM
213
+ # to +checkout_dir+ - a local working copy."
214
+ #
215
+ # "The +to_identifier+ parameter may be optionally specified to
216
+ # obtain files up to a particular time or label."
217
+ #
218
+ # For accurev, this:
219
+ # * checks to see if +@checkout_dir+ exists and appears checked out.
220
+ # If it's already checked out, this calls +update()+. If
221
+ # +@checkout_dir+ exists and +to_identifier+ is given, an
222
+ # exception is raised.
223
+ # * otherwise, this creates a new workspace stream and populates it
224
+ # at +@checkout_dir+.
225
+ #
226
+ # This both returns and yields a list of updated files.
227
+ # Only updated files are returned, not directories.
228
+ #
229
+ # Current, ac_update returns both files and directories, including
230
+ # deleted files.
231
+ #
232
+ def checkout( to_identifier=Time.infinite )
233
+ co = RSCM::PathConverter.nativepath_to_filepath( @checkout_dir )
234
+ if @backing_stream.nil?
235
+ self.attempt_init_from_info()
236
+ end
237
+ if @workspace_stream.nil?
238
+ self.checkout_pop()
239
+ else
240
+ unless File.exists?( co )
241
+ # create new workspace
242
+ self.checkout_workspace()
243
+ end
244
+ # update workspace
245
+ self.update( to_identifier )
246
+ end
247
+ end
248
+
249
+ def checkout_pop()
250
+ co = RSCM::PathConverter.nativepath_to_filepath( @checkout_dir )
251
+ raise "A backing stream is required" if @backing_stream.nil?
252
+ raise "A working stream may not be given" unless @working_stream.nil?
253
+ # for `accurev pop`: remove and re-pop the checkout copy
254
+ if File.exists?( co )
255
+ unless @overwrite
256
+ raise "Checkout dir #{co} already exists (@overwrite=#@overwrite)"
257
+ end
258
+ rm_rf( co )
259
+ end
260
+ mkdir(co)
261
+ with_working_dir( co ) do
262
+ cmd = Command.instance
263
+ pop_out = cmd.accurev_nofx("pop",
264
+ "-R",
265
+ "-v", @backing_stream,
266
+ "-L", ".", ".")
267
+ elems = []
268
+ popio = StringIO.new( pop_out )
269
+ popio.each_line do |line|
270
+ if line =~ /^Populating element \/.\/(.*)$/
271
+ loc = $1
272
+ elems << loc
273
+ yield loc if block_given?
274
+ end
275
+ end
276
+ return elems
277
+ end
278
+ end
279
+
280
+ def checkout_workspace()
281
+ co = RSCM::PathConverter.nativepath_to_filepath( @checkout_dir )
282
+ raise "A backing stream is required" if @backing_stream.nil?
283
+ raise "A workspace is required" if @working_stream.nil?
284
+ if File.exist?( co ) and !@overwrite
285
+ raise "Checkout dir #{co} already exists (@overwrite=#@overwrite)"
286
+ end
287
+ cmd = Command.instance
288
+ mkws_out = cmd.accurev_nofx( "mkws",
289
+ "-b", @backing_stream,
290
+ "-w", @workspace_stream,
291
+ "-l", co )
292
+ # kinda sucks:
293
+ if ( mkws_out =~ /already exists/ )
294
+ raise AccurevException.new( "Failed to checkout", mkws_out )
295
+ end
296
+ end
297
+
298
+ def update( to_identifier=Time.infinite )
299
+ co = RSCM::PathConverter.nativepath_to_filepath( @checkout_dir )
300
+ unless File.exists?( co )
301
+ raise AccurevException.new( "Workspace does not exist!" )
302
+ end
303
+ updated = []
304
+ with_working_dir( co ) do
305
+ cmd = Command.instance
306
+ acresponse = cmd.accurev( "update" )
307
+ if acresponse.error
308
+ if acresponse.error =~ /workspace have been modified/
309
+ raise StaleWorkspaceException.new( "Workspace stale",
310
+ acresponse.error )
311
+ else
312
+ # some other update problem
313
+ raise AccurevException.new( "Error on update", acresponse.error )
314
+ end
315
+ end
316
+ if acresponse['element']
317
+ acresponse['element'].each do |up|
318
+ # "ac update" on a new workspace yields element locations
319
+ # with leading "/"s, which should be removed to get a proper
320
+ # relative path:
321
+ loc = up.location.sub( /^\//, "" )
322
+ yield loc if block_given?
323
+ updated << loc
324
+ end
325
+ end
326
+ end
327
+ return updated
328
+ end
329
+
330
+ # --- internals
331
+
332
+ # status flag mappings for "stat", "file" commands
333
+ STATUSES = {
334
+ '(backed)' => :backed,
335
+ '(external)' => :external,
336
+ '(modified)' => :modified,
337
+ '(kept)' => :kept,
338
+ '(defunct)' => :defunct,
339
+ '(missing)' => :missing,
340
+ '(stranded)' => :stranded,
341
+ '(overlap)' => :overlap
342
+ }
343
+
344
+ # private
345
+
346
+ # psuedo-accessor (cached)
347
+ def co_filepath
348
+ if @co_filepath.nil?
349
+ @co_filepath = RSCM::PathConverter.nativepath_to_filepath( @checkout_dir )
350
+ end
351
+ return @co_filepath
352
+ end
353
+
354
+ #
355
+ # Runs an `ac info` command in +@checkout_dir+, and tries to
356
+ # set +@backing_stream+ and +@workspace_stream+ from the output.
357
+ #
358
+ def attempt_init_from_info()
359
+ if File.exists?( self.co_filepath )
360
+ info = self.ac_info
361
+ if info.has_key?( "Basis" )
362
+ @backing_stream = info["Basis"]
363
+ end
364
+ if info.has_key?( "ws/ref" )
365
+ @workspace_stream = info["ws/ref"]
366
+ end
367
+ end
368
+ end
369
+
370
+ # Takes a status flags line (eg, "(modified)(kept)")
371
+ # and returns a list of status flags.
372
+ def map_status( status_line )
373
+ l = status_line.split( " " ).map {|x| x.trim}
374
+ end
375
+
376
+ end
377
+
378
+ end
379
+
380
+ #
381
+ # Copyright (c) 2005 Gregory D. Fast <gdf@speakeasy.net>
382
+ #
@@ -0,0 +1,364 @@
1
+ # -*- ruby -*-
2
+ #
3
+ # = api.rb
4
+ #
5
+ # API for interacting with accurev workspaces.
6
+ #
7
+ # Includes:
8
+ #
9
+ # * API
10
+ #
11
+ require 'stringio'
12
+ require 'rscm/base'
13
+ require 'rscm/path_converter'
14
+ require 'rexml/document'
15
+ require 'rscm/scm/accurev/xml'
16
+ require 'rscm/scm/accurev/filterio'
17
+ require 'rscm/scm/accurev/command'
18
+ require 'rscm/scm/accurev/exception'
19
+
20
+ # need to define module within module before you can use module X::Y
21
+ module RSCM; module Accurev; end; end
22
+
23
+ module RSCM::Accurev
24
+
25
+ #
26
+ # RSCM implementation for Accurev (http://www.accurev.com).
27
+ #
28
+ # Requires an accurev cli executable on the path (see also
29
+ # RSCM::Accurev::Command), and the correct environment
30
+ # configuration for ac server/principal/password.
31
+ #
32
+ # ---
33
+ #
34
+ # === Example
35
+ #
36
+ # api = RSCM::Accurev::API.new( "./ws/proj-1", "proj", "proj-1" )
37
+ # api.checkout() each do |file|
38
+ # puts "Updated #{file}..."
39
+ # end
40
+ #
41
+ class API < RSCM::Base
42
+ register self
43
+
44
+ ann :description => "Accurev Depot"
45
+ attr_accessor :depot
46
+
47
+ ann :description => "Backing Stream (autodetected)"
48
+ attr_accessor :backing_stream
49
+
50
+ # If workspace stream is nil (the default), checkout/update will be
51
+ # done using `accurev pop`. This is faster and more appropriate for
52
+ # read-only use; however a workspace is required for commits.
53
+ ann :description => "Workspace Stream"
54
+ attr_accessor :workspace_stream
55
+
56
+ # [defined in Base]
57
+ # ann :description => "Filesystem Path to Workspace"
58
+ # attr_accessor :checkout_dir
59
+
60
+ ann :description => "Overwrite Mode: if true, co overwrites existing"
61
+ attr_accessor :overwrite
62
+
63
+ def initialize(checkout_dir=nil, backing_stream=nil, workspace_stream=nil)
64
+ @depot = nil # will be pulled from files cmd output
65
+ @workspace_stream = workspace_stream
66
+ @backing_stream = backing_stream
67
+ @checkout_dir = checkout_dir
68
+ @overwrite = false
69
+ end
70
+
71
+ def name()
72
+ return "Accurev"
73
+ end
74
+
75
+ # Accurev operations are atomic: returns true.
76
+ def transactional?
77
+ return true
78
+ end
79
+
80
+ # "Adds +relative_filename+ to the working copy."
81
+ def add( relative_filename )
82
+ raise "XXX implement me"
83
+ end
84
+
85
+ def edit( relative_filename )
86
+ # NOP - not required for ac
87
+ end
88
+
89
+ # "Returns a Revisions object for the period specified ... (inclusive)."
90
+ #
91
+ # list txns (promotes to bs) from hist within the period
92
+ #
93
+ def revisions( from_identifier, to_identifier=Time.infinity )
94
+ raise "XXX implement me"
95
+ end
96
+
97
+ # default impl depends on a correct impl of revisions()
98
+ # def uptodate?( identifier=nil )
99
+ #
100
+ # end
101
+
102
+ # def checked_out? - base
103
+
104
+ # accurev actually does have triggers, but I haven't implemented that yet
105
+ def supports_trigger?
106
+ false
107
+ end
108
+
109
+ # def diff() XXX
110
+
111
+
112
+ # --- ac specific
113
+
114
+ def ac_files( relative_path )
115
+ cmd = Command.instance
116
+ ret = []
117
+ with_working_dir( @checkout_dir ) do
118
+ acresponse = cmd.accurev( "files", relative_path )
119
+ acresponse['element'].each do |fd|
120
+ yield fd if block_given?
121
+ ret << fd
122
+ end
123
+ end
124
+ return ret
125
+ end
126
+
127
+ def ac_keep( files=[], message="" )
128
+ raise "XXX implement me"
129
+ end
130
+
131
+ def ac_promote( files=[], message="" )
132
+ raise "XXX implement me"
133
+ end
134
+
135
+ def ac_update( relative_path="." )
136
+ d = accurev( "update", relative_path )
137
+ if xxx_error_stale
138
+ raise StaleWorkspaceError.new( "#{relative_path} is stale -- keep/anchor all changes and re-update" )
139
+ end
140
+ end
141
+
142
+ def ac_purge( files=[] )
143
+ raise "XXX implement me"
144
+ end
145
+
146
+ def ac_revert( files=[] )
147
+ raise "XXX implement me"
148
+ end
149
+
150
+ def ac_move( current_file, new_file )
151
+ raise "XXX implement me"
152
+ end
153
+
154
+ #
155
+ # Performs an "accurev info" command in the workspace.
156
+ # Returns a map of
157
+ #
158
+ # ac info: shows eg, backing stream
159
+ # but doesn't support -fx!
160
+ #
161
+ def ac_info()
162
+ co = RSCM::PathConverter.nativepath_to_filepath( @checkout_dir )
163
+ unless File.exists?( co )
164
+ raise AccurevException.new( "Checkout dir #{co} does not exist" )
165
+ end
166
+ info = {}
167
+ with_working_dir( co ) do
168
+ cmd = Command.instance
169
+ io = StringIO.new( cmd.accurev_nofx( "info" ) )
170
+ io.each_line do |line|
171
+ next unless line =~ /\S/
172
+ if line =~ /^(.*?):\s+(.*)$/
173
+ info[$1] = $2
174
+ end
175
+ end
176
+ end
177
+ return info
178
+ end
179
+
180
+ # ac mkws
181
+ # -b orbitz-host-gt3-0-impl
182
+ # -w orbitz-host-gt3-0-impl-test2_gfast
183
+ # -l `pwd`
184
+ # - does not support fx (argh!@)
185
+ # - does not pop
186
+ #
187
+
188
+ # diff: /usr/local/bin/acdiff (!) --fx
189
+ # (after an ac cat on the backed file)
190
+ # (eg, no in-process diffing ala cvs diff)
191
+ # lists modified lines in xml
192
+
193
+ #
194
+ # "Checks out or updates[!] contents from a central SCM
195
+ # to +checkout_dir+ - a local working copy."
196
+ #
197
+ # "The +to_identifier+ parameter may be optionally specified to
198
+ # obtain files up to a particular time or label."
199
+ #
200
+ # For accurev, this:
201
+ # * checks to see if +@checkout_dir+ exists and appears checked out.
202
+ # If it's already checked out, this calls +update()+. If
203
+ # +@checkout_dir+ exists and +to_identifier+ is given, an
204
+ # exception is raised.
205
+ # * otherwise, this creates a new workspace stream and populates it
206
+ # at +@checkout_dir+.
207
+ #
208
+ # This both returns and yields a list of updated files.
209
+ # Only updated files are returned, not directories.
210
+ #
211
+ # Current, ac_update returns both files and directories, including
212
+ # deleted files.
213
+ #
214
+ def checkout( to_identifier=Time.infinite )
215
+ co = RSCM::PathConverter.nativepath_to_filepath( @checkout_dir )
216
+ if @backing_stream.nil?
217
+ self.attempt_init_from_info()
218
+ end
219
+ if @workspace_stream.nil?
220
+ self.checkout_pop()
221
+ else
222
+ unless File.exists?( co )
223
+ # create new workspace
224
+ self.checkout_workspace()
225
+ end
226
+ # update workspace
227
+ self.update( to_identifier )
228
+ end
229
+ end
230
+
231
+ def checkout_pop()
232
+ co = RSCM::PathConverter.nativepath_to_filepath( @checkout_dir )
233
+ raise "A backing stream is required" if @backing_stream.nil?
234
+ raise "A working stream may not be given" unless @working_stream.nil?
235
+ # for `accurev pop`: remove and re-pop the checkout copy
236
+ if File.exists?( co )
237
+ unless @overwrite
238
+ raise "Checkout dir #{co} already exists (@overwrite=#@overwrite)"
239
+ end
240
+ rm_rf( co )
241
+ end
242
+ mkdir(co)
243
+ with_working_dir( co ) do
244
+ cmd = Command.instance
245
+ pop_out = cmd.accurev_nofx("pop",
246
+ "-R",
247
+ "-v", @backing_stream,
248
+ "-L", ".", ".")
249
+ elems = []
250
+ popio = StringIO.new( pop_out )
251
+ popio.each_line do |line|
252
+ if line =~ /^Populating element \/.\/(.*)$/
253
+ loc = $1
254
+ elems << loc
255
+ yield loc if block_given?
256
+ end
257
+ end
258
+ return elems
259
+ end
260
+ end
261
+
262
+ def checkout_workspace()
263
+ co = RSCM::PathConverter.nativepath_to_filepath( @checkout_dir )
264
+ raise "A backing stream is required" if @backing_stream.nil?
265
+ raise "A workspace is required" if @working_stream.nil?
266
+ if File.exist?( co ) and !@overwrite
267
+ raise "Checkout dir #{co} already exists (@overwrite=#@overwrite)"
268
+ end
269
+ cmd = Command.instance
270
+ mkws_out = cmd.accurev_nofx( "mkws",
271
+ "-b", @backing_stream,
272
+ "-w", @workspace_stream,
273
+ "-l", co )
274
+ # kinda sucks:
275
+ if ( mkws_out =~ /already exists/ )
276
+ raise AccurevException.new( "Failed to checkout", mkws_out )
277
+ end
278
+ end
279
+
280
+ def update( to_identifier=Time.infinite )
281
+ co = RSCM::PathConverter.nativepath_to_filepath( @checkout_dir )
282
+ unless File.exists?( co )
283
+ raise AccurevException.new( "Workspace does not exist!" )
284
+ end
285
+ updated = []
286
+ with_working_dir( co ) do
287
+ cmd = Command.instance
288
+ acresponse = cmd.accurev( "update" )
289
+ if acresponse.error
290
+ if acresponse.error =~ /workspace have been modified/
291
+ raise StaleWorkspaceException.new( "Workspace stale",
292
+ acresponse.error )
293
+ else
294
+ # some other update problem
295
+ raise AccurevException.new( "Error on update", acresponse.error )
296
+ end
297
+ end
298
+ if acresponse['element']
299
+ acresponse['element'].each do |up|
300
+ # "ac update" on a new workspace yields element locations
301
+ # with leading "/"s, which should be removed to get a proper
302
+ # relative path:
303
+ loc = up.location.sub( /^\//, "" )
304
+ yield loc if block_given?
305
+ updated << loc
306
+ end
307
+ end
308
+ end
309
+ return updated
310
+ end
311
+
312
+ # --- internals
313
+
314
+ # status flag mappings for "stat", "file" commands
315
+ STATUSES = {
316
+ '(backed)' => :backed,
317
+ '(external)' => :external,
318
+ '(modified)' => :modified,
319
+ '(kept)' => :kept,
320
+ '(defunct)' => :defunct,
321
+ '(missing)' => :missing,
322
+ '(stranded)' => :stranded,
323
+ '(overlap)' => :overlap
324
+ }
325
+
326
+ # private
327
+
328
+ # psuedo-accessor (cached)
329
+ def co_filepath
330
+ if @co_filepath.nil?
331
+ @co_filepath = RSCM::PathConverter.nativepath_to_filepath( @checkout_dir )
332
+ end
333
+ return @co_filepath
334
+ end
335
+
336
+ #
337
+ # Runs an `ac info` command in +@checkout_dir+, and tries to
338
+ # set +@backing_stream+ and +@workspace_stream+ from the output.
339
+ #
340
+ def attempt_init_from_info()
341
+ if File.exists?( self.co_filepath )
342
+ info = self.ac_info
343
+ if info.has_key?( "Basis" )
344
+ @backing_stream = info["Basis"]
345
+ end
346
+ if info.has_key?( "ws/ref" )
347
+ @workspace_stream = info["ws/ref"]
348
+ end
349
+ end
350
+ end
351
+
352
+ # Takes a status flags line (eg, "(modified)(kept)")
353
+ # and returns a list of status flags.
354
+ def map_status( status_line )
355
+ l = status_line.split( " " ).map {|x| x.trim}
356
+ end
357
+
358
+ end
359
+
360
+ end
361
+
362
+ #
363
+ # Copyright (c) 2005 Gregory D. Fast <gdf@speakeasy.net>
364
+ #