sub 0.1.0 → 0.3.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.
data/History.txt CHANGED
@@ -1,4 +1,9 @@
1
- == 0.0.1 2007-09-04
1
+ == 0.3.0 2007-10-02
2
2
 
3
- * 1 major enhancement:
4
- * Initial release
3
+ * Bugfix: checks out externals that were added since last update
4
+ * Bugfix: doesn't swallow parameter following url
5
+ * You can now use either SUB_BASE_URL or SVN for the default repository path environment variable
6
+
7
+ == 0.1.0 2007-09-04
8
+
9
+ * Initial release
data/Manifest.txt CHANGED
@@ -6,5 +6,9 @@ Rakefile
6
6
  bin/sub
7
7
  lib/sub.rb
8
8
  lib/sub/app.rb
9
+ lib/sub/array.rb
10
+ lib/sub/external.rb
11
+ lib/sub/root.rb
12
+ lib/sub/status.rb
9
13
  lib/sub/version.rb
10
14
  tasks/environment.rake
data/bin/sub CHANGED
@@ -1,3 +1,4 @@
1
+ #!/usr/local/bin/ruby
1
2
  $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
2
3
  require 'sub'
3
4
  Sub.from_args(ARGV).execute
data/lib/sub.rb CHANGED
@@ -1,3 +1,6 @@
1
- $:.unshift File.dirname(__FILE__)
2
-
3
1
  require 'sub/app'
2
+ require 'sub/array'
3
+ require 'sub/external'
4
+ require 'sub/status'
5
+ require 'sub/version'
6
+ require 'sub/root'
data/lib/sub/app.rb CHANGED
@@ -6,94 +6,6 @@ QUIET = 0
6
6
  NORMAL = 1
7
7
  VERBOSE = 2
8
8
 
9
- class External
10
-
11
- def self.externals_in_directory(parent)
12
- exts = []
13
- prop = `svn propget svn:externals #{parent}`
14
- prop.split("\n").each do |prop_line|
15
- next if prop_line.strip.empty?
16
- exts << External.new(parent, prop_line)
17
- end
18
- exts
19
- end
20
-
21
- attr_reader :name, :path, :url, :revision
22
-
23
- def initialize(parent, property)
24
- match = /^(\S+)\s+(-r\s*\d*\s+)?(\S+)\s*$/.match(property)
25
- @name = match[1]
26
- @revision = match[2] ? match[2].gsub(/\D/,'').to_i : nil
27
- @url = match[3]
28
- @path = "#{parent}/#{@name}"
29
- end
30
-
31
- def ==(other)
32
- (path == other.path and url == other.url)
33
- end
34
-
35
- def to_s
36
- "External[name=#{name}, path=#{path}, url=#{url}, revision=#{revision}]"
37
- end
38
-
39
- def revision_actual
40
- info = `svn info #{@path}`
41
- info.grep(/^Revision:/).first.gsub(/Revision: /, '').to_i
42
- end
43
-
44
- def should_update?
45
- revision == nil || revision_actual != revision
46
- end
47
-
48
- end
49
-
50
- class Status
51
- class Line
52
- def initialize(text)
53
- text.strip!
54
- @modified = /^M/.match(text) ? true : false
55
- @external = /^X/.match(text) ? true : false
56
- @unversioned = /^\?/.match(text) ? true : false
57
- @modified_properties = /^.M/.match(text) ? true : false
58
- @path = text.gsub(/^...... /, '')
59
- end
60
-
61
- attr_reader :path
62
-
63
- def modified?
64
- @modified
65
- end
66
- def external?
67
- @external
68
- end
69
- def unversioned?
70
- @unversioned
71
- end
72
- def modified_properties?
73
- @modified_properties
74
- end
75
- end
76
-
77
-
78
- def initialize(text)
79
- @lines = text.split("\n").collect do |line|
80
- Status::Line.new(line)
81
- end
82
- end
83
-
84
- def modified
85
- @lines.select { |line| line.modified? }.collect {|line| line.path}
86
- end
87
-
88
- def externals
89
- @lines.select { |line| line.external? }.collect {|line| line.path}
90
- end
91
-
92
- def unversioned
93
- @lines.select { |line| line.unversioned? }.collect {|line| line.path}
94
- end
95
- end
96
-
97
9
  class Sub
98
10
  DEFAULT_BASE_URL = "svn+ssh://rubyforge.org/var/svn"
99
11
 
@@ -117,7 +29,10 @@ class Sub
117
29
  options[:clean] = true
118
30
  when '--url'
119
31
  args.shift
120
- options[:url] = args.shift
32
+ options[:url] = args[0]
33
+ when '--version'
34
+ puts "Sub version #{Sub::VERSION::STRING}"
35
+ exit 0
121
36
  else
