rscm-accurev 0.0

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