svn_wc 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +13 -2
- data/README.rdoc +2 -2
- data/lib/svn_wc.rb +230 -176
- data/test/svn_wc_test.rb +159 -53
- metadata +2 -2
data/ChangeLog
CHANGED
@@ -1,2 +1,13 @@
|
|
1
|
-
version 0.0.
|
2
|
-
*
|
1
|
+
version 0.0.3
|
2
|
+
* add all possible arg options to 'add' (from client.rb)
|
3
|
+
* enable propset (ignore) only
|
4
|
+
* 'exposed the abstraction' introduced Delegation if we don't define a method pass it on to the ruby bindings.
|
5
|
+
* (code refactoring/cleanup)
|
6
|
+
|
7
|
+
version 0.0.2
|
8
|
+
* changes for backwards compatibility to subversion-ruby bindings 1.4.2
|
9
|
+
(tests failed for (Centos 5.4))
|
10
|
+
* minor Readme update
|
11
|
+
|
12
|
+
version 0.0.1
|
13
|
+
* initial release
|
data/README.rdoc
CHANGED
@@ -5,7 +5,7 @@ Operate on the working copy of a (remote) Subversion (svn) repository.
|
|
5
5
|
|
6
6
|
== VERSION:
|
7
7
|
|
8
|
-
Version 0.0.
|
8
|
+
Version 0.0.3
|
9
9
|
|
10
10
|
|
11
11
|
== SYNOPSIS:
|
@@ -265,7 +265,7 @@ See the ChangeLog file for details.
|
|
265
265
|
Copyright 2009 David Wright (david_v_wright@yahoo.com), all rights reserved.
|
266
266
|
|
267
267
|
|
268
|
-
SvnWc::RepoAccess 0.0.
|
268
|
+
SvnWc::RepoAccess 0.0.3 is released under the LGPL license.
|
269
269
|
|
270
270
|
|
271
271
|
== AUTHOR:
|
data/lib/svn_wc.rb
CHANGED
@@ -44,6 +44,8 @@ require 'svn/error'
|
|
44
44
|
# * add
|
45
45
|
# * revert
|
46
46
|
# * delete
|
47
|
+
# * log info
|
48
|
+
# * propset (ignore only)
|
47
49
|
# * svn+ssh is our primary connection use case, however can connect to, and operate on a (local) file:/// URI as well
|
48
50
|
#
|
49
51
|
# Is built on top of the SVN (SWIG) (Subversion) Ruby Bindings and requires that they be installed.
|
@@ -99,41 +101,38 @@ require 'svn/error'
|
|
99
101
|
# Author:: David V. Wright <david_v_wright@yahoo.com>
|
100
102
|
# License:: LGPL License
|
101
103
|
#
|
102
|
-
#
|
103
104
|
#--
|
104
105
|
# TODO make sure args are what is expected for all methods
|
105
|
-
# TODO
|
106
|
+
# TODO propset/propget,
|
106
107
|
# look into:
|
107
108
|
# #wc_status = infos.assoc(@wc_path).last
|
108
109
|
# #assert(wc_status.text_normal?)
|
109
110
|
# #assert(wc_status.entry.dir?)
|
110
111
|
# #assert(wc_status.entry.normal?)
|
111
112
|
# #ctx.prop_set(Svn::Core::PROP_IGNORE, file2, dir_path)
|
113
|
+
#
|
114
|
+
# currently, propset IGNORE is enabled
|
115
|
+
#
|
116
|
+
# TODO/Think About Delegation: (do we want to do this?)
|
117
|
+
# Inherit from or mixin the svn bindings directly, so method calls
|
118
|
+
# not defined here explicitly can be run againt the bindings directly
|
119
|
+
# (i.e. begin ; send(svn ruby binding method signature) ; rescue)
|
112
120
|
#++
|
113
|
-
#module Svn
|
114
|
-
# class Error
|
115
|
-
# #WC_NOT_DIRECTORY # 1.4.2
|
116
|
-
# class WcNotDirectory # 1.6.6
|
117
|
-
# raise StandardError.new(
|
118
|
-
# #WC_NOT_DIRECTORY # 1.4.2
|
119
|
-
# end
|
120
|
-
# class AuthnNoProvider
|
121
|
-
# raise StandardError
|
122
|
-
# end
|
123
|
-
# end
|
124
|
-
#end
|
125
|
-
|
126
121
|
module SvnWc
|
127
122
|
|
128
123
|
# (general) exception class raised on all errors
|
129
124
|
class RepoAccessError < RuntimeError ; end
|
130
125
|
|
126
|
+
#
|
127
|
+
# class that provides API to (common) svn operations (working copy of a repo)
|
128
|
+
# also exposes the svn ruby bindings directly
|
129
|
+
#
|
130
|
+
# It aims to provide (simple) client CLI type behavior,
|
131
|
+
# for working directory repository management. in an API
|
132
|
+
#
|
131
133
|
class RepoAccess
|
132
134
|
|
133
|
-
VERSION = '0.0.
|
134
|
-
|
135
|
-
DEFAULT_CONF_FILE = File.join(File.dirname(File.dirname(\
|
136
|
-
File.expand_path(__FILE__))), 'svn_wc_conf.yaml')
|
135
|
+
VERSION = '0.0.3'
|
137
136
|
|
138
137
|
# initialization
|
139
138
|
# three optional parameters
|
@@ -143,13 +142,24 @@ module SvnWc
|
|
143
142
|
# 3. Force. Overwrite anything that may be preventing a checkout
|
144
143
|
|
145
144
|
def initialize(conf=nil, checkout=false, force=false)
|
146
|
-
set_conf(conf)
|
145
|
+
set_conf(conf) if conf
|
147
146
|
do_checkout(force) if checkout == true
|
148
147
|
|
149
148
|
# instance var of out open repo session
|
150
149
|
@ctx = svn_session
|
151
150
|
end
|
152
151
|
|
152
|
+
#
|
153
|
+
# 'expose the abstraction'
|
154
|
+
# introduce Delegation, if we don't define the method pass it on to the
|
155
|
+
# ruby bindings.
|
156
|
+
#
|
157
|
+
# (yup, this is probably asking for trouble)
|
158
|
+
#
|
159
|
+
def method_missing(sym, *args, &block)
|
160
|
+
@ctx.send sym, *args, &block
|
161
|
+
end
|
162
|
+
|
153
163
|
#--
|
154
164
|
# TODO revist these
|
155
165
|
#++
|
@@ -157,19 +167,9 @@ module SvnWc
|
|
157
167
|
:svn_repo_working_copy, :cur_file
|
158
168
|
attr_reader :ctx, :repos
|
159
169
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
end
|
164
|
-
## do checkout if not exists at specified local path
|
165
|
-
if File.directory? @svn_repo_working_copy and not force
|
166
|
-
raise RepoAccessError, 'target local directory ' << \
|
167
|
-
"[#{@svn_repo_working_copy}] exists, please remove" << \
|
168
|
-
'or specify another directory'
|
169
|
-
end
|
170
|
-
checkout
|
171
|
-
end
|
172
|
-
|
170
|
+
#
|
171
|
+
# set config file with abs path
|
172
|
+
#
|
173
173
|
def set_conf(conf)
|
174
174
|
begin
|
175
175
|
conf = load_conf(conf)
|
@@ -184,6 +184,35 @@ module SvnWc
|
|
184
184
|
end
|
185
185
|
end
|
186
186
|
|
187
|
+
|
188
|
+
def do_checkout(force=false)
|
189
|
+
if @svn_repo_working_copy.nil?
|
190
|
+
raise RepoAccessError, 'conf file not loaded! - Fatal Error'
|
191
|
+
end
|
192
|
+
## do checkout if not exists at specified local path
|
193
|
+
|
194
|
+
if force
|
195
|
+
begin
|
196
|
+
#FileUtils.rm_rf @svn_repo_working_copy
|
197
|
+
FileUtils.mkdir_p @svn_repo_working_copy, :force => true
|
198
|
+
rescue
|
199
|
+
end
|
200
|
+
else
|
201
|
+
if File.directory? @svn_repo_working_copy
|
202
|
+
raise RepoAccessError, 'target local directory ' << \
|
203
|
+
"[#{@svn_repo_working_copy}] exists, please remove" << \
|
204
|
+
'or specify another directory'
|
205
|
+
end
|
206
|
+
begin
|
207
|
+
FileUtils.mkdir_p @svn_repo_working_copy
|
208
|
+
rescue Errno::EACCES => err
|
209
|
+
raise RepoAccessError, err.message
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
checkout
|
214
|
+
end
|
215
|
+
|
187
216
|
# checkout
|
188
217
|
#
|
189
218
|
# create a local working copy of a remote svn repo (creates dir if not
|
@@ -192,23 +221,15 @@ module SvnWc
|
|
192
221
|
#
|
193
222
|
|
194
223
|
def checkout
|
195
|
-
if not File.directory? @svn_repo_working_copy
|
196
|
-
begin
|
197
|
-
FileUtils.mkdir_p @svn_repo_working_copy
|
198
|
-
rescue Errno::EACCES => e
|
199
|
-
raise RepoAccessError, e.message
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
224
|
begin
|
204
|
-
svn_session()
|
205
|
-
ctx.checkout(@svn_repo_master, @svn_repo_working_copy)
|
206
|
-
|
225
|
+
svn_session() do |ctx|
|
226
|
+
ctx.checkout(@svn_repo_master, @svn_repo_working_copy)
|
227
|
+
end
|
207
228
|
#rescue Svn::Error::RaLocalReposOpenFailed,
|
208
229
|
# Svn::Error::FsAlreadyExists,
|
209
|
-
#
|
210
|
-
rescue Exception =>
|
211
|
-
raise RepoAccessError,
|
230
|
+
#rescue Errno::EACCES => e
|
231
|
+
rescue Exception => err
|
232
|
+
raise RepoAccessError, err.message
|
212
233
|
end
|
213
234
|
end
|
214
235
|
alias_method :co, :checkout
|
@@ -216,8 +237,7 @@ module SvnWc
|
|
216
237
|
#
|
217
238
|
# load conf file (yaml)
|
218
239
|
#
|
219
|
-
# takes a path to a yaml config file, loads values.
|
220
|
-
# nothing passed
|
240
|
+
# takes a path to a yaml config file, loads values.
|
221
241
|
# raises RepoAccessError if something goes wrong
|
222
242
|
#
|
223
243
|
# private
|
@@ -226,7 +246,7 @@ module SvnWc
|
|
226
246
|
def load_conf(cnf) # :nodoc:
|
227
247
|
|
228
248
|
if cnf.nil? or cnf.empty?
|
229
|
-
|
249
|
+
raise RepoAccessError, 'No config file provided!'
|
230
250
|
elsif cnf and cnf.class == String and File.exists? cnf
|
231
251
|
cnf = IO.read(cnf)
|
232
252
|
end
|
@@ -247,23 +267,29 @@ module SvnWc
|
|
247
267
|
#
|
248
268
|
# raises RepoAccessError if something goes wrong
|
249
269
|
#
|
270
|
+
#--
|
271
|
+
# "svn/client.rb" Svn::Client
|
272
|
+
# def add(path, recurse=true, force=false, no_ignore=false)
|
273
|
+
# Client.add3(path, recurse, force, no_ignore, self)
|
274
|
+
# end
|
275
|
+
#++
|
250
276
|
|
251
|
-
def add(files=[])
|
277
|
+
def add(files=[], recurse=true, force=false, no_ignore=false)
|
252
278
|
|
253
279
|
# TODO make sure args are what is expected for all methods
|
254
280
|
raise ArgumentError, 'files is empty' unless files
|
255
281
|
|
256
282
|
svn_session() do |svn|
|
257
283
|
begin
|
258
|
-
files.each
|
259
|
-
svn.add(ef,
|
260
|
-
|
284
|
+
files.each do |ef|
|
285
|
+
svn.add(ef, recurse, force, no_ignore)
|
286
|
+
end
|
261
287
|
#rescue Svn::Error::ENTRY_EXISTS,
|
262
288
|
# Svn::Error::AuthnNoProvider,
|
263
289
|
# #Svn::Error::WcNotDirectory,
|
264
290
|
# Svn::Error::SvnError => e
|
265
|
-
rescue Exception =>
|
266
|
-
raise RepoAccessError, "Add Failed: #{
|
291
|
+
rescue Exception => excp
|
292
|
+
raise RepoAccessError, "Add Failed: #{excp.message}"
|
267
293
|
end
|
268
294
|
end
|
269
295
|
end
|
@@ -277,7 +303,7 @@ module SvnWc
|
|
277
303
|
# raises RepoAccessError if something goes wrong
|
278
304
|
#
|
279
305
|
|
280
|
-
def delete(files=[], recurs=
|
306
|
+
def delete(files=[], recurs=false)
|
281
307
|
svn_session() do |svn|
|
282
308
|
begin
|
283
309
|
svn.delete(files)
|
@@ -285,8 +311,8 @@ module SvnWc
|
|
285
311
|
# #Svn::Error::WcNotDirectory,
|
286
312
|
# Svn::Error::ClientModified,
|
287
313
|
# Svn::Error::SvnError => e
|
288
|
-
rescue Exception =>
|
289
|
-
raise RepoAccessError, "Delete Failed: #{
|
314
|
+
rescue Exception => err
|
315
|
+
raise RepoAccessError, "Delete Failed: #{err.message}"
|
290
316
|
end
|
291
317
|
end
|
292
318
|
end
|
@@ -316,8 +342,8 @@ module SvnWc
|
|
316
342
|
# Svn::Error::IllegalTarget,
|
317
343
|
# #Svn::Error::EntryNotFound => e
|
318
344
|
# Exception => e
|
319
|
-
rescue Exception =>
|
320
|
-
raise RepoAccessError, "Commit Failed: #{
|
345
|
+
rescue Exception => err
|
346
|
+
raise RepoAccessError, "Commit Failed: #{err.message}"
|
321
347
|
end
|
322
348
|
end
|
323
349
|
rev
|
@@ -336,33 +362,17 @@ module SvnWc
|
|
336
362
|
# raises RepoAccessError if something goes wrong
|
337
363
|
#
|
338
364
|
# alias up
|
339
|
-
#--
|
340
|
-
# XXX refactor this (too long)
|
341
|
-
#++
|
342
365
|
def update(paths=[])
|
343
366
|
|
344
367
|
if paths.empty? then paths = self.svn_repo_working_copy end
|
345
368
|
#XXX update is a bummer, just returns the rev num, not affected files
|
346
369
|
#(svn command line up, also returns altered/new files - mimic that)
|
347
|
-
# hence our inplace hack
|
370
|
+
# hence our inplace hack (_pre/_post update_entries)
|
348
371
|
#
|
349
372
|
# unfortunetly, we cant use 'Repos', only works on local filesystem repo
|
350
373
|
# (NOT remote)
|
351
374
|
#p Svn::Repos.open(@svn_repo_master) # Svn::Repos.open('/tmp/svnrepo')
|
352
|
-
|
353
|
-
pre_up_entries = Array.new
|
354
|
-
modified_entries = Array.new
|
355
|
-
list_entries.each { |ent|
|
356
|
-
##puts "#{ent[:status]} | #{ent[:repo_rev]} | #{ent[:entry_name]}"
|
357
|
-
pre_up_entries.push ent[:entry_name]
|
358
|
-
## how does it handle deletes?
|
359
|
-
#if info()[:rev] != ent[:repo_rev]
|
360
|
-
# puts "changed file: #{File.join(paths, ent[:entry_name])} | #{ent[:status]} "
|
361
|
-
#end
|
362
|
-
if ent[:status] == 'M'
|
363
|
-
modified_entries.push "#{ent[:status]}\t#{ent[:entry_name]}"
|
364
|
-
end
|
365
|
-
}
|
375
|
+
_pre_update_entries
|
366
376
|
|
367
377
|
rev = String.new
|
368
378
|
svn_session() do |svn|
|
@@ -374,30 +384,60 @@ module SvnWc
|
|
374
384
|
# #Svn::Error::WcNotDirectory,
|
375
385
|
# #Svn::Error::EntryNotFound => e
|
376
386
|
# Exception => e
|
377
|
-
rescue Exception =>
|
378
|
-
raise RepoAccessError, "Update Failed: #{
|
387
|
+
rescue Exception => err
|
388
|
+
raise RepoAccessError, "Update Failed: #{err.message}"
|
379
389
|
end
|
380
390
|
end
|
381
391
|
|
392
|
+
_post_update_entries
|
393
|
+
|
394
|
+
return rev, @modified_entries
|
395
|
+
|
396
|
+
end
|
397
|
+
alias_method :up, :update
|
398
|
+
|
399
|
+
#
|
400
|
+
# get list of entries before doing an update
|
401
|
+
#
|
402
|
+
def _pre_update_entries #:nodoc:
|
403
|
+
@pre_up_entries = Array.new
|
404
|
+
@modified_entries = Array.new
|
405
|
+
list_entries.each do |ent|
|
406
|
+
##puts "#{ent[:status]} | #{ent[:repo_rev]} | #{ent[:entry_name]}"
|
407
|
+
e_name = ent[:entry_name]
|
408
|
+
stat = ent[:status]
|
409
|
+
@pre_up_entries.push e_name
|
410
|
+
## how does it handle deletes?
|
411
|
+
#if info()[:rev] != ent[:repo_rev]
|
412
|
+
# puts "changed file: #{File.join(paths, ent[:entry_name])} | #{ent[:status]} "
|
413
|
+
#end
|
414
|
+
if stat == 'M' then @modified_entries.push "#{stat}\t#{e_name}" end
|
415
|
+
end
|
416
|
+
end
|
417
|
+
private :_pre_update_entries
|
418
|
+
|
419
|
+
#
|
420
|
+
# get list of entries after doing an update
|
421
|
+
#
|
422
|
+
def _post_update_entries #:nodoc:
|
382
423
|
post_up_entries = Array.new
|
383
424
|
list_entries.each { |ent| post_up_entries.push ent[:entry_name] }
|
384
425
|
|
385
|
-
added
|
386
|
-
removed = pre_up_entries - post_up_entries
|
426
|
+
added = post_up_entries - @pre_up_entries
|
427
|
+
removed = @pre_up_entries - post_up_entries
|
387
428
|
|
388
|
-
if added.length > 0
|
389
|
-
added.each {|
|
429
|
+
if added.length > 0
|
430
|
+
added.each {|e_add| @modified_entries.push "A\t#{e_add}" }
|
390
431
|
end
|
391
432
|
|
392
433
|
if removed.length > 0
|
393
|
-
removed.each {|
|
434
|
+
removed.each {|e_rm| @modified_entries.push "D\t#{e_rm}" }
|
394
435
|
end
|
395
436
|
|
396
|
-
return rev, modified_entries
|
397
|
-
|
398
437
|
end
|
399
|
-
|
400
|
-
|
438
|
+
private :_post_update_entries
|
439
|
+
|
440
|
+
|
401
441
|
#
|
402
442
|
# get status on dir/file path.
|
403
443
|
#
|
@@ -469,21 +509,31 @@ module SvnWc
|
|
469
509
|
end
|
470
510
|
end
|
471
511
|
|
512
|
+
_file_list infos
|
513
|
+
|
514
|
+
end
|
515
|
+
private :do_status
|
516
|
+
|
517
|
+
|
518
|
+
#
|
519
|
+
# create and return list of anon hashes
|
520
|
+
# set hashes to contain :path and :status
|
521
|
+
#
|
522
|
+
def _file_list(info_list) # :nodoc:
|
472
523
|
files = Array.new
|
473
|
-
|
524
|
+
info_list.each {|r|
|
474
525
|
#p r.inspect
|
475
526
|
# file is not modified, we don't want to see it (this is 'status')
|
476
|
-
|
527
|
+
txt_stat = r[1].text_status
|
528
|
+
next if ' ' == status_codes(txt_stat)
|
477
529
|
f_rec = Hash.new
|
478
530
|
f_rec[:path] = r[0]
|
479
|
-
f_rec[:status] = status_codes(
|
531
|
+
f_rec[:status] = status_codes(txt_stat)
|
480
532
|
files.push f_rec
|
481
533
|
}
|
482
|
-
|
483
534
|
files
|
484
|
-
|
485
535
|
end
|
486
|
-
private :
|
536
|
+
private :_file_list
|
487
537
|
|
488
538
|
#
|
489
539
|
# list (ls)
|
@@ -562,24 +612,26 @@ module SvnWc
|
|
562
612
|
#
|
563
613
|
|
564
614
|
def list_entries(dir=self.svn_repo_working_copy, file=nil, verbose=false)
|
565
|
-
@entry_list = []
|
566
|
-
show = true
|
615
|
+
@entry_list, @show, @verbose = [], true, verbose
|
567
616
|
Svn::Wc::AdmAccess.open(nil, dir, false, 5) do |adm|
|
617
|
+
@adm = adm
|
568
618
|
if file.nil?
|
569
619
|
#also see walk_entries (in svn bindings) has callback
|
570
620
|
adm.read_entries.keys.sort.each { |ef|
|
571
621
|
next unless ef.length >= 1 # why this check and not file.exists?
|
572
622
|
f_path = File.join(dir, ef)
|
573
623
|
if File.file? f_path
|
574
|
-
_collect_get_entry_info(f_path
|
624
|
+
_collect_get_entry_info(f_path)
|
575
625
|
elsif File.directory? f_path
|
576
|
-
_walk_entries(f_path
|
626
|
+
_walk_entries(f_path)
|
577
627
|
end
|
578
628
|
}
|
579
629
|
else
|
580
|
-
_collect_get_entry_info(file
|
630
|
+
_collect_get_entry_info(file)
|
581
631
|
end
|
582
632
|
end
|
633
|
+
#XXX do we want nil or empty on no entries, choosing empty for now
|
634
|
+
#@entry_list unless @entry_list.empty?
|
583
635
|
@entry_list
|
584
636
|
end
|
585
637
|
|
@@ -589,11 +641,11 @@ module SvnWc
|
|
589
641
|
# given a dir, iterate each entry, getting detailed file entry info
|
590
642
|
#
|
591
643
|
|
592
|
-
def _walk_entries(f_path
|
644
|
+
def _walk_entries(f_path) #:nodoc:
|
593
645
|
Dir.entries(f_path).each do |de|
|
594
646
|
next if de == '..' or de == '.' or de == '.svn'
|
595
647
|
fp_path = File.join(f_path, de)
|
596
|
-
_collect_get_entry_info(fp_path
|
648
|
+
_collect_get_entry_info(fp_path)
|
597
649
|
end
|
598
650
|
end
|
599
651
|
private :_walk_entries
|
@@ -608,10 +660,10 @@ module SvnWc
|
|
608
660
|
# requested
|
609
661
|
#
|
610
662
|
|
611
|
-
def _collect_get_entry_info(abs_path_file
|
612
|
-
@status_info =
|
613
|
-
_get_entry_info(abs_path_file
|
614
|
-
@entry_list.push @status_info
|
663
|
+
def _collect_get_entry_info(abs_path_file) #:nodoc:
|
664
|
+
@status_info = Hash.new
|
665
|
+
_get_entry_info(abs_path_file)
|
666
|
+
@entry_list.push @status_info unless @status_info.empty?
|
615
667
|
end
|
616
668
|
private :_collect_get_entry_info
|
617
669
|
|
@@ -628,63 +680,65 @@ module SvnWc
|
|
628
680
|
# TODO - document all the params available from this command
|
629
681
|
#++
|
630
682
|
|
631
|
-
def _get_entry_info(abs_path_file
|
683
|
+
def _get_entry_info(abs_path_file) # :nodoc:
|
632
684
|
wc = self.svn_repo_working_copy
|
633
685
|
entry_repo_location = abs_path_file[(wc.length+1)..-1]
|
634
686
|
|
635
|
-
entry = Svn::Wc::Entry.new(abs_path_file, adm, show)
|
636
|
-
|
687
|
+
entry = Svn::Wc::Entry.new(abs_path_file, @adm, @show)
|
688
|
+
#@status_info[:entry_name] = entry_repo_location
|
637
689
|
|
638
|
-
status = adm.status(abs_path_file)
|
690
|
+
status = @adm.status(abs_path_file)
|
639
691
|
return if status.entry.nil?
|
640
692
|
|
641
|
-
@status_info[:
|
642
|
-
@status_info[:
|
643
|
-
@status_info[:
|
693
|
+
@status_info[:entry_name] = entry_repo_location
|
694
|
+
@status_info[:status] = status_codes(status.text_status)
|
695
|
+
@status_info[:repo_rev] = status.entry.revision
|
696
|
+
@status_info[:kind] = status.entry.kind
|
644
697
|
|
645
698
|
if @status_info[:kind] == 2
|
646
699
|
# remove the repo root abs path, give dirs relative to repo root
|
647
700
|
@status_info[:dir_name] = entry_repo_location
|
648
701
|
# XXX hmmm, this is a little like a goto, revisit this
|
649
|
-
_walk_entries(abs_path_file
|
702
|
+
_walk_entries(abs_path_file)
|
650
703
|
end
|
651
|
-
return if verbose == false
|
704
|
+
return if @verbose == false
|
652
705
|
# only on demand ; i.e. verbose = true
|
653
|
-
|
654
|
-
@status_info[:entry_conflict]
|
655
|
-
@status_info[:
|
656
|
-
@status_info[:
|
657
|
-
@status_info[:
|
658
|
-
@status_info[:
|
659
|
-
@status_info[:
|
660
|
-
@status_info[:
|
661
|
-
@status_info[:
|
662
|
-
@status_info[:
|
663
|
-
@status_info[:
|
664
|
-
@status_info[:
|
665
|
-
@status_info[:
|
666
|
-
@status_info[:
|
667
|
-
@status_info[:
|
668
|
-
@status_info[:
|
669
|
-
@status_info[:
|
670
|
-
@status_info[:
|
671
|
-
@status_info[:
|
672
|
-
@status_info[:
|
673
|
-
@status_info[:
|
674
|
-
@status_info[:
|
675
|
-
@status_info[:
|
676
|
-
@status_info[:
|
677
|
-
@status_info[:
|
678
|
-
@status_info[:
|
679
|
-
@status_info[:
|
680
|
-
@status_info[:
|
681
|
-
@status_info[:
|
682
|
-
@status_info[:
|
683
|
-
@status_info[:
|
684
|
-
@status_info[:
|
685
|
-
@status_info[:
|
686
|
-
@status_info[:
|
687
|
-
@status_info[:
|
706
|
+
s = status.entry
|
707
|
+
@status_info[:entry_conflict] = entry.conflicted?(abs_path_file)
|
708
|
+
@status_info[:lock_creation_date] = s.lock_creation_date
|
709
|
+
@status_info[:present_props] = s.present_props
|
710
|
+
@status_info[:has_prop_mods] = s.has_prop_mods
|
711
|
+
@status_info[:copyfrom_url] = s.copyfrom_url
|
712
|
+
@status_info[:conflict_old] = s.conflict_old
|
713
|
+
@status_info[:conflict_new] = s.conflict_new
|
714
|
+
@status_info[:lock_comment] = s.lock_comment
|
715
|
+
@status_info[:copyfrom_rev] = s.copyfrom_rev
|
716
|
+
@status_info[:working_size] = s.working_size
|
717
|
+
@status_info[:conflict_wrk] = s.conflict_wrk
|
718
|
+
@status_info[:cmt_author] = s.cmt_author
|
719
|
+
@status_info[:changelist] = s.changelist
|
720
|
+
@status_info[:lock_token] = s.lock_token
|
721
|
+
@status_info[:keep_local] = s.keep_local
|
722
|
+
@status_info[:lock_owner] = s.lock_owner
|
723
|
+
@status_info[:prop_time] = s.prop_time
|
724
|
+
@status_info[:has_props] = s.has_props
|
725
|
+
@status_info[:schedule] = s.schedule
|
726
|
+
@status_info[:text_time] = s.text_time
|
727
|
+
@status_info[:revision] = s.revision
|
728
|
+
@status_info[:checksum] = s.checksum
|
729
|
+
@status_info[:cmt_date] = s.cmt_date
|
730
|
+
@status_info[:prejfile] = s.prejfile
|
731
|
+
@status_info[:is_file] = s.file?
|
732
|
+
@status_info[:normal?] = s.normal?
|
733
|
+
@status_info[:cmt_rev] = s.cmt_rev
|
734
|
+
@status_info[:deleted] = s.deleted
|
735
|
+
@status_info[:absent] = s.absent
|
736
|
+
@status_info[:is_add] = s.add?
|
737
|
+
@status_info[:is_dir] = s.dir?
|
738
|
+
@status_info[:repos] = s.repos
|
739
|
+
@status_info[:depth] = s.depth
|
740
|
+
@status_info[:uuid] = s.uuid
|
741
|
+
@status_info[:url] = s.url
|
688
742
|
end
|
689
743
|
private :_get_entry_info
|
690
744
|
|
@@ -701,7 +755,7 @@ module SvnWc
|
|
701
755
|
wc_path = self.svn_repo_working_copy
|
702
756
|
end
|
703
757
|
|
704
|
-
r_info =
|
758
|
+
r_info = Hash.new
|
705
759
|
begin
|
706
760
|
@ctx.info(wc_path) do |path, type|
|
707
761
|
r_info[:last_changed_author] = type.last_changed_author
|
@@ -718,20 +772,14 @@ module SvnWc
|
|
718
772
|
r_info[:conflict_new] = type.conflict_new
|
719
773
|
r_info[:has_wc_info] = type.has_wc_info
|
720
774
|
r_info[:repos_UUID] = type.repos_UUID
|
721
|
-
#r_info[:changelist] = type.changelist
|
722
|
-
r_info[:prop_time] = type.prop_time
|
723
|
-
r_info[:text_time] = type.text_time
|
724
775
|
r_info[:checksum] = type.checksum
|
725
|
-
r_info[:
|
726
|
-
r_info[:schedule]
|
727
|
-
r_info[:taguri]
|
728
|
-
|
729
|
-
r_info[:
|
730
|
-
#r_info[:
|
731
|
-
r_info[:
|
732
|
-
r_info[:dup] = type.dup
|
733
|
-
r_info[:URL] = type.URL
|
734
|
-
r_info[:rev] = type.rev
|
776
|
+
r_info[:prop_time], r_info[:text_time] = type.prop_time, type.text_time
|
777
|
+
r_info[:prejfile], r_info[:schedule] = type.prejfile, type.schedule
|
778
|
+
r_info[:taguri], r_info[:lock] = type.taguri, type.lock
|
779
|
+
r_info[:rev], r_info[:dup] = type.rev, type.dup
|
780
|
+
r_info[:url], r_info[:URL] = type.url, type.URL
|
781
|
+
#r_info[:changelist] = type.changelist
|
782
|
+
#r_info[:depth], r_info[:size] = type.depth, type.size
|
735
783
|
end
|
736
784
|
#rescue Svn::Error::WcNotDirectory => e
|
737
785
|
# #Svn::Error::RaIllegalUrl,
|
@@ -814,6 +862,23 @@ module SvnWc
|
|
814
862
|
out_file.readlines
|
815
863
|
end
|
816
864
|
|
865
|
+
# currently supports type='ignore' only
|
866
|
+
#--
|
867
|
+
# TODO support other propset's ; also propget
|
868
|
+
#++
|
869
|
+
def propset(type, file, dir_path)
|
870
|
+
raise RepoAccessError, '"ignore" is only supported propset' \
|
871
|
+
unless type == 'ignore'
|
872
|
+
|
873
|
+
svn_session() do |svn|
|
874
|
+
begin
|
875
|
+
svn.propset(Svn::Core::PROP_IGNORE, file, dir_path)
|
876
|
+
rescue Exception => e #Svn::Error::EntryNotFound => e
|
877
|
+
raise RepoAccessError, "Propset (Ignore) Failed: #{e.message}"
|
878
|
+
end
|
879
|
+
end
|
880
|
+
end
|
881
|
+
|
817
882
|
# svn session set up
|
818
883
|
#--
|
819
884
|
# from
|
@@ -858,14 +923,3 @@ module SvnWc
|
|
858
923
|
end
|
859
924
|
|
860
925
|
end
|
861
|
-
|
862
|
-
if __FILE__ == $0
|
863
|
-
|
864
|
-
svn = SvnWc::RepoAccess.new
|
865
|
-
p svn
|
866
|
-
#n = '/tmp/NEW'
|
867
|
-
#svn.add n
|
868
|
-
#svn.commit n
|
869
|
-
|
870
|
-
end
|
871
|
-
|
data/test/svn_wc_test.rb
CHANGED
@@ -11,9 +11,6 @@
|
|
11
11
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
12
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
13
|
# Lesser General Public License for more details.
|
14
|
-
#$LOAD_PATH.unshift '..'
|
15
|
-
#$LOAD_PATH.unshift File.join('..', 'lib')
|
16
|
-
#$LOAD_PATH.unshift File.join('lib')
|
17
14
|
|
18
15
|
require 'yaml'
|
19
16
|
require File.join(File.dirname(__FILE__), "..", "lib", 'svn_wc')
|
@@ -34,6 +31,8 @@ require 'pp'
|
|
34
31
|
# svn status
|
35
32
|
# svn delete
|
36
33
|
# svn diff
|
34
|
+
# propset ignore
|
35
|
+
# log
|
37
36
|
|
38
37
|
# NOTE
|
39
38
|
# svn+ssh is our primary use case, however
|
@@ -409,27 +408,33 @@ class TestSvnWc < Test::Unit::TestCase
|
|
409
408
|
assert_equal Array.new, svn.diff(f)
|
410
409
|
end
|
411
410
|
|
412
|
-
## TODO
|
411
|
+
## TODO not sure if we actually want this
|
413
412
|
#def test_add_does_recursive_nested_dirs
|
414
|
-
# svn = SvnWc::RepoAccess.new(
|
415
|
-
|
416
|
-
#
|
417
|
-
#
|
418
|
-
# FileUtils.mkdir_p @conf['svn_repo_working_copy'] + "/d1/d2/d3"
|
419
|
-
# nested = @conf['svn_repo_working_copy'] +
|
420
|
-
# "/d1/d2/d3/test_#{Time.now.usec.to_s}.txt"
|
413
|
+
# svn = SvnWc::RepoAccess.new(YAML::dump(@conf), true, true)
|
414
|
+
# repo_wc = @conf['svn_repo_working_copy']
|
415
|
+
# FileUtils.mkdir_p File.join(repo_wc, 'd1','d2','d3')
|
416
|
+
# nested = File.join(repo_wc, 'd1','d2','d3',"test_#{Time.now.usec.to_s}.txt")
|
421
417
|
# FileUtils.touch nested
|
418
|
+
# # TODO ability to add recursive nested dirs
|
422
419
|
# svn.add nested
|
423
|
-
|
424
|
-
#
|
425
|
-
#
|
426
|
-
#
|
427
|
-
#
|
428
|
-
# }
|
429
|
-
#
|
430
|
-
#
|
431
|
-
|
432
|
-
#
|
420
|
+
|
421
|
+
# ## add 1 new file in nested heirerarcy
|
422
|
+
# ## TODO ability to add recursive nested dirs
|
423
|
+
# #FileUtils.mkdir_p @conf['svn_repo_working_copy'] + "/d1/d2/d3"
|
424
|
+
# #nested = @conf['svn_repo_working_copy'] +
|
425
|
+
# # "/d1/d2/d3/test_#{Time.now.usec.to_s}.txt"
|
426
|
+
# #FileUtils.touch nested
|
427
|
+
# #svn.add nested
|
428
|
+
|
429
|
+
# #svn.status.each { |ef|
|
430
|
+
# # next unless ef[:entry_name].match /test_.*/
|
431
|
+
# # assert_equal 'A', ef[:status]
|
432
|
+
# # assert_equal nested, File.join(@conf['svn_repo_working_copy'], ef[:entry_name])
|
433
|
+
# #}
|
434
|
+
# #svn.revert
|
435
|
+
# #assert_equal 1, svn.status.length
|
436
|
+
# #assert_equal File.basename(@conf['svn_repo_working_copy']),
|
437
|
+
# # svn.status[0][:entry_name]
|
433
438
|
#end
|
434
439
|
|
435
440
|
def test_update_acts_on_whole_repo_by_default_knows_a_m_d
|
@@ -474,25 +479,20 @@ class TestSvnWc < Test::Unit::TestCase
|
|
474
479
|
|
475
480
|
end
|
476
481
|
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
482
|
+
def test_add_exception_on_already_added_file
|
483
|
+
svn = SvnWc::RepoAccess.new(YAML::dump(@conf), true, true)
|
484
|
+
this_wc_rev = 0
|
485
|
+
assert_equal [this_wc_rev, []], svn.update
|
481
486
|
|
482
|
-
|
487
|
+
rev, f_name = check_out_new_working_copy_add_and_commit_new_entries
|
483
488
|
|
484
|
-
|
489
|
+
assert_equal [rev, ["A\t#{File.basename(f_name.to_s)}"]], svn.update
|
485
490
|
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
# start_rev = svn.commit f
|
492
|
-
# #p svn.status(f)
|
493
|
-
# #assert_equal 'M', svn.status(f)[0][:status]
|
494
|
-
# #assert_equal start_rev, svn.info(f)[:rev]
|
495
|
-
#end
|
491
|
+
(rev, f) = modify_file_and_commit_into_another_working_repo
|
492
|
+
File.open(f, 'w') {|fl| fl.write('adding text to file.')}
|
493
|
+
#already under version control
|
494
|
+
assert_raise(SvnWc::RepoAccessError) { svn.add f }
|
495
|
+
end
|
496
496
|
|
497
497
|
def test_list_recursive
|
498
498
|
FileUtils.rm_rf @conf['svn_repo_working_copy']
|
@@ -603,19 +603,125 @@ class TestSvnWc < Test::Unit::TestCase
|
|
603
603
|
assert fails
|
604
604
|
end
|
605
605
|
|
606
|
-
|
607
|
-
|
608
|
-
#for this one, we will have to create another working copy, modify a file
|
609
|
-
# and commit from there, then to an update in this working copy
|
610
|
-
#end
|
606
|
+
def test_list_entries_verbose
|
607
|
+
svn = SvnWc::RepoAccess.new(YAML::dump(@conf), true, true)
|
611
608
|
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
609
|
+
repo_wc = @conf['svn_repo_working_copy']
|
610
|
+
|
611
|
+
dir_ent = Dir.mktmpdir('P', repo_wc)
|
612
|
+
file = 'file.txt'
|
613
|
+
file = File.join dir_ent, file
|
614
|
+
File.open(file, "w") {|f| f.print('contents.')}
|
615
|
+
file2 = new_unique_file_at_path dir_ent
|
616
|
+
|
617
|
+
svn.add dir_ent, recurse=true, force=true
|
618
|
+
rev = svn.commit repo_wc
|
619
|
+
|
620
|
+
entries = svn.list_entries
|
621
|
+
|
622
|
+
assert_equal File.basename(entries[0][:entry_name]),
|
623
|
+
File.basename(file)
|
624
|
+
assert_equal File.basename(entries[1][:entry_name]),
|
625
|
+
File.basename(file2)
|
626
|
+
assert_equal entries.size, 2
|
627
|
+
assert_nil entries[2]
|
628
|
+
# verbose info from list_entries
|
629
|
+
#p svn.list_entries(repo_wc, file, verbose=true)
|
630
|
+
f_entry = File.join(File.basename(dir_ent), File.basename(file))
|
631
|
+
svn.list_entries(repo_wc, file, verbose=true).each do |info|
|
632
|
+
# bools
|
633
|
+
assert ! info[:absent]
|
634
|
+
assert ! info[:entry_conflict]
|
635
|
+
assert ! info[:is_add]
|
636
|
+
assert ! info[:is_dir]
|
637
|
+
assert ! info[:has_props]
|
638
|
+
assert ! info[:deleted]
|
639
|
+
assert ! info[:keep_local]
|
640
|
+
assert ! info[:has_prop_mods]
|
641
|
+
assert info[:normal?]
|
642
|
+
assert info[:is_file]
|
643
|
+
|
644
|
+
assert_nil info[:copyfrom_url]
|
645
|
+
assert_nil info[:conflict_old]
|
646
|
+
assert_nil info[:conflict_new]
|
647
|
+
assert_nil info[:conflict_wrk]
|
648
|
+
assert_nil info[:lock_comment]
|
649
|
+
assert_nil info[:lock_owner]
|
650
|
+
assert_nil info[:present_props]
|
651
|
+
assert_nil info[:lock_token]
|
652
|
+
assert_nil info[:changelist]
|
653
|
+
assert_nil info[:prejfile]
|
654
|
+
|
655
|
+
#assert_equal info[:cmt_author], `id`
|
656
|
+
assert_equal info[:revision], rev
|
657
|
+
assert_equal info[:repo_rev], rev
|
658
|
+
assert_equal info[:cmt_rev] , rev
|
659
|
+
#assert_equal info[:cmt_date] , rev
|
660
|
+
#:checksum=>"bb9c00f6fc03c2213ac1f0278853dc32", :working_size=>9,
|
661
|
+
assert_equal info[:working_size] , 9
|
662
|
+
assert_equal info[:schedule], 0
|
663
|
+
assert_equal info[:lock_creation_date], 0
|
664
|
+
assert_equal info[:copyfrom_rev], -1
|
665
|
+
assert_equal info[:prop_time], 0
|
666
|
+
assert_equal info[:kind], 1 # i.e. is file
|
667
|
+
assert_equal info[:depth], 3
|
668
|
+
assert_equal info[:status] , ' '
|
669
|
+
assert_equal info[:entry_name], f_entry
|
670
|
+
assert_equal info[:url], "#{File.join(@conf['svn_repo_master'],f_entry)}"
|
671
|
+
assert_equal info[:repos], @conf['svn_repo_master']
|
672
|
+
end
|
673
|
+
|
674
|
+
end
|
675
|
+
|
676
|
+
def test_propset_ignore_file
|
677
|
+
svn = SvnWc::RepoAccess.new(YAML::dump(@conf), true, true)
|
678
|
+
|
679
|
+
repo_wc = @conf['svn_repo_working_copy']
|
680
|
+
|
681
|
+
dir_ent = Dir.mktmpdir('P', repo_wc)
|
682
|
+
file = 'file_to_ignore.txt'
|
683
|
+
|
684
|
+
svn.add dir_ent, recurse=false
|
685
|
+
svn.propset('ignore', file, dir_ent)
|
686
|
+
svn.commit repo_wc
|
687
|
+
|
688
|
+
file = File.join dir_ent, file
|
689
|
+
# create file we have already ignored, above
|
690
|
+
File.open(file, "w") {|f| f.print('testing propset ignore file.')}
|
691
|
+
|
692
|
+
file2 = new_unique_file_at_path dir_ent
|
693
|
+
|
694
|
+
svn.add dir_ent, recurse=true, force=true
|
695
|
+
svn.commit repo_wc
|
696
|
+
|
697
|
+
assert_raise(SvnWc::RepoAccessError) { svn.info file }
|
698
|
+
assert_equal File.basename(svn.list_entries[0][:entry_name]),
|
699
|
+
File.basename(file2)
|
700
|
+
assert_equal svn.list_entries.size, 1
|
701
|
+
assert_nil svn.list_entries[1]
|
702
|
+
|
703
|
+
end
|
704
|
+
|
705
|
+
# TODO
|
706
|
+
# from client.rb
|
707
|
+
#def log(paths, start_rev, end_rev, limit,
|
708
|
+
# discover_changed_paths, strict_node_history,
|
709
|
+
# peg_rev=nil)
|
710
|
+
def test_commit_with_message
|
711
|
+
log_mess = 'added new file'
|
712
|
+
|
713
|
+
svn = SvnWc::RepoAccess.new(YAML::dump(@conf), true, true)
|
714
|
+
file = new_unique_file_at_path
|
715
|
+
svn.add file
|
716
|
+
o_rev = svn.commit file, log_mess
|
717
|
+
assert o_rev >= 1
|
718
|
+
args = [file, 0, "HEAD", 0, true, nil]
|
719
|
+
svn.log(*args) do |changed_paths, rev, author, date, message|
|
720
|
+
assert_equal rev, o_rev
|
721
|
+
assert_equal message, log_mess
|
722
|
+
end
|
723
|
+
end
|
616
724
|
|
617
|
-
#def test_delete
|
618
|
-
#end
|
619
725
|
|
620
726
|
#
|
621
727
|
# methods used by the tests below here
|
@@ -662,14 +768,14 @@ class TestSvnWc < Test::Unit::TestCase
|
|
662
768
|
return rev, ff
|
663
769
|
end
|
664
770
|
|
665
|
-
def modify_file_and_commit_into_another_working_repo
|
666
|
-
raise ArgumentError, "path arg is empty" unless f and not f.empty?
|
771
|
+
def modify_file_and_commit_into_another_working_repo
|
667
772
|
svn = _working_copy_repo_at_path
|
668
|
-
|
773
|
+
f = new_unique_file_at_path(svn.svn_repo_working_copy)
|
774
|
+
File.open(f, 'w') {|fl| fl.write('this is the original content of file.')}
|
775
|
+
svn.add f
|
669
776
|
rev = svn.commit f
|
670
|
-
raise 'cant get status' unless ' ' == svn.status(f)[0][:status]
|
671
777
|
raise 'cant get revision' unless rev == svn.info(f)[:rev]
|
672
|
-
rev
|
778
|
+
return rev, f
|
673
779
|
end
|
674
780
|
|
675
781
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: svn_wc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Wright
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-02-
|
12
|
+
date: 2010-02-20 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|