sub 0.1.0 → 0.3.0

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