markryall-basketcase 1.1.9 → 1.1.10
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.
- data/.gitignore +1 -0
- data/Rakefile +8 -1
- data/VERSION +1 -1
- data/basketcase.gemspec +15 -3
- data/lib/basketcase.rb +31 -312
- data/lib/basketcase/command.rb +120 -0
- data/lib/basketcase/help_command.rb +27 -0
- data/lib/basketcase/ls_command.rb +63 -0
- data/lib/basketcase/update_command.rb +81 -0
- data/lib/basketcase/utils.rb +13 -0
- data/spec/auto_sync_spec.rb +6 -13
- data/spec/autocommit_steps.rb +20 -0
- data/spec/modify_files_spec.rb +85 -0
- data/spec/spec_helper.rb +9 -0
- metadata +12 -3
data/.gitignore
CHANGED
data/Rakefile
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'rubygems'
|
|
2
|
+
require 'spec/rake/spectask'
|
|
2
3
|
|
|
3
4
|
=begin
|
|
4
5
|
require 'hoe'
|
|
@@ -17,10 +18,16 @@ begin
|
|
|
17
18
|
gemspec.description = "basketcase fork"
|
|
18
19
|
gemspec.email = "mdub@dogbiscuit.org"
|
|
19
20
|
gemspec.homepage = "http://github.com/markryall/basketcase"
|
|
20
|
-
gemspec.description = "TODO"
|
|
21
21
|
gemspec.authors = ["mdub", 'mark ryall', 'duana stanley']
|
|
22
22
|
end
|
|
23
23
|
rescue LoadError
|
|
24
24
|
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
desc "Run all examples"
|
|
28
|
+
Spec::Rake::SpecTask.new('spec') do |t|
|
|
29
|
+
|
|
30
|
+
#t.spec_files = FileList['spec//.rb']
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.1.
|
|
1
|
+
1.1.10
|
data/basketcase.gemspec
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
+
# Generated by jeweler
|
|
2
|
+
# DO NOT EDIT THIS FILE
|
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
|
1
4
|
# -*- encoding: utf-8 -*-
|
|
2
5
|
|
|
3
6
|
Gem::Specification.new do |s|
|
|
4
7
|
s.name = %q{basketcase}
|
|
5
|
-
s.version = "1.1.
|
|
8
|
+
s.version = "1.1.10"
|
|
6
9
|
|
|
7
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
8
11
|
s.authors = ["mdub", "mark ryall", "duana stanley"]
|
|
9
|
-
s.date = %q{2009-
|
|
10
|
-
s.description = %q{
|
|
12
|
+
s.date = %q{2009-09-07}
|
|
13
|
+
s.description = %q{basketcase fork}
|
|
11
14
|
s.email = %q{mdub@dogbiscuit.org}
|
|
12
15
|
s.executables = ["basketcase", "bc-mirror"]
|
|
13
16
|
s.extra_rdoc_files = [
|
|
@@ -25,9 +28,16 @@ Gem::Specification.new do |s|
|
|
|
25
28
|
"bin/bc-mirror",
|
|
26
29
|
"lib/array_patching.rb",
|
|
27
30
|
"lib/basketcase.rb",
|
|
31
|
+
"lib/basketcase/command.rb",
|
|
32
|
+
"lib/basketcase/help_command.rb",
|
|
33
|
+
"lib/basketcase/ls_command.rb",
|
|
34
|
+
"lib/basketcase/update_command.rb",
|
|
35
|
+
"lib/basketcase/utils.rb",
|
|
28
36
|
"spec/auto_sync_spec.rb",
|
|
37
|
+
"spec/autocommit_steps.rb",
|
|
29
38
|
"spec/basketcase_spec.rb",
|
|
30
39
|
"spec/cleartool",
|
|
40
|
+
"spec/modify_files_spec.rb",
|
|
31
41
|
"spec/spec_helper.rb"
|
|
32
42
|
]
|
|
33
43
|
s.homepage = %q{http://github.com/markryall/basketcase}
|
|
@@ -37,7 +47,9 @@ Gem::Specification.new do |s|
|
|
|
37
47
|
s.summary = %q{clearcase for the masses}
|
|
38
48
|
s.test_files = [
|
|
39
49
|
"spec/auto_sync_spec.rb",
|
|
50
|
+
"spec/autocommit_steps.rb",
|
|
40
51
|
"spec/basketcase_spec.rb",
|
|
52
|
+
"spec/modify_files_spec.rb",
|
|
41
53
|
"spec/spec_helper.rb"
|
|
42
54
|
]
|
|
43
55
|
|
data/lib/basketcase.rb
CHANGED
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
require 'pathname'
|
|
2
2
|
require 'forwardable'
|
|
3
3
|
require 'array_patching'
|
|
4
|
+
require 'basketcase/utils'
|
|
5
|
+
require 'find'
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
#load commands
|
|
8
|
+
Find.find(File.dirname(__FILE__)+'/basketcase') do |path|
|
|
9
|
+
load path if path =~ /_command\.rb$/
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
module Utils
|
|
13
|
+
def mkpath(path)
|
|
14
|
+
path = path.to_str
|
|
15
|
+
path = path.tr('\\', '/')
|
|
16
|
+
path = path.sub(%r{^\./},'')
|
|
17
|
+
path = path.sub(%r{^([A-Za-z]):\/}, '/cygdrive/\1/')
|
|
18
|
+
Pathname.new(path)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
6
21
|
|
|
22
|
+
class Basketcase
|
|
7
23
|
VERSION = '1.1.0'
|
|
8
24
|
|
|
9
25
|
@usage = <<EOF
|
|
@@ -29,18 +45,6 @@ EOF
|
|
|
29
45
|
@test_mode
|
|
30
46
|
end
|
|
31
47
|
|
|
32
|
-
module Utils
|
|
33
|
-
|
|
34
|
-
def mkpath(path)
|
|
35
|
-
path = path.to_str
|
|
36
|
-
path = path.tr('\\', '/')
|
|
37
|
-
path = path.sub(%r{^\./},'')
|
|
38
|
-
path = path.sub(%r{^([A-Za-z]):\/}, '/cygdrive/\1/')
|
|
39
|
-
Pathname.new(path)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
end
|
|
43
|
-
|
|
44
48
|
include Utils
|
|
45
49
|
|
|
46
50
|
def ignored?(path)
|
|
@@ -98,25 +102,6 @@ EOF
|
|
|
98
102
|
|
|
99
103
|
public
|
|
100
104
|
|
|
101
|
-
# Represents the status of an element
|
|
102
|
-
class ElementStatus
|
|
103
|
-
|
|
104
|
-
def initialize(path, status, base_version = nil)
|
|
105
|
-
@path = path
|
|
106
|
-
@status = status
|
|
107
|
-
@base_version = base_version
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
attr_reader :path, :status, :base_version
|
|
111
|
-
|
|
112
|
-
def to_s
|
|
113
|
-
s = "#{path} (#{status})"
|
|
114
|
-
s += " [#{base_version}]" if base_version
|
|
115
|
-
return s
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
end
|
|
119
|
-
|
|
120
105
|
# Object responsible for nice fomatting of output
|
|
121
106
|
DefaultListener = lambda do |element|
|
|
122
107
|
printf("%-7s %-15s %s\n", element.status,
|
|
@@ -159,211 +144,21 @@ EOF
|
|
|
159
144
|
class UsageException < Exception
|
|
160
145
|
end
|
|
161
146
|
|
|
162
|
-
#
|
|
163
|
-
class
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
def_delegators :@basketcase, :log_debug, :just_testing?, :ignored?, :make_command, :run
|
|
169
|
-
|
|
170
|
-
def synopsis
|
|
171
|
-
""
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
def help
|
|
175
|
-
"Sorry, no help provided ..."
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
def initialize(basketcase)
|
|
179
|
-
@basketcase = basketcase
|
|
180
|
-
@listener = DefaultListener
|
|
181
|
-
@recursive = false
|
|
182
|
-
@graphical = false
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
attr_writer :listener
|
|
186
|
-
attr_writer :targets
|
|
187
|
-
|
|
188
|
-
def report(status, path, version = nil)
|
|
189
|
-
@listener.call(ElementStatus.new(path, status, version))
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
def option_recurse
|
|
193
|
-
@recursive = true
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
alias :option_r :option_recurse
|
|
197
|
-
|
|
198
|
-
def option_graphical
|
|
199
|
-
@graphical = true
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
alias :option_g :option_graphical
|
|
203
|
-
|
|
204
|
-
def option_comment(comment)
|
|
205
|
-
@comment = comment
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
alias :option_m :option_comment
|
|
209
|
-
|
|
210
|
-
attr_accessor :comment
|
|
211
|
-
|
|
212
|
-
# Handle command-line arguments:
|
|
213
|
-
# - For option arguments of the form "-X", call the corresponding
|
|
214
|
-
# option_X() method.
|
|
215
|
-
# - Remaining arguments are stored in @targets
|
|
216
|
-
def accept_args(args)
|
|
217
|
-
while /^-+(.+)/ === args[0]
|
|
218
|
-
option = args.shift
|
|
219
|
-
option_method_name = "option_#{$1}"
|
|
220
|
-
unless respond_to?(option_method_name)
|
|
221
|
-
raise UsageException, "Unrecognised option: #{option}"
|
|
222
|
-
end
|
|
223
|
-
option_method = method(option_method_name)
|
|
224
|
-
parameters = []
|
|
225
|
-
option_method.arity.times { parameters << args.shift }
|
|
226
|
-
option_method.call(*parameters)
|
|
227
|
-
end
|
|
228
|
-
@targets = args
|
|
229
|
-
self
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
def effective_targets
|
|
233
|
-
TargetList.new(@targets.empty? ? ['.'] : @targets)
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
def specified_targets
|
|
237
|
-
raise UsageException, "No target specified" if @targets.empty?
|
|
238
|
-
TargetList.new(@targets)
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
private
|
|
242
|
-
|
|
243
|
-
def cleartool(command)
|
|
244
|
-
log_debug "RUNNING: cleartool #{command}"
|
|
245
|
-
IO.popen("cleartool " + command).each_line do |line|
|
|
246
|
-
line.sub!("\r", '')
|
|
247
|
-
log_debug "<<< " + line
|
|
248
|
-
yield(line) if block_given?
|
|
249
|
-
end
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
def cleartool_unsafe(command, &block)
|
|
253
|
-
if just_testing?
|
|
254
|
-
puts "WOULD RUN: cleartool #{command}"
|
|
255
|
-
return
|
|
256
|
-
end
|
|
257
|
-
cleartool(command, &block)
|
|
258
|
-
end
|
|
259
|
-
|
|
260
|
-
def view_root
|
|
261
|
-
@root ||= catch(:root) do
|
|
262
|
-
cleartool("pwv -root") do |line|
|
|
263
|
-
throw :root, mkpath(line.chomp)
|
|
264
|
-
end
|
|
265
|
-
end
|
|
266
|
-
log_debug "view_root = #{@root}"
|
|
267
|
-
@root
|
|
268
|
-
end
|
|
269
|
-
|
|
270
|
-
def cannot_deal_with(line)
|
|
271
|
-
$stderr.puts "unrecognised output: " + line
|
|
272
|
-
end
|
|
273
|
-
|
|
274
|
-
def edit(file)
|
|
275
|
-
editor = ENV["EDITOR"] || "notepad"
|
|
276
|
-
system("#{editor} #{file}")
|
|
277
|
-
end
|
|
278
|
-
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
class HelpCommand < Command
|
|
282
|
-
|
|
283
|
-
def synopsis
|
|
284
|
-
"[<command>]"
|
|
285
|
-
end
|
|
286
|
-
|
|
287
|
-
def help
|
|
288
|
-
"Display usage instructions."
|
|
289
|
-
end
|
|
290
|
-
|
|
291
|
-
def execute
|
|
292
|
-
if @targets.empty?
|
|
293
|
-
puts @basketcase.usage
|
|
294
|
-
exit
|
|
295
|
-
end
|
|
296
|
-
@targets.each do |command_name|
|
|
297
|
-
command = make_command(command_name)
|
|
298
|
-
puts
|
|
299
|
-
puts "% basketcase #{command_name} #{command.synopsis}"
|
|
300
|
-
puts
|
|
301
|
-
puts command.help.gsub(/^/, " ")
|
|
302
|
-
end
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
end
|
|
306
|
-
|
|
307
|
-
class LsCommand < Command
|
|
308
|
-
|
|
309
|
-
def synopsis
|
|
310
|
-
"[<element> ...]"
|
|
311
|
-
end
|
|
312
|
-
|
|
313
|
-
def help
|
|
314
|
-
<<EOF
|
|
315
|
-
List element status.
|
|
316
|
-
|
|
317
|
-
-a(ll) Show all files.
|
|
318
|
-
(by default, up-to-date files are not reported)
|
|
319
|
-
|
|
320
|
-
-r(ecurse) Recursively list sub-directories.
|
|
321
|
-
(by default, just lists current directory)
|
|
322
|
-
EOF
|
|
323
|
-
end
|
|
324
|
-
|
|
325
|
-
def option_all
|
|
326
|
-
@include_all = true
|
|
327
|
-
end
|
|
328
|
-
|
|
329
|
-
alias :option_a :option_all
|
|
330
|
-
|
|
331
|
-
def option_directory
|
|
332
|
-
@directory_only = true
|
|
147
|
+
# Represents the status of an element
|
|
148
|
+
class ElementStatus
|
|
149
|
+
def initialize(path, status, base_version = nil)
|
|
150
|
+
@path = path
|
|
151
|
+
@status = status
|
|
152
|
+
@base_version = base_version
|
|
333
153
|
end
|
|
334
154
|
|
|
335
|
-
|
|
155
|
+
attr_reader :path, :status, :base_version
|
|
336
156
|
|
|
337
|
-
def
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
cleartool("ls #{args} #{effective_targets}") do |line|
|
|
342
|
-
case line
|
|
343
|
-
when /^(.+)@@(\S+) \[hijacked/
|
|
344
|
-
report(:HIJACK, mkpath($1), $2)
|
|
345
|
-
when /^(.+)@@(\S+) \[loaded but missing\]/
|
|
346
|
-
report(:MISSING, mkpath($1), $2)
|
|
347
|
-
when /^(.+)@@\S+\\CHECKEDOUT(?: from (\S+))?/
|
|
348
|
-
element_path = mkpath($1)
|
|
349
|
-
status = element_path.exist? ? :CO : :MISSING
|
|
350
|
-
report(status, element_path, $2 || 'new')
|
|
351
|
-
when /^(.+)@@(\S+) +Rule: /
|
|
352
|
-
next unless @include_all
|
|
353
|
-
report(:OK, mkpath($1), $2)
|
|
354
|
-
when /^(.+)/
|
|
355
|
-
path = mkpath($1)
|
|
356
|
-
if ignored?(path)
|
|
357
|
-
log_debug "ignoring #{path}"
|
|
358
|
-
next
|
|
359
|
-
end
|
|
360
|
-
report(:LOCAL, path)
|
|
361
|
-
else
|
|
362
|
-
cannot_deal_with line
|
|
363
|
-
end
|
|
364
|
-
end
|
|
157
|
+
def to_s
|
|
158
|
+
s = "#{path} (#{status})"
|
|
159
|
+
s += " [#{base_version}]" if base_version
|
|
160
|
+
return s
|
|
365
161
|
end
|
|
366
|
-
|
|
367
162
|
end
|
|
368
163
|
|
|
369
164
|
class LsCoCommand < Command
|
|
@@ -402,83 +197,6 @@ EOF
|
|
|
402
197
|
|
|
403
198
|
end
|
|
404
199
|
|
|
405
|
-
class UpdateCommand < Command
|
|
406
|
-
|
|
407
|
-
def synopsis
|
|
408
|
-
"[-nomerge] [<element> ...]"
|
|
409
|
-
end
|
|
410
|
-
|
|
411
|
-
def help
|
|
412
|
-
<<EOF
|
|
413
|
-
Update your (snapshot) view.
|
|
414
|
-
|
|
415
|
-
-nomerge Don\'t attempt to merge in changes to checked-out files.
|
|
416
|
-
EOF
|
|
417
|
-
|
|
418
|
-
end
|
|
419
|
-
|
|
420
|
-
def option_nomerge
|
|
421
|
-
@nomerge = true
|
|
422
|
-
end
|
|
423
|
-
|
|
424
|
-
def relative_path(s)
|
|
425
|
-
full_path = view_root + mkpath(s)
|
|
426
|
-
full_path.relative_path_from(Pathname.pwd)
|
|
427
|
-
end
|
|
428
|
-
|
|
429
|
-
def execute_update
|
|
430
|
-
args = '-log nul -force'
|
|
431
|
-
args += ' -print' if just_testing?
|
|
432
|
-
cleartool("update #{args} #{effective_targets}") do |line|
|
|
433
|
-
case line
|
|
434
|
-
when /^Processing dir "(.*)"/
|
|
435
|
-
# ignore
|
|
436
|
-
when /^\.*$/
|
|
437
|
-
# ignore
|
|
438
|
-
when /^Making dir "(.*)"/
|
|
439
|
-
report(:NEW, relative_path($1))
|
|
440
|
-
when /^Loading "(.*)"/
|
|
441
|
-
report(:UPDATED, relative_path($1))
|
|
442
|
-
when /^Unloaded "(.*)"/
|
|
443
|
-
report(:REMOVED, relative_path($1))
|
|
444
|
-
when /^Keeping hijacked object "(.*)" - base "(.*)"/
|
|
445
|
-
report(:HIJACK, relative_path($1), $2)
|
|
446
|
-
when /^Keeping "(.*)"/
|
|
447
|
-
# ignore
|
|
448
|
-
when /^End dir/
|
|
449
|
-
# ignore
|
|
450
|
-
when /^Done loading/
|
|
451
|
-
# ignore
|
|
452
|
-
else
|
|
453
|
-
cannot_deal_with line
|
|
454
|
-
end
|
|
455
|
-
end
|
|
456
|
-
end
|
|
457
|
-
|
|
458
|
-
def execute_merge
|
|
459
|
-
args = '-log nul -flatest '
|
|
460
|
-
if just_testing?
|
|
461
|
-
args += "-print"
|
|
462
|
-
elsif @graphical
|
|
463
|
-
args += "-gmerge"
|
|
464
|
-
else
|
|
465
|
-
args += "-merge -gmerge"
|
|
466
|
-
end
|
|
467
|
-
cleartool("findmerge #{effective_targets} #{args}") do |line|
|
|
468
|
-
case line
|
|
469
|
-
when /^Needs Merge "(.+)" \[to \S+ from (\S+) base (\S+)\]/
|
|
470
|
-
report(:MERGE, mkpath($1), $2)
|
|
471
|
-
end
|
|
472
|
-
end
|
|
473
|
-
end
|
|
474
|
-
|
|
475
|
-
def execute
|
|
476
|
-
execute_update
|
|
477
|
-
execute_merge unless @nomerge
|
|
478
|
-
end
|
|
479
|
-
|
|
480
|
-
end
|
|
481
|
-
|
|
482
200
|
class CheckinCommand < Command
|
|
483
201
|
|
|
484
202
|
def synopsis
|
|
@@ -495,7 +213,9 @@ EOF
|
|
|
495
213
|
comment_file.open("w") do |out|
|
|
496
214
|
out.puts(@comment)
|
|
497
215
|
end
|
|
498
|
-
|
|
216
|
+
|
|
217
|
+
# if checking in an add/remove, one needs to also check in the parent directory of that file
|
|
218
|
+
cleartool_unsafe("checkin -cfile #{comment_file} #{specified_targets} #{specified_targets.parents}") do |line|
|
|
499
219
|
case line
|
|
500
220
|
when /^Loading /
|
|
501
221
|
# ignore
|
|
@@ -699,7 +419,6 @@ EOF
|
|
|
699
419
|
end
|
|
700
420
|
end
|
|
701
421
|
end
|
|
702
|
-
|
|
703
422
|
end
|
|
704
423
|
|
|
705
424
|
class MoveCommand < DirectoryModificationCommand
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
require 'basketcase/utils'
|
|
2
|
+
|
|
3
|
+
class Basketcase
|
|
4
|
+
# Base ClearCase command
|
|
5
|
+
class Command
|
|
6
|
+
include Basketcase::Utils
|
|
7
|
+
|
|
8
|
+
extend Forwardable
|
|
9
|
+
def_delegators :@basketcase, :log_debug, :just_testing?, :ignored?, :make_command, :run
|
|
10
|
+
|
|
11
|
+
def synopsis
|
|
12
|
+
""
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def help
|
|
16
|
+
"Sorry, no help provided ..."
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def initialize(basketcase)
|
|
20
|
+
@basketcase = basketcase
|
|
21
|
+
@listener = DefaultListener
|
|
22
|
+
@recursive = false
|
|
23
|
+
@graphical = false
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
attr_writer :listener
|
|
27
|
+
attr_writer :targets
|
|
28
|
+
|
|
29
|
+
def report(status, path, version = nil)
|
|
30
|
+
@listener.call(ElementStatus.new(path, status, version))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def option_recurse
|
|
34
|
+
@recursive = true
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
alias :option_r :option_recurse
|
|
38
|
+
|
|
39
|
+
def option_graphical
|
|
40
|
+
@graphical = true
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
alias :option_g :option_graphical
|
|
44
|
+
|
|
45
|
+
def option_comment(comment)
|
|
46
|
+
@comment = comment
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
alias :option_m :option_comment
|
|
50
|
+
|
|
51
|
+
attr_accessor :comment
|
|
52
|
+
|
|
53
|
+
# Handle command-line arguments:
|
|
54
|
+
# - For option arguments of the form "-X", call the corresponding
|
|
55
|
+
# option_X() method.
|
|
56
|
+
# - Remaining arguments are stored in @targets
|
|
57
|
+
def accept_args(args)
|
|
58
|
+
while /^-+(.+)/ === args[0]
|
|
59
|
+
option = args.shift
|
|
60
|
+
option_method_name = "option_#{$1}"
|
|
61
|
+
unless respond_to?(option_method_name)
|
|
62
|
+
raise UsageException, "Unrecognised option: #{option}"
|
|
63
|
+
end
|
|
64
|
+
option_method = method(option_method_name)
|
|
65
|
+
parameters = []
|
|
66
|
+
option_method.arity.times { parameters << args.shift }
|
|
67
|
+
option_method.call(*parameters)
|
|
68
|
+
end
|
|
69
|
+
@targets = args
|
|
70
|
+
self
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def effective_targets
|
|
74
|
+
TargetList.new(@targets.empty? ? ['.'] : @targets)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def specified_targets
|
|
78
|
+
raise UsageException, "No target specified" if @targets.empty?
|
|
79
|
+
TargetList.new(@targets)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
def cleartool(command)
|
|
85
|
+
log_debug "RUNNING: cleartool #{command}"
|
|
86
|
+
IO.popen("cleartool " + command).each_line do |line|
|
|
87
|
+
line.sub!("\r", '')
|
|
88
|
+
log_debug "<<< " + line
|
|
89
|
+
yield(line) if block_given?
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def cleartool_unsafe(command, &block)
|
|
94
|
+
if just_testing?
|
|
95
|
+
puts "WOULD RUN: cleartool #{command}"
|
|
96
|
+
return
|
|
97
|
+
end
|
|
98
|
+
cleartool(command, &block)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def view_root
|
|
102
|
+
@root ||= catch(:root) do
|
|
103
|
+
cleartool("pwv -root") do |line|
|
|
104
|
+
throw :root, mkpath(line.chomp)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
log_debug "view_root = #{@root}"
|
|
108
|
+
@root
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def cannot_deal_with(line)
|
|
112
|
+
$stderr.puts "unrecognised output: " + line
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def edit(file)
|
|
116
|
+
editor = ENV["EDITOR"] || "notepad"
|
|
117
|
+
system("#{editor} #{file}")
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'basketcase/command'
|
|
2
|
+
|
|
3
|
+
class Basketcase
|
|
4
|
+
class HelpCommand < Command
|
|
5
|
+
def synopsis
|
|
6
|
+
"[<command>]"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def help
|
|
10
|
+
"Display usage instructions."
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def execute
|
|
14
|
+
if @targets.empty?
|
|
15
|
+
puts @basketcase.usage
|
|
16
|
+
exit
|
|
17
|
+
end
|
|
18
|
+
@targets.each do |command_name|
|
|
19
|
+
command = make_command(command_name)
|
|
20
|
+
puts
|
|
21
|
+
puts "% basketcase #{command_name} #{command.synopsis}"
|
|
22
|
+
puts
|
|
23
|
+
puts command.help.gsub(/^/, " ")
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require 'basketcase/command'
|
|
2
|
+
|
|
3
|
+
class Basketcase
|
|
4
|
+
class LsCommand < Command
|
|
5
|
+
def synopsis
|
|
6
|
+
"[<element> ...]"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def help
|
|
10
|
+
<<EOF
|
|
11
|
+
List element status.
|
|
12
|
+
|
|
13
|
+
-a(ll) Show all files.
|
|
14
|
+
(by default, up-to-date files are not reported)
|
|
15
|
+
|
|
16
|
+
-r(ecurse) Recursively list sub-directories.
|
|
17
|
+
(by default, just lists current directory)
|
|
18
|
+
EOF
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def option_all
|
|
22
|
+
@include_all = true
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
alias :option_a :option_all
|
|
26
|
+
|
|
27
|
+
def option_directory
|
|
28
|
+
@directory_only = true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
alias :option_d :option_directory
|
|
32
|
+
|
|
33
|
+
def execute
|
|
34
|
+
args = ''
|
|
35
|
+
args += ' -recurse' if @recursive
|
|
36
|
+
args += ' -directory' if @directory_only
|
|
37
|
+
cleartool("ls #{args} #{effective_targets}") do |line|
|
|
38
|
+
case line
|
|
39
|
+
when /^(.+)@@(\S+) \[hijacked/
|
|
40
|
+
report(:HIJACK, mkpath($1), $2)
|
|
41
|
+
when /^(.+)@@(\S+) \[loaded but missing\]/
|
|
42
|
+
report(:MISSING, mkpath($1), $2)
|
|
43
|
+
when /^(.+)@@\S+\\CHECKEDOUT(?: from (\S+))?/
|
|
44
|
+
element_path = mkpath($1)
|
|
45
|
+
status = element_path.exist? ? :CO : :MISSING
|
|
46
|
+
report(status, element_path, $2 || 'new')
|
|
47
|
+
when /^(.+)@@(\S+) +Rule: /
|
|
48
|
+
next unless @include_all
|
|
49
|
+
report(:OK, mkpath($1), $2)
|
|
50
|
+
when /^(.+)/
|
|
51
|
+
path = mkpath($1)
|
|
52
|
+
if ignored?(path)
|
|
53
|
+
log_debug "ignoring #{path}"
|
|
54
|
+
next
|
|
55
|
+
end
|
|
56
|
+
report(:LOCAL, path)
|
|
57
|
+
else
|
|
58
|
+
cannot_deal_with line
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
require 'basketcase/command'
|
|
2
|
+
|
|
3
|
+
class Basketcase
|
|
4
|
+
|
|
5
|
+
class UpdateCommand < Command
|
|
6
|
+
|
|
7
|
+
def synopsis
|
|
8
|
+
"[-nomerge] [<element> ...]"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def help
|
|
12
|
+
<<EOF
|
|
13
|
+
Update your (snapshot) view.
|
|
14
|
+
|
|
15
|
+
-nomerge Don\'t attempt to merge in changes to checked-out files.
|
|
16
|
+
EOF
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def option_nomerge
|
|
21
|
+
@nomerge = true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def relative_path(s)
|
|
25
|
+
full_path = view_root + mkpath(s)
|
|
26
|
+
full_path.relative_path_from(mkpath(Pathname.pwd))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def execute_update
|
|
30
|
+
args = '-log nul -force'
|
|
31
|
+
args += ' -print' if just_testing?
|
|
32
|
+
cleartool("update #{args} #{effective_targets}") do |line|
|
|
33
|
+
case line
|
|
34
|
+
when /^Processing dir "(.*)"/
|
|
35
|
+
# ignore
|
|
36
|
+
when /^\.*$/
|
|
37
|
+
# ignore
|
|
38
|
+
when /^Making dir "(.*)"/
|
|
39
|
+
report(:NEW, relative_path($1))
|
|
40
|
+
when /^Loading "(.*)"/
|
|
41
|
+
report(:UPDATED, relative_path($1))
|
|
42
|
+
when /^Unloaded "(.*)"/
|
|
43
|
+
report(:REMOVED, relative_path($1))
|
|
44
|
+
when /^Keeping hijacked object "(.*)" - base "(.*)"/
|
|
45
|
+
report(:HIJACK, relative_path($1), $2)
|
|
46
|
+
when /^Keeping "(.*)"/
|
|
47
|
+
# ignore
|
|
48
|
+
when /^End dir/
|
|
49
|
+
# ignore
|
|
50
|
+
when /^Done loading/
|
|
51
|
+
# ignore
|
|
52
|
+
else
|
|
53
|
+
cannot_deal_with line
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def execute_merge
|
|
59
|
+
args = '-log nul -flatest '
|
|
60
|
+
if just_testing?
|
|
61
|
+
args += "-print"
|
|
62
|
+
elsif @graphical
|
|
63
|
+
args += "-gmerge"
|
|
64
|
+
else
|
|
65
|
+
args += "-merge -gmerge"
|
|
66
|
+
end
|
|
67
|
+
cleartool("findmerge #{effective_targets} #{args}") do |line|
|
|
68
|
+
case line
|
|
69
|
+
when /^Needs Merge "(.+)" \[to \S+ from (\S+) base (\S+)\]/
|
|
70
|
+
report(:MERGE, mkpath($1), $2)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def execute
|
|
76
|
+
execute_update
|
|
77
|
+
execute_merge unless @nomerge
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
end
|
|
81
|
+
end
|
data/spec/auto_sync_spec.rb
CHANGED
|
@@ -2,13 +2,9 @@ require File.dirname(__FILE__) + '/spec_helper'
|
|
|
2
2
|
load 'cleartool'
|
|
3
3
|
|
|
4
4
|
describe 'Autosync' do
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__))}:#{ENV['PATH']}"
|
|
9
|
-
|
|
10
|
-
File.delete(CLEARTOOL_ARGS_LOG) if File.exists?(CLEARTOOL_ARGS_LOG)
|
|
11
|
-
|
|
5
|
+
it 'should be able to handle lots of files that need to be added' do
|
|
6
|
+
File.delete(CLEARTOOL_ARGS_LOG) if File.exists?(CLEARTOOL_ARGS_LOG)
|
|
7
|
+
|
|
12
8
|
OutputQueue.enqueue(%q{
|
|
13
9
|
deleteddir1@@\main\2 [loaded but missing] Rule: \main\LATEST
|
|
14
10
|
deletedfile1.txt@@\main\1 [loaded but missing] Rule: \main\LATEST
|
|
@@ -35,10 +31,10 @@ cleartool: Error: Can't modify directory "." because it is not checked out.
|
|
|
35
31
|
cleartool: Error: Can't modify directory "." because it is not checked out.
|
|
36
32
|
cleartool: Error: Can't modify directory "." because it is not checked out.
|
|
37
33
|
})
|
|
38
|
-
|
|
34
|
+
|
|
39
35
|
$stderr.stub!(:puts)
|
|
40
36
|
Basketcase.new.do('auto-sync','-n')
|
|
41
|
-
|
|
37
|
+
|
|
42
38
|
File.read(CLEARTOOL_ARGS_LOG).should == <<HERE
|
|
43
39
|
ls -recurse .
|
|
44
40
|
ls -directory . newdir1 updateddir1
|
|
@@ -48,8 +44,5 @@ ls -directory . deleteddir1 1
|
|
|
48
44
|
rmname -ncomment deleteddir1 deletedfile1.txt deleteddir1/deletedfile1.txt 1/deletedfile1.txt
|
|
49
45
|
checkout -unreserved -ncomment -usehijack updatedfiled1.txt updateddir1/updatedfiled1.txt
|
|
50
46
|
HERE
|
|
51
|
-
|
|
52
47
|
end
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
|
2
|
+
load 'cleartool'
|
|
3
|
+
|
|
4
|
+
describe 'Autocommit' do
|
|
5
|
+
it 'should be able to handle lots of files that need to be added' do
|
|
6
|
+
File.delete(CLEARTOOL_ARGS_LOG) if File.exists?(CLEARTOOL_ARGS_LOG)
|
|
7
|
+
|
|
8
|
+
OutputQueue.enqueue(%q{
|
|
9
|
+
.@@\main\CHECKEDOUT from \main\15 Rule: CHECKEDOUT
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
$stderr.stub!(:puts)
|
|
13
|
+
Basketcase.new.do('auto-commit', '-m', 'a comment')
|
|
14
|
+
|
|
15
|
+
File.read(CLEARTOOL_ARGS_LOG).should == <<HERE
|
|
16
|
+
ls -recurse .
|
|
17
|
+
checkin -cfile basketcase-checkin-comment.tmp .
|
|
18
|
+
HERE
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
|
|
2
|
+
require 'pathname'
|
|
3
|
+
require File.dirname(__FILE__)+'/spec_helper'
|
|
4
|
+
load 'cleartool'
|
|
5
|
+
|
|
6
|
+
describe 'modify files' do
|
|
7
|
+
|
|
8
|
+
before(:all) do
|
|
9
|
+
FileUtils.mkdir_p(WORKING_VIEW)
|
|
10
|
+
FileUtils.mkdir_p(CONTROL_VIEW)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def create_file_in_clearcase
|
|
14
|
+
|
|
15
|
+
newfile = "newfile-" + Time.now.to_i.to_s + ".txt"
|
|
16
|
+
Dir.chdir(WORKING_VIEW) do
|
|
17
|
+
File.open(newfile, 'w') { |out| out.puts "spanky new content" }
|
|
18
|
+
Basketcase.new.do('add', newfile)
|
|
19
|
+
Basketcase.new.do('ci', '-m', 'adding test file', newfile)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
newfile
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
it 'should add a file to clearcase' do
|
|
27
|
+
|
|
28
|
+
newfile = create_file_in_clearcase
|
|
29
|
+
|
|
30
|
+
Dir.chdir(CONTROL_VIEW) do
|
|
31
|
+
Basketcase.new.do('update', '.')
|
|
32
|
+
File.exists?(newfile).should == true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it 'should update a file in clearcase' do
|
|
39
|
+
|
|
40
|
+
testfilename = create_file_in_clearcase
|
|
41
|
+
|
|
42
|
+
new_content = 'i will love you as long as this file exists'
|
|
43
|
+
|
|
44
|
+
Dir.chdir(WORKING_VIEW) do
|
|
45
|
+
|
|
46
|
+
file = Pathname(testfilename)
|
|
47
|
+
file.chmod(file.stat.mode | 0600) unless file.writable?
|
|
48
|
+
File.open(testfilename, 'a') { |io| io.puts new_content}
|
|
49
|
+
|
|
50
|
+
Basketcase.new.do('co', '-h', testfilename)
|
|
51
|
+
Basketcase.new.do('ci', '-m', 'updating test file', testfilename)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
Dir.chdir(CONTROL_VIEW) do
|
|
55
|
+
|
|
56
|
+
Basketcase.new.do('update', '.')
|
|
57
|
+
|
|
58
|
+
file_contents = File.read(testfilename)
|
|
59
|
+
file_contents.include?(new_content)
|
|
60
|
+
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it 'should delete a file from clearcase' do
|
|
66
|
+
|
|
67
|
+
testfilename = create_file_in_clearcase
|
|
68
|
+
|
|
69
|
+
Dir.chdir(WORKING_VIEW) do
|
|
70
|
+
|
|
71
|
+
Basketcase.new.do('remove', testfilename)
|
|
72
|
+
Basketcase.new.do('ci', '-m', 'removing test file', testfilename)
|
|
73
|
+
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
Dir.chdir(CONTROL_VIEW) do
|
|
77
|
+
Basketcase.new.do('update', '.')
|
|
78
|
+
File.exists?(testfilename).should == false
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -3,3 +3,12 @@ $:.unshift(File.expand_path(File.dirname(__FILE__)))
|
|
|
3
3
|
|
|
4
4
|
require 'basketcase'
|
|
5
5
|
|
|
6
|
+
ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__))}:#{ENV['PATH']}"
|
|
7
|
+
|
|
8
|
+
ROOT = File.expand_path(File.dirname(__FILE__)+'/..')
|
|
9
|
+
|
|
10
|
+
WORKING_VIEW = ENV['BC_WORKING_VIEW']
|
|
11
|
+
CONTROL_VIEW = ENV['BC_CONTROL_VIEW']
|
|
12
|
+
|
|
13
|
+
WORKING_VIEW ||= "#{ROOT}/tmp/views/working-view"
|
|
14
|
+
CONTROL_VIEW ||= "#{ROOT}/tmp/views/control-view"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: markryall-basketcase
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.1.
|
|
4
|
+
version: 1.1.10
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- mdub
|
|
@@ -11,11 +11,11 @@ autorequire:
|
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
13
|
|
|
14
|
-
date: 2009-
|
|
14
|
+
date: 2009-09-07 00:00:00 -07:00
|
|
15
15
|
default_executable:
|
|
16
16
|
dependencies: []
|
|
17
17
|
|
|
18
|
-
description:
|
|
18
|
+
description: basketcase fork
|
|
19
19
|
email: mdub@dogbiscuit.org
|
|
20
20
|
executables:
|
|
21
21
|
- basketcase
|
|
@@ -36,9 +36,16 @@ files:
|
|
|
36
36
|
- bin/bc-mirror
|
|
37
37
|
- lib/array_patching.rb
|
|
38
38
|
- lib/basketcase.rb
|
|
39
|
+
- lib/basketcase/command.rb
|
|
40
|
+
- lib/basketcase/help_command.rb
|
|
41
|
+
- lib/basketcase/ls_command.rb
|
|
42
|
+
- lib/basketcase/update_command.rb
|
|
43
|
+
- lib/basketcase/utils.rb
|
|
39
44
|
- spec/auto_sync_spec.rb
|
|
45
|
+
- spec/autocommit_steps.rb
|
|
40
46
|
- spec/basketcase_spec.rb
|
|
41
47
|
- spec/cleartool
|
|
48
|
+
- spec/modify_files_spec.rb
|
|
42
49
|
- spec/spec_helper.rb
|
|
43
50
|
has_rdoc: false
|
|
44
51
|
homepage: http://github.com/markryall/basketcase
|
|
@@ -69,5 +76,7 @@ specification_version: 3
|
|
|
69
76
|
summary: clearcase for the masses
|
|
70
77
|
test_files:
|
|
71
78
|
- spec/auto_sync_spec.rb
|
|
79
|
+
- spec/autocommit_steps.rb
|
|
72
80
|
- spec/basketcase_spec.rb
|
|
81
|
+
- spec/modify_files_spec.rb
|
|
73
82
|
- spec/spec_helper.rb
|