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 +8 -3
- data/Manifest.txt +4 -0
- data/bin/sub +1 -0
- data/lib/sub.rb +5 -2
- data/lib/sub/app.rb +15 -200
- data/lib/sub/array.rb +14 -0
- data/lib/sub/external.rb +67 -0
- data/lib/sub/root.rb +126 -0
- data/lib/sub/status.rb +46 -0
- data/lib/sub/version.rb +2 -2
- metadata +6 -2
data/History.txt
CHANGED
@@ -1,4 +1,9 @@
|
|
1
|
-
== 0.0
|
1
|
+
== 0.3.0 2007-10-02
|
2
2
|
|
3
|
-
*
|
4
|
-
|
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
data/bin/sub
CHANGED
data/lib/sub.rb
CHANGED
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
|
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 :
|
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
|
-
|
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
|
-
|
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
data/lib/sub/external.rb
ADDED
@@ -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
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.
|
7
|
-
date: 2007-
|
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: []
|