122
37
  still_parsing_options = false
123
38
  end
@@ -127,17 +42,16 @@ class Sub
127
42
  Sub.new(options, args)
128
43
  end
129
44
 
130
- attr_reader :verbosity, :clean, :command, :args, :url
45
+ attr_reader :clean, :command, :args, :url
46
+ attr_accessor :verbosity
131
47
 
132
48
  def initialize(options = {:verbosity => NORMAL}, args = [])
133
49
  options = defaults.merge(options)
134
50
  @verbosity = options[:verbosity]
135
51
  @clean = options[:clean]
136
52
  @command = options[:command]
137
- @url = options[:url] || ENV['SUB_BASE_URL'] || DEFAULT_BASE_URL
53
+ @url = options[:url] || ENV['SUB_BASE_URL'] || ENV['SVN'] || DEFAULT_BASE_URL
138
54
  @args = args
139
-
140
- @status = {}
141
55
  end
142
56
 
143
57
  def defaults
@@ -173,6 +87,8 @@ class Sub
173
87
  puts """
174
88
  sub - Alex's wrapper for subversion
175
89
 
90
+ Version: #{Sub::VERSION::STRING}
91
+
176
92
  Usage:
177
93
  sub co project_name [dir_name]
178
94
  checks out [base_url]/project_name/trunk into ./project_name (or dir_name if specified)
@@ -192,7 +108,9 @@ Options:
192
108
  'up' command removes all unversioned files and directories
193
109
  --url [base_url]
194
110
  sets base repository url for 'co' command
195
- (default is ENV['SUB_BASE_URL'] or #{DEFAULT_BASE_URL})
111
+ (default is ENV['SUB_BASE_URL'] or ENV['SVN'] or #{DEFAULT_BASE_URL})
112
+ --version
113
+ prints release version
196
114
  """
197
115
  end
198
116
 
@@ -207,100 +125,11 @@ Options:
207
125
  end
208
126
 
209
127
  def update(root)
210
- if @clean
211
- remove_unversioned(root)
212
- end
213
-
214
- externals_before = externals(root)
215
- svn("up --ignore-externals #{root}")
216
- externals = externals(root)
217
- # for some reason (array - array) doesn't work right here
218
- # so i had to write my own subtract method
219
- removed_externals = externals_before.subtract(externals)
220
- removed_externals.each do |ext|
221
- say "Removed external #{ext}"
222
- FileUtils.rm_rf(ext.path)
223
- end
224
-
225
- added_externals = externals.subtract(externals_before)
226
- existing_externals = externals.subtract(added_externals)
227
-
228
- # todo: extract Processes
229
- processes = []
230
- def update_external(processes, external)
231
- pid = fork do
232
- say "Updating external #{external.path}"
233
- run("svn cleanup #{external.path}") if File.exists?(external.path)
234
- rev = external.revision.nil? ? '' : "-r#{external.revision}"
235
- svn("up #{rev} #{external.path} | grep -v 'At revision'")
236
- end
237
- processes << {:pid => pid, :external => external}
238
- end
239
-
240
- def checkout_external(processes, external)
241
- pid = fork do
242
- say "Checking out external #{external.path}"
243
- rev = external.revision.nil? ? '' : "-r#{external.revision}"
244
- svn("co #{rev} #{external.url} #{external.path}")
245
- end
246
- processes << {:pid => pid, :external => external}
247
- end
248
-
249
- added_externals.each do |external|
250
- checkout_external(processes, external)
251
- end
252
-
253
- already_up_to_date = []
254
- existing_externals.each do |external|
255
- if external.should_update?
256
- update_external(processes, external)
257
- else
258
- already_up_to_date << external
259
- end
260
- end
261
-
262
- unless already_up_to_date.empty?
263
- say("External#{'s' if already_up_to_date.size > 1} " +
264
- already_up_to_date.collect {|external| external.name}.join(", ") +
265
- " already up to date")
266
- end
267
-
268
- processes.each do |process|
269
- Process.waitpid(process[:pid], 0)
270
- end
271
- end
272
-
273
- def remove_unversioned(root)
274
- status(root).unversioned.each do |path|
275
- if File.directory?(path)
276
- say "Removing unversioned directory #{path}"
277
- else
278
- say "Removing unversioned file #{path}"
279
- end
280
- FileUtils.rm_rf(path)
281
- end
282
- end
283
-
284
- def status(root)
285
- @status[root] ||= Status.new(run("svn st #{root}", true))
128
+ Root.new(root, self, clean).update
286
129
  end
287
130
 
288
131
  def externals(root)
289
- exts = []
290
- directories_containing_externals(root).collect do |parent|
291
- exts += External.externals_in_directory(parent)
292
- end
293
- exts
294
- end
295
-
296
- def directories_containing_externals(root)
297
- status(root).externals.collect do |path|
298
- if (path !~ /\//)
299
- "."
300
- else
301
- path.gsub(/\/[^\/]*$/, '')
302
- end
303
- end.uniq
132
+ Root.new(root, self, clean).externals
304
133
  end
305
134
 
306
135
  def parse_externals(st)
@@ -313,7 +142,7 @@ Options:
313
142
  prop = `svn propget svn:externals #{parent}`
314
143
  prop.split("\n").each do |external|
315
144
  next if external.strip.empty?
316
- exts << External.new(parent, external)
145
+ exts << External.new(parent, external, self)
317
146
  end
318
147
  end
319
148
  exts
@@ -340,17 +169,3 @@ Options:
340
169
  end
341
170
  end
342
171
 
343
- class Array
344
- def subtract(other)
345
- self.select do |item|
346
- !other.has?(item)
347
- end
348
- end
349
-
350
- def has?(something)
351
- self.each do |item|
352
- return true if item == something
353
- end
354
- false
355
- end
356
- end
data/lib/sub/array.rb ADDED
@@ -0,0 +1,14 @@
1
+ class Array
2
+ def subtract(other)
3
+ self.select do |item|
4
+ !other.has?(item)
5
+ end
6
+ end
7
+
8
+ def has?(something)
9
+ self.each do |item|
10
+ return true if item == something
11
+ end
12
+ false
13
+ end
14
+ end
@@ -0,0 +1,67 @@
1
+ class External
2
+
3
+ def self.externals_in_directory(parent, app)
4
+ exts = []
5
+ prop = `svn propget svn:externals #{parent}`
6
+ prop.split("\n").each do |prop_line|
7
+ next if prop_line.strip.empty?
8
+ exts << External.new(parent, prop_line, app)
9
+ end
10
+ exts
11
+ end
12
+
13
+ attr_reader :name, :path, :url, :revision, :app
14
+
15
+ def initialize(parent, property, app = nil)
16
+ match = /^(\S+)\s+(-r\s*\d*\s+)?(\S+)\s*$/.match(property)
17
+ @name = match[1]
18
+ @revision = match[2] ? match[2].gsub(/\D/,'').to_i : nil
19
+ @url = match[3]
20
+ @path = "#{parent}/#{@name}"
21
+ @app = app
22
+ end
23
+
24
+ def say(msg)
25
+ app.say(msg)
26
+ end
27
+
28
+ def svn(cmd)
29
+ app.svn(cmd)
30
+ end
31
+
32
+ def run(cmd, return_output = false)
33
+ app.run(cmd, return_output)
34
+ end
35
+
36
+ def ==(other)
37
+ (path == other.path and url == other.url)
38
+ end
39
+
40
+ def to_s
41
+ "External[name=#{name}, path=#{path}, url=#{url}, revision=#{revision}]"
42
+ end
43
+
44
+ def revision_actual
45
+ info = `svn info #{@path}`
46
+ info.grep(/^Revision:/).first.gsub(/Revision: /, '').to_i
47
+ end
48
+
49
+ def should_update?
50
+ revision == nil || revision_actual != revision
51
+ end
52
+
53
+ #todo: test? (indirectly tested via root.rb)
54
+ def update
55
+ say "Updating external #{path}"
56
+ run("svn cleanup #{path}")
57
+ rev = revision.nil? ? '' : "-r#{revision}"
58
+ svn("up #{rev} #{path} | grep -v 'At revision'")
59
+ end
60
+
61
+ def checkout
62
+ say "Checking out external #{path}"
63
+ rev = revision.nil? ? '' : "-r#{revision}"
64
+ svn("co #{rev} #{url} #{path}")
65
+ end
66
+
67
+ end
data/lib/sub/root.rb ADDED
@@ -0,0 +1,126 @@
1
+ class Root
2
+ attr_reader :app, :root_path
3
+
4
+ def initialize(root_path, app, clean = false)
5
+ @root_path = root_path
6
+ @app = app
7
+ @clean = clean
8
+ @status = nil
9
+ end
10
+
11
+ def say(msg)
12
+ app.say(msg)
13
+ end
14
+
15
+ def svn(cmd)
16
+ app.svn(cmd)
17
+ end
18
+
19
+ def run(cmd, return_output = false)
20
+ app.run(cmd, return_output)
21
+ end
22
+
23
+ class Processes < Array
24
+ def initialize
25
+ end
26
+
27
+ def launch
28
+ pid = fork do
29
+ yield
30
+ end
31
+ self << pid
32
+ end
33
+
34
+ def join
35
+ self.each do |pid|
36
+ Process.waitpid(pid, 0)
37
+ end
38
+ end
39
+ end
40
+
41
+ def update
42
+ if @clean
43
+ remove_unversioned
44
+ end
45
+
46
+ externals_before = externals
47
+
48
+ svn("up --ignore-externals #{root_path}")
49
+ # for some reason (array - array) doesn't work right here
50
+ # so i had to write my own subtract method
51
+ removed_externals = externals_before.subtract(externals)
52
+ removed_externals.each do |ext|
53
+ say "Removed external #{ext}"
54
+ FileUtils.rm_rf(ext.path)
55
+ end
56
+
57
+ @processes = Processes.new
58
+
59
+ already_up_to_date = []
60
+ externals.each do |external|
61
+ if external.should_update?
62
+ if File.exists?(external.path)
63
+ update_external(external)
64
+ else
65
+ checkout_external(external)
66
+ end
67
+ else
68
+ already_up_to_date << external
69
+ end
70
+ end
71
+
72
+ unless already_up_to_date.empty?
73
+ say("External#{'s' if already_up_to_date.size > 1} " +
74
+ already_up_to_date.collect {|external| external.name}.join(", ") +
75
+ " already up to date")
76
+ end
77
+
78
+ @processes.join
79
+ end
80
+
81
+ def update_external(external)
82
+ @processes.launch do
83
+ external.update
84
+ end
85
+ end
86
+
87
+ def checkout_external(external)
88
+ @processes.launch do
89
+ external.checkout
90
+ end
91
+ end
92
+
93
+ def remove_unversioned
94
+ status.unversioned.each do |path|
95
+ if File.directory?(path)
96
+ say "Removing unversioned directory #{path}"
97
+ else
98
+ say "Removing unversioned file #{path}"
99
+ end
100
+ FileUtils.rm_rf(path)
101
+ end
102
+ end
103
+
104
+ def status
105
+ @status ||= Status.new(run("svn st #{root_path}", true))
106
+ end
107
+
108
+ def externals
109
+ exts = []
110
+ directories_containing_externals.collect do |parent|
111
+ exts += External.externals_in_directory(parent, app)
112
+ end
113
+ exts
114
+ end
115
+
116
+ def directories_containing_externals
117
+ status.externals.collect do |path|
118
+ if (path !~ /\//)
119
+ "."
120
+ else
121
+ path.gsub(/\/[^\/]*$/, '')
122
+ end
123
+ end.uniq
124
+ end
125
+
126
+ end
data/lib/sub/status.rb ADDED
@@ -0,0 +1,46 @@
1
+ class Status
2
+ class Line
3
+ def initialize(text)
4
+ text.strip!
5
+ @modified = /^M/.match(text) ? true : false
6
+ @external = /^X/.match(text) ? true : false
7
+ @unversioned = /^\?/.match(text) ? true : false
8
+ @modified_properties = /^.M/.match(text) ? true : false
9
+ @path = text.gsub(/^...... /, '')
10
+ end
11
+
12
+ attr_reader :path
13
+
14
+ def modified?
15
+ @modified
16
+ end
17
+ def external?
18
+ @external
19
+ end
20
+ def unversioned?
21
+ @unversioned
22
+ end
23
+ def modified_properties?
24
+ @modified_properties
25
+ end
26
+ end
27
+
28
+
29
+ def initialize(text)
30
+ @lines = text.split("\n").collect do |line|
31
+ Status::Line.new(line)
32
+ end
33
+ end
34
+
35
+ def modified
36
+ @lines.select { |line| line.modified? }.collect {|line| line.path}
37
+ end
38
+
39
+ def externals
40
+ @lines.select { |line| line.external? }.collect {|line| line.path}
41
+ end
42
+
43
+ def unversioned
44
+ @lines.select { |line| line.unversioned? }.collect {|line| line.path}
45
+ end
46
+ end
data/lib/sub/version.rb CHANGED
@@ -1,7 +1,7 @@
1
- module Pong #:nodoc:
1
+ class Sub #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 1
4
+ MINOR = 3
5
5
  TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
3
3
  specification_version: 1
4
4
  name: sub
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.1.0
7
- date: 2007-09-12 00:00:00 -07:00
6
+ version: 0.3.0
7
+ date: 2007-10-02 00:00:00 -07:00
8
8
  summary: sub is a wrapper for svn that adds faster/safer updates, easy trunk checkout, etc.
9
9
  require_paths:
10
10
  - lib
@@ -37,6 +37,10 @@ files:
37
37
  - bin/sub
38
38
  - lib/sub.rb
39
39
  - lib/sub/app.rb
40
+ - lib/sub/array.rb
41
+ - lib/sub/external.rb
42
+ - lib/sub/root.rb
43
+ - lib/sub/status.rb
40
44
  - lib/sub/version.rb
41
45
  - tasks/environment.rake
42
46
  test_files: []