linkparser 1.0.4 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Style Fixup Rake Tasks
3
- # $Id: style.rb 10 2008-07-18 15:52:48Z deveiant $
3
+
4
4
  #
5
5
  # Authors:
6
6
  # * Michael Granger <ged@FaerieMUD.org>
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Subversion Rake Tasks
3
- # $Id: svn.rb 105 2009-06-01 19:20:12Z deveiant $
3
+
4
4
  #
5
5
  # Authors:
6
6
  # * Michael Granger <ged@FaerieMUD.org>
@@ -12,629 +12,657 @@ require 'date'
12
12
  require 'time'
13
13
  require 'abbrev'
14
14
 
15
- # Strftime format for tags/releases
16
- TAG_TIMESTAMP_FORMAT = '%Y%m%d-%H%M%S'
17
- TAG_TIMESTAMP_PATTERN = /\d{4}\d{2}\d{2}-\d{6}/
18
-
19
- RELEASE_VERSION_PATTERN = /\d+\.\d+\.\d+/
20
-
21
- DEFAULT_EDITOR = 'vi'
22
- DEFAULT_KEYWORDS = %w[Date Rev Author URL Id]
23
- KEYWORDED_FILEDIRS = %w[applets spec bin etc ext experiments examples lib misc docs]
24
- KEYWORDED_FILEPATTERN = /
25
- ^(?:
26
- (?:meta)?rakefile.* # Rakefiles
27
- |
28
- .*\.(?:rb|c|h|js|html|css|template|erb|page) # Source file extensions
29
- |
30
- readme|install|todo
31
- )$/ix
32
-
33
- COMMIT_MSG_FILE = 'commit-msg.txt'
34
-
35
- SVN_TRUNK_DIR = 'trunk' unless defined?( SVN_TRUNK_DIR )
36
- SVN_RELEASES_DIR = 'branches' unless defined?( SVN_RELEASES_DIR )
37
- SVN_BRANCHES_DIR = 'branches' unless defined?( SVN_BRANCHES_DIR )
38
- SVN_TAGS_DIR = 'tags' unless defined?( SVN_TAGS_DIR )
39
-
40
- FILE_INDENT = " " * 12
41
- LOG_INDENT = " " * 3
42
-
43
-
44
-
45
- ###
46
- ### Subversion-specific Helpers
47
- ###
48
-
49
- ### Return a new tag for the given time
50
- def make_new_tag( time=Time.now )
51
- return time.strftime( TAG_TIMESTAMP_FORMAT )
52
- end
53
-
54
-
55
- ### Get the subversion information for the current working directory as
56
- ### a hash.
57
- def get_svn_info( dir='.' )
58
- return {} unless File.directory?( File.join(dir, '.svn') )
59
- info = IO.read( '|-' ) or exec 'svn', 'info', dir.to_s
60
- return YAML.load( info ) # 'svn info' outputs valid YAML! Yay!
61
- rescue NotImplementedError
62
- trace "No fork(), proceeding without svn info..."
63
- return {}
64
- end
65
-
66
-
67
- ### Get a list of the objects registered with subversion under the specified directory and
68
- ### return them as an Array of Pathame objects.
69
- def get_svn_filelist( dir='.' )
70
- list = IO.read( '|-' ) or exec 'svn', 'st', '-v', '--ignore-externals', dir
71
-
72
- # Split into lines, filter out the unknowns, and grab the filenames as Pathnames
73
- # :FIXME: This will break if we ever put in a file with spaces in its name. This
74
- # will likely be the least of our worries if we do so, however, so it's not worth
75
- # the additional complexity to make it handle that case. If we do need that, there's
76
- # always the --xml output for 'svn st'...
77
- return list.split( $/ ).
78
- reject {|line| line =~ /^(\?|(\s*|--- .*)$)/ }.
79
- collect {|fn| Pathname(fn[/\S+$/]) }
80
- end
81
-
82
- ### Return the URL to the repository root for the specified +dir+.
83
- def get_svn_repo_root( dir='.' )
84
- info = get_svn_info( dir )
85
- return info['Repository Root']
86
- end
87
-
88
-
89
- ### Return the Subversion URL to the given +dir+.
90
- def get_svn_url( dir='.' )
91
- info = get_svn_info( dir )
92
- return info['URL']
93
- end
94
-
95
-
96
- ### Return the path of the specified +dir+ under the svn root of the
97
- ### checkout.
98
- def get_svn_path( dir='.' )
99
- root = get_svn_repo_root( dir )
100
- url = get_svn_url( dir )
101
-
102
- return url.sub( root + '/', '' )
103
- end
104
-
105
-
106
- ### Return the keywords for the specified array of +files+ as a Hash keyed by filename.
107
- def get_svn_keyword_map( *files )
108
- files.flatten!
109
- files.push( '.' ) if files.empty?
110
-
111
- cmd = ['svn', 'pg', 'svn:keywords', *files]
112
-
113
- # trace "Executing: svn pg svn:keywords " + files.join(' ')
114
- output = IO.read( '|-' ) or exec( 'svn', 'pg', 'svn:keywords', *files )
115
-
116
- kwmap = {}
117
- output.split( "\n" ).each do |line|
118
- next if line !~ /\s+-\s+/
119
- path, keywords = line.split( /\s+-\s+/, 2 )
120
- kwmap[ path ] = keywords.split
121
- end
122
-
123
- return kwmap
124
- end
125
-
126
-
127
- ### Return the latest revision number of the specified +dir+ as an Integer.
128
- def get_svn_rev( dir='.' )
129
- info = get_svn_info( dir )
130
- return info['Revision']
131
- end
132
-
133
-
134
- ### Return the latest revision number of the specified +dir+ as an Integer.
135
- def get_last_changed_rev( dir='.' )
136
- info = get_svn_info( dir )
137
- return info['Last Changed Rev']
138
- end
139
-
140
-
141
- ### Return a list of the entries at the specified Subversion url. If
142
- ### no +url+ is specified, it will default to the list in the URL
143
- ### corresponding to the current working directory.
144
- def svn_ls( url=nil )
145
- url ||= get_svn_url()
146
- list = IO.read( '|-' ) or exec 'svn', 'ls', url
147
-
148
- trace 'svn ls of %s: %p' % [url, list] if $trace
149
-
150
- return [] if list.nil? || list.empty?
151
- return list.split( $INPUT_RECORD_SEPARATOR )
152
- end
153
-
154
-
155
- ### Return the URL of the latest timestamp in the tags directory.
156
- def get_latest_svn_timestamp_tag
157
- rooturl = get_svn_repo_root()
158
- tagsurl = rooturl + "/#{SVN_TAGS_DIR}"
159
-
160
- tags = svn_ls( tagsurl ).grep( TAG_TIMESTAMP_PATTERN ).sort
161
- return nil if tags.nil? || tags.empty?
162
- return tagsurl + '/' + tags.last
163
- end
164
-
165
-
166
- ### Get a subversion diff of the specified targets and return it. If no targets are
167
- ### specified, the current directory will be diffed instead.
168
- def get_svn_diff( *targets )
169
- targets << BASEDIR if targets.empty?
170
- trace "Getting svn diff for targets: %p" % [targets]
171
- log = IO.read( '|-' ) or exec 'svn', 'diff', *(targets.flatten)
172
-
173
- return log
174
- end
175
-
176
-
177
- ### Get a subversion status as an Array of tuples of the form:
178
- ### [ <status>, <path> ]
179
- def get_svn_status( *targets )
180
- targets << BASEDIR if targets.empty?
181
- trace "Getting svn status for targets: %p" % [targets]
182
- status = IO.read( '|-' ) or exec 'svn', 'st', '--ignore-externals', *(targets.flatten)
183
- entries = status.split( /\n/ ).
184
- select {|line| line !~ /^(\s*|--- .*)$/ }.
185
- collect do |line|
186
- flag, path = line.strip.split( /\s+/, 2 )
187
- [ flag, Pathname.new(path) ]
188
- end
189
-
190
- return entries
191
- end
192
-
193
-
194
- ### Return the URL of the latest timestamp in the tags directory.
195
- def get_latest_release_tag
196
- rooturl = get_svn_repo_root()
197
- releaseurl = rooturl + "/#{SVN_RELEASES_DIR}"
198
-
199
- tags = svn_ls( releaseurl ).grep( RELEASE_VERSION_PATTERN ).sort_by do |tag|
200
- tag[RELEASE_VERSION_PATTERN].split('.').collect {|i| Integer(i) }
201
- end
202
- return nil if tags.empty?
15
+ unless defined?( SVN_DOTDIR )
16
+
17
+ # Subversion constants -- directory names for releases and tags
18
+ SVN_TRUNK_DIR = 'trunk'
19
+ SVN_RELEASES_DIR = 'releases'
20
+ SVN_BRANCHES_DIR = 'branches'
21
+ SVN_TAGS_DIR = 'tags'
22
+
23
+ SVN_DOTDIR = BASEDIR + '.svn'
24
+ SVN_ENTRIES = SVN_DOTDIR + 'entries'
25
+
26
+ # Ignore .svn directories in the various FileLists used by the main Rakefile
27
+ [
28
+ BIN_FILES,
29
+ LIB_FILES,
30
+ EXT_FILES,
31
+ DATA_FILES,
32
+ SPEC_FILES,
33
+ TEST_FILES,
34
+ EXTRA_PKGFILES,
35
+ ].each {|filelist| filelist.exclude(/\.svn/) }
36
+
37
+ # Strftime format for tags/releases
38
+ TAG_TIMESTAMP_FORMAT = '%Y%m%d-%H%M%S'
39
+ TAG_TIMESTAMP_PATTERN = /\d{4}\d{2}\d{2}-\d{6}/
40
+
41
+ RELEASE_VERSION_PATTERN = /\d+\.\d+\.\d+/
42
+
43
+ DEFAULT_KEYWORDS = %w[Date Rev Author URL Id]
44
+ KEYWORDED_FILEDIRS = %w[applets spec bin etc ext experiments examples lib misc docs]
45
+ KEYWORDED_FILEPATTERN = /
46
+ ^(?:
47
+ (?:meta)?rakefile.* # Rakefiles
48
+ |
49
+ .*\.(?:rb|c|h|js|html|css|template|erb|page) # Source file extensions
50
+ |
51
+ readme|install|todo
52
+ )$/ix
53
+
54
+
55
+
56
+ ###
57
+ ### Subversion-specific Helpers
58
+ ###
59
+ module SubversionHelpers
60
+
61
+ ###############
62
+ module_function
63
+ ###############
64
+
65
+ ### Return a new tag for the given time
66
+ def make_new_tag( time=Time.now )
67
+ return time.strftime( TAG_TIMESTAMP_FORMAT )
68
+ end
69
+
70
+
71
+ ### Get the subversion information for the current working directory as
72
+ ### a hash.
73
+ def get_svn_info( dir='.' )
74
+ return {} unless File.directory?( File.join(dir, '.svn') )
75
+ info = IO.read( '|-' ) or exec 'svn', 'info', dir.to_s
76
+ return YAML.load( info ) # 'svn info' outputs valid YAML! Yay!
77
+ rescue NotImplementedError
78
+ trace "No fork(), proceeding without svn info..."
79
+ return {}
80
+ end
81
+
82
+
83
+ ### Get a list of the objects registered with subversion under the specified directory and
84
+ ### return them as an Array of Pathame objects.
85
+ def get_svn_filelist( dir='.' )
86
+ list = IO.read( '|-' ) or exec 'svn', 'st', '-v', '--ignore-externals', dir
87
+
88
+ # Split into lines, filter out the unknowns, and grab the filenames as Pathnames
89
+ # :FIXME: This will break if we ever put in a file with spaces in its name. This
90
+ # will likely be the least of our worries if we do so, however, so it's not worth
91
+ # the additional complexity to make it handle that case. If we do need that, there's
92
+ # always the --xml output for 'svn st'...
93
+ return list.split( $/ ).
94
+ reject {|line| line =~ /^(\?|(\s*|--- .*)$)/ }.
95
+ collect {|fn| Pathname(fn[/\S+$/]) }
96
+ end
203
97
 
204
- return releaseurl + '/' + tags.last
205
- end
98
+ ### Return the URL to the repository root for the specified +dir+.
99
+ def get_svn_repo_root( dir='.' )
100
+ info = get_svn_info( dir )
101
+ return info['Repository Root']
102
+ end
206
103
 
207
104
 
208
- ### Return the names of all existing branches.
209
- def get_branch_names
210
- rooturl = get_svn_repo_root()
211
- branchesurl = rooturl + "/#{SVN_BRANCHES_DIR}"
212
-
213
- return svn_ls( branchesurl )
214
- end
105
+ ### Return the Subversion URL to the given +dir+.
106
+ def get_svn_url( dir='.' )
107
+ info = get_svn_info( dir )
108
+ return info['URL']
109
+ end
215
110
 
216
111
 
217
- ### Extract a diff from the specified subversion working +dir+ and return it.
218
- def make_svn_commit_log( dir='.' )
219
- diff = IO.read( '|-' ) or exec 'svn', 'diff'
220
- fail "No differences." if diff.empty?
112
+ ### Return the path of the specified +dir+ under the svn root of the
113
+ ### checkout.
114
+ def get_svn_path( dir='.' )
115
+ root = get_svn_repo_root( dir )
116
+ url = get_svn_url( dir )
221
117
 
222
- return diff
223
- end
118
+ return url.sub( root + '/', '' )
119
+ end
224
120
 
225
121
 
226
- ### Extract the svn log from the specified subversion working +dir+,
227
- ### starting from rev +start+ and ending with rev +finish+, and return it.
228
- def make_svn_log( dir='.', start='PREV', finish='HEAD' )
229
- trace "svn log -r#{start}:#{finish} #{dir}"
230
- log = IO.read( '|-' ) or exec 'svn', 'log', "-r#{start}:#{finish}", dir
231
- fail "No log between #{start} and #{finish}." if log.empty?
122
+ ### Return the keywords for the specified array of +files+ as a Hash keyed by filename.
123
+ def get_svn_keyword_map( *files )
124
+ files.flatten!
125
+ files.push( '.' ) if files.empty?
232
126
 
233
- return log
234
- end
127
+ cmd = ['svn', 'pg', 'svn:keywords', *files]
235
128
 
129
+ # trace "Executing: svn pg svn:keywords " + files.join(' ')
130
+ output = IO.read( '|-' ) or exec( 'svn', 'pg', 'svn:keywords', *files )
236
131
 
237
- ### Extract the verbose XML svn log from the specified subversion working +dir+,
238
- ### starting from rev +start+ and ending with rev +finish+, and return it.
239
- def make_xml_svn_log( dir='.', start='PREV', finish='HEAD' )
240
- trace "svn log --xml --verbose -r#{start}:#{finish} #{dir}"
241
- log = IO.read( '|-' ) or exec 'svn', 'log', '--verbose', '--xml', "-r#{start}:#{finish}", dir
242
- fail "No log between #{start} and #{finish}." if log.empty?
132
+ kwmap = {}
133
+ output.split( "\n" ).each do |line|
134
+ next if line !~ /\s+-\s+/
135
+ path, keywords = line.split( /\s+-\s+/, 2 )
136
+ kwmap[ path ] = keywords.split
137
+ end
243
138
 
244
- return log
245
- end
139
+ return kwmap
140
+ end
246
141
 
247
142
 
248
- ### Create a changelog from the subversion log of the specified +dir+ and return it.
249
- def make_svn_changelog( dir='.' )
250
- require 'xml/libxml'
143
+ ### Return the latest revision number of the specified +dir+ as an Integer.
144
+ def get_svn_rev( dir='.' )
145
+ info = get_svn_info( dir )
146
+ return info['Revision']
147
+ end
148
+ alias get_vcs_rev get_svn_rev
251
149
 
252
- changelog = ''
253
- path_prefix = '/' + get_svn_path( dir ) + '/'
254
150
 
255
- xmllog = make_xml_svn_log( dir, 0 )
151
+ ### Return the latest revision number of the specified +dir+ as an Integer.
152
+ def get_last_changed_rev( dir='.' )
153
+ info = get_svn_info( dir )
154
+ return info['Last Changed Rev']
155
+ end
256
156
 
257
- parser = XML::Parser.string( xmllog )
258
- root = parser.parse.root
259
- root.find( '//log/logentry' ).to_a.reverse.each do |entry|
260
- trace "Making a changelog entry for r%s" % [ entry['revision'] ]
261
157
 
262
- added = []
263
- deleted = []
264
- changed = []
158
+ ### Return a list of the entries at the specified Subversion url. If
159
+ ### no +url+ is specified, it will default to the list in the URL
160
+ ### corresponding to the current working directory.
161
+ def svn_ls( url=nil )
162
+ url ||= get_svn_url()
163
+ list = IO.read( '|-' ) or exec 'svn', 'ls', url
265
164
 
266
- entry.find( 'paths/path').each do |path|
267
- pathname = path.content
268
- pathname.sub!( path_prefix , '' ) if pathname.count('/') > 1
165
+ trace 'svn ls of %s: %p' % [url, list] if $trace
269
166
 
270
- case path['action']
271
- when 'A', 'R'
272
- if path['copyfrom-path']
273
- verb = path['action'] == 'A' ? 'renamed' : 'copied'
274
- added << "%s\n#{FILE_INDENT}-> #{verb} from %s@r%s" % [
275
- pathname,
276
- path['copyfrom-path'],
277
- path['copyfrom-rev'],
278
- ]
279
- else
280
- added << "%s (new)" % [ pathname ]
281
- end
282
-
283
- when 'M'
284
- changed << pathname
285
-
286
- when 'D'
287
- deleted << pathname
288
-
289
- else
290
- log "Unknown action %p in rev %d" % [ path['action'], entry['revision'] ]
167
+ return [] if list.nil? || list.empty?
168
+ return list.split( $INPUT_RECORD_SEPARATOR )
169
+ end
170
+
171
+
172
+ ### Return the URL of the latest timestamp in the tags directory.
173
+ def get_latest_svn_timestamp_tag
174
+ rooturl = get_svn_repo_root()
175
+ tagsurl = rooturl + "/#{SVN_TAGS_DIR}"
176
+
177
+ tags = svn_ls( tagsurl ).grep( TAG_TIMESTAMP_PATTERN ).sort
178
+ return nil if tags.nil? || tags.empty?
179
+ return tagsurl + '/' + tags.last
180
+ end
181
+
182
+
183
+ ### Get a subversion diff of the specified targets and return it. If no targets are
184
+ ### specified, the current directory will be diffed instead.
185
+ def get_svn_diff( *targets )
186
+ targets << BASEDIR if targets.empty?
187
+ trace "Getting svn diff for targets: %p" % [targets]
188
+ log = IO.read( '|-' ) or exec 'svn', 'diff', *(targets.flatten)
189
+
190
+ return log
191
+ end
192
+
193
+
194
+ ### Generate a commit log and invoke the user's editor on it.
195
+ def edit_commit_log
196
+ diff = make_commit_log()
197
+
198
+ File.open( COMMIT_MSG_FILE, File::WRONLY|File::TRUNC|File::CREAT ) do |fh|
199
+ fh.print( diff )
291
200
  end
292
-
293
- end
294
-
295
- date = Time.parse( entry.find_first('date').content )
296
-
297
- # cvs2svn doesn't set 'author'
298
- author = 'unknown'
299
- if entry.find_first( 'author' )
300
- author = entry.find_first( 'author' ).content
301
- end
302
-
303
- msg = entry.find_first( 'msg' ).content
304
- rev = entry['revision']
305
-
306
- changelog << "-- #{date.rfc2822} by #{author} (r#{rev}) -----\n"
307
- changelog << " Added: " << humanize_file_list(added) << "\n" unless added.empty?
308
- changelog << " Changed: " << humanize_file_list(changed) << "\n" unless changed.empty?
309
- changelog << " Deleted: " << humanize_file_list(deleted) << "\n" unless deleted.empty?
310
- changelog << "\n"
311
-
312
- indent = msg[/^(\s*)/] + LOG_INDENT
313
-
314
- changelog << indent << msg.strip.gsub(/\n\s*/m, "\n#{indent}")
315
- changelog << "\n\n\n"
316
- end
317
-
318
- return changelog
319
- end
320
201
 
202
+ edit( COMMIT_MSG_FILE )
203
+ end
321
204
 
322
- ### Returns a human-scannable file list by joining and truncating the list if it's too long.
323
- def humanize_file_list( list, indent=FILE_INDENT )
324
- listtext = list[0..5].join( "\n#{indent}" )
325
- if list.length > 5
326
- listtext << " (and %d other/s)" % [ list.length - 5 ]
327
- end
328
-
329
- return listtext
330
- end
331
205
 
206
+ ### Get a subversion status as an Array of tuples of the form:
207
+ ### [ <status>, <path> ]
208
+ def get_svn_status( *targets )
209
+ targets << BASEDIR if targets.empty?
210
+ trace "Getting svn status for targets: %p" % [targets]
211
+ status = IO.read( '|-' ) or exec 'svn', 'st', '--ignore-externals', *(targets.flatten)
212
+ entries = status.split( /\n/ ).
213
+ select {|line| line !~ /^(\s*|--- .*)$/ }.
214
+ collect do |line|
215
+ flag, path = line.strip.split( /\s+/, 2 )
216
+ [ flag, Pathname.new(path) ]
217
+ end
332
218
 
333
- ### Add the list of +pathnames+ to the svn:ignore list.
334
- def svn_ignore_files( *pathnames )
335
- pathnames.flatten!
219
+ return entries
220
+ end
336
221
 
337
- map = pathnames.inject({}) do |map,path|
338
- map[ path.dirname ] ||= []
339
- map[ path.dirname ] << path.basename
340
- map
341
- end
342
222
 
343
- trace "Ignoring %d files in %d directories." % [ pathnames.length, map.length ]
223
+ ### Return the URL of the latest timestamp in the tags directory.
224
+ def get_latest_release_tag
225
+ rooturl = get_svn_repo_root()
226
+ releaseurl = rooturl + "/#{SVN_RELEASES_DIR}"
344
227
 
345
- map.each do |dir, files|
346
- trace " %s: %p" % [ dir, files ]
347
- io = open( '|-' ) or exec 'svn', 'pg', 'svn:ignore', dir
348
- ignorelist = io.read.strip
349
- ignorelist << "\n" << files.join("\n")
350
- system 'svn', 'ps', 'svn:ignore', ignorelist, dir
351
- end
352
- end
228
+ tags = svn_ls( releaseurl ).grep( RELEASE_VERSION_PATTERN ).sort_by do |tag|
229
+ tag[RELEASE_VERSION_PATTERN].split('.').collect {|i| Integer(i) }
230
+ end
231
+ return nil if tags.empty?
232
+
233
+ return releaseurl + '/' + tags.last
234
+ end
353
235
 
354
236
 
355
- ### Delete the files in the given +filelist+ after confirming with the user.
356
- def delete_extra_files( filelist )
357
- description = humanize_file_list( filelist, ' ' )
358
- log "Files to delete:\n ", description
359
- ask_for_confirmation( "Really delete them?", false ) do
360
- filelist.each do |f|
361
- rm_rf( f, :verbose => true )
237
+ ### Return the names of all existing branches.
238
+ def get_branch_names
239
+ rooturl = get_svn_repo_root()
240
+ branchesurl = rooturl + "/#{SVN_BRANCHES_DIR}"
241
+
242
+ return svn_ls( branchesurl )
362
243
  end
363
- end
364
- end
365
244
 
366
245
 
246
+ ### Extract a diff from the specified subversion working +dir+ and return it.
247
+ def make_svn_commit_log( dir='.' )
248
+ diff = IO.read( '|-' ) or exec 'svn', 'diff'
249
+ fail "No differences." if diff.empty?
367
250
 
368
- ###
369
- ### Tasks
370
- ###
251
+ return diff
252
+ end
371
253
 
372
- desc "Subversion tasks"
373
- namespace :svn do
374
254
 
375
- desc "Copy the HEAD revision of the current #{SVN_TRUNK_DIR}/ to #{SVN_TAGS_DIR}/ with a " +
376
- "current timestamp."
377
- task :tag do
378
- svninfo = get_svn_info()
379
- tag = make_new_tag()
380
- svntrunk = svninfo['Repository Root'] + "/#{SVN_TRUNK_DIR}"
381
- svntagdir = svninfo['Repository Root'] + "/#{SVN_TAGS_DIR}"
382
- svntag = svntagdir + '/' + tag
255
+ ### Extract the svn log from the specified subversion working +dir+,
256
+ ### starting from rev +start+ and ending with rev +finish+, and return it.
257
+ def make_svn_log( dir='.', start='PREV', finish='HEAD' )
258
+ trace "svn log -r#{start}:#{finish} #{dir}"
259
+ log = IO.read( '|-' ) or exec 'svn', 'log', "-r#{start}:#{finish}", dir
260
+ fail "No log between #{start} and #{finish}." if log.empty?
383
261
 
384
- desc = "Tagging trunk as #{svntag}"
385
- ask_for_confirmation( desc ) do
386
- msg = prompt_with_default( "Commit log: ", "Tagging for code push" )
387
- run 'svn', 'cp', '-m', msg, svntrunk, svntag
262
+ return log
388
263
  end
389
- end
390
264
 
391
265
 
392
- desc "Copy the HEAD revision of the current #{SVN_TRUNK_DIR}/ to #{SVN_BRANCHES_DIR} with a " +
393
- "user-specified name."
394
- task :branch, [:name] do |task, args|
395
- branchname = args.name
396
- unless branchname
397
- branchname = prompt( "Branch name" ) or abort
398
- end
399
-
400
- svninfo = get_svn_info()
401
- svntrunk = Pathname.new( svninfo['Repository Root'] ) + SVN_TRUNK_DIR
402
- svnbranchdir = Pathname.new( svninfo['Repository Root'] ) + SVN_BRANCHES_DIR
403
- svnbranch = svnbranchdir + branchname
404
-
405
- desc = "Making a new branch: #{svnbranch}"
406
- ask_for_confirmation( desc ) do
407
- msg = prompt_with_default( "Commit log: ", "Making a '#{args.name}' branch" )
408
- run 'svn', 'cp', '-m', msg, svntrunk, svnbranch
409
- ask_for_confirmation( "Switch to the new branch?", false ) do
410
- run 'svn', 'sw', svnbranch
266
+ ### Extract the verbose XML svn log from the specified subversion working +dir+,
267
+ ### starting from rev +start+ and ending with rev +finish+, and return it.
268
+ def make_xml_svn_log( dir='.', start='PREV', finish='HEAD' )
269
+ trace "svn log --xml --verbose -r#{start}:#{finish} #{dir}"
270
+ log = IO.read( '|-' ) or exec 'svn', 'log', '--verbose', '--xml', "-r#{start}:#{finish}", dir
271
+ fail "No log between #{start} and #{finish}." if log.empty?
272
+
273
+ return log
274
+ end
275
+
276
+
277
+ ### Create a changelog from the subversion log of the specified +dir+ and return it.
278
+ def make_svn_changelog( dir='.' )
279
+ require 'xml/libxml'
280
+
281
+ changelog = ''
282
+ path_prefix = '/' + get_svn_path( dir ) + '/'
283
+
284
+ xmllog = make_xml_svn_log( dir, 0 )
285
+
286
+ parser = XML::Parser.string( xmllog )
287
+ root = parser.parse.root
288
+ root.find( '//log/logentry' ).to_a.reverse.each do |entry|
289
+ trace "Making a changelog entry for r%s" % [ entry['revision'] ]
290
+
291
+ added = []
292
+ deleted = []
293
+ changed = []
294
+
295
+ entry.find( 'paths/path').each do |path|
296
+ pathname = path.content
297
+ pathname.sub!( path_prefix , '' ) if pathname.count('/') > 1
298
+
299
+ case path['action']
300
+ when 'A', 'R'
301
+ if path['copyfrom-path']
302
+ verb = path['action'] == 'A' ? 'renamed' : 'copied'
303
+ added << "%s\n#{FILE_INDENT}-> #{verb} from %s@r%s" % [
304
+ pathname,
305
+ path['copyfrom-path'],
306
+ path['copyfrom-rev'],
307
+ ]
308
+ else
309
+ added << "%s (new)" % [ pathname ]
310
+ end
311
+
312
+ when 'M'
313
+ changed << pathname
314
+
315
+ when 'D'
316
+ deleted << pathname
317
+
318
+ else
319
+ log "Unknown action %p in rev %d" % [ path['action'], entry['revision'] ]
320
+ end
321
+
322
+ end
323
+
324
+ date = Time.parse( entry.find_first('date').content )
325
+
326
+ # cvs2svn doesn't set 'author'
327
+ author = 'unknown'
328
+ if entry.find_first( 'author' )
329
+ author = entry.find_first( 'author' ).content
330
+ end
331
+
332
+ msg = entry.find_first( 'msg' ).content
333
+ rev = entry['revision']
334
+
335
+ changelog << "-- #{date.rfc2822} by #{author} (r#{rev}) -----\n"
336
+ changelog << " Added: " << humanize_file_list(added) << "\n" unless added.empty?
337
+ changelog << " Changed: " << humanize_file_list(changed) << "\n" unless changed.empty?
338
+ changelog << " Deleted: " << humanize_file_list(deleted) << "\n" unless deleted.empty?
339
+ changelog << "\n"
340
+
341
+ indent = msg[/^(\s*)/] + LOG_INDENT
342
+
343
+ changelog << indent << msg.strip.gsub(/\n\s*/m, "\n#{indent}")
344
+ changelog << "\n\n\n"
411
345
  end
346
+
347
+ return changelog
412
348
  end
413
- end
414
-
415
-
416
- desc "Switch the working copy to the named branch"
417
- task :switch, [:name] do |task, args|
418
- branches = get_branch_names().collect {|name| name.chomp('/') }
419
-
420
- unless args.name
421
- log "Branches are:\n" + branches.collect {|br| " #{br}" }.join( "\n" )
422
-
423
- begin
424
- oldproc = Readline.completion_proc
425
- abbrev = branches.abbrev
426
- Readline.completion_proc = lambda{|string| abbrev[string] }
427
-
428
- name = prompt( "Branch to switch to" ) or abort
429
- args.with_defaults( :name => name )
430
- ensure
431
- Readline.completion_proc = oldproc unless oldproc.nil?
349
+
350
+
351
+ ### Returns a human-scannable file list by joining and truncating the list if it's too long.
352
+ def humanize_file_list( list, indent=FILE_INDENT )
353
+ listtext = list[0..5].join( "\n#{indent}" )
354
+ if list.length > 5
355
+ listtext << " (and %d other/s)" % [ list.length - 5 ]
432
356
  end
357
+
358
+ return listtext
433
359
  end
434
360
 
435
- svninfo = get_svn_info()
436
- abort "Branch '#{args.name}' does not exist" unless branches.include?( args.name )
437
- branchuri = Pathname.new( svninfo['Repository Root'] ) + SVN_BRANCHES_DIR + args.name
438
- run 'svn', 'sw', branchuri
439
- end
440
- task :sw => :switch
441
-
442
-
443
- desc "Switch to the trunk if the working copy isn't there already."
444
- task :trunk do
445
- svninfo = get_svn_info()
446
- svntrunk = Pathname.new( svninfo['Repository Root'] ) + SVN_TRUNK_DIR
447
-
448
- if svninfo['URL'] != svntrunk.to_s
449
- log "Switching to #{svntrunk}"
450
- run 'svn', 'sw', svntrunk
451
- else
452
- log "You are already on trunk (#{svntrunk})"
361
+
362
+ ### Add the list of +pathnames+ to the svn:ignore list.
363
+ def svn_ignore_files( *pathnames )
364
+ pathnames.flatten!
365
+
366
+ map = pathnames.inject({}) do |map,path|
367
+ map[ path.dirname ] ||= []
368
+ map[ path.dirname ] << path.basename
369
+ map
370
+ end
371
+
372
+ trace "Ignoring %d files in %d directories." % [ pathnames.length, map.length ]
373
+
374
+ map.each do |dir, files|
375
+ trace " %s: %p" % [ dir, files ]
376
+ io = open( '|-' ) or exec 'svn', 'pg', 'svn:ignore', dir
377
+ ignorelist = io.read.strip
378
+ ignorelist << "\n" << files.join("\n")
379
+ system 'svn', 'ps', 'svn:ignore', ignorelist, dir
380
+ end
453
381
  end
454
- end
455
-
456
-
457
- desc "Copy the most recent tag to #{SVN_RELEASES_DIR}/#{PKG_VERSION}"
458
- task :release do
459
- last_tag = get_latest_svn_timestamp_tag()
460
- svninfo = get_svn_info()
461
- svnroot = Pathname.new( svninfo['Repository Root'] )
462
- svntrunk = svnroot + SVN_TRUNK_DIR
463
- svnrel = svnroot + SVN_RELEASES_DIR
464
- release = PKG_VERSION
465
- svnrelease = svnrel + release
466
-
467
- unless svn_ls( svnrel.dirname ).include?( svnrel.basename.to_s + '/' )
468
- log "Releases path #{svnrel} does not exist."
469
- ask_for_confirmation( "To continue I'll need to create it." ) do
470
- run 'svn', 'mkdir', svnrel, '-m', 'Creating releases/ directory'
382
+
383
+
384
+ ### Delete the files in the given +filelist+ after confirming with the user.
385
+ def delete_extra_files( filelist )
386
+ description = humanize_file_list( filelist, ' ' )
387
+ log "Files to delete:\n ", description
388
+ ask_for_confirmation( "Really delete them?", false ) do
389
+ filelist.each do |f|
390
+ rm_rf( f, :verbose => true )
391
+ end
471
392
  end
472
- else
473
- trace "Found release dir #{svnrel}"
474
393
  end
475
394
 
476
- releases = svn_ls( svnrel ).collect {|name| name.sub(%r{/$}, '') }
477
- trace "Releases: %p" % [releases]
478
- if releases.include?( release )
479
- error "Version #{release} already has a branch (#{svnrelease}). Did you mean " +
480
- "to increment the version in #{VERSION_FILE}?"
481
- fail
482
- else
483
- trace "No #{release} version currently exists"
395
+ end # module SubversionHelpers
396
+
397
+
398
+ ###
399
+ ### Tasks
400
+ ###
401
+
402
+ desc "Subversion tasks"
403
+ namespace :svn do
404
+ include SubversionHelpers
405
+
406
+ desc "Copy the HEAD revision of the current #{SVN_TRUNK_DIR}/ to #{SVN_TAGS_DIR}/ with a " +
407
+ "current timestamp."
408
+ task :tag do
409
+ svninfo = get_svn_info()
410
+ tag = make_new_tag()
411
+ svntrunk = svninfo['Repository Root'] + "/#{SVN_TRUNK_DIR}"
412
+ svntagdir = svninfo['Repository Root'] + "/#{SVN_TAGS_DIR}"
413
+ svntag = svntagdir + '/' + tag
414
+
415
+ desc = "Tagging trunk as #{svntag}"
416
+ ask_for_confirmation( desc ) do
417
+ msg = prompt_with_default( "Commit log: ", "Tagging for code push" )
418
+ run 'svn', 'cp', '-m', msg, svntrunk, svntag
419
+ end
484
420
  end
485
-
486
- desc = "Tagging trunk as #{svnrelease}..."
487
- ask_for_confirmation( desc ) do
488
- msg = prompt_with_default( "Commit log: ", "Branching for release" )
489
- run 'svn', 'cp', '-m', msg, svntrunk, svnrelease
421
+
422
+
423
+ desc "Copy the HEAD revision of the current #{SVN_TRUNK_DIR}/ to #{SVN_BRANCHES_DIR} with a " +
424
+ "user-specified name."
425
+ task :branch, [:name] do |task, args|
426
+ branchname = args.name
427
+ unless branchname
428
+ branchname = prompt( "Branch name" ) or abort
429
+ end
430
+
431
+ svninfo = get_svn_info()
432
+ svntrunk = Pathname.new( svninfo['Repository Root'] ) + SVN_TRUNK_DIR
433
+ svnbranchdir = Pathname.new( svninfo['Repository Root'] ) + SVN_BRANCHES_DIR
434
+ svnbranch = svnbranchdir + branchname
435
+
436
+ desc = "Making a new branch: #{svnbranch}"
437
+ ask_for_confirmation( desc ) do
438
+ msg = prompt_with_default( "Commit log: ", "Making a '#{args.name}' branch" )
439
+ run 'svn', 'cp', '-m', msg, svntrunk, svnbranch
440
+ ask_for_confirmation( "Switch to the new branch?", false ) do
441
+ run 'svn', 'sw', svnbranch
442
+ end
443
+ end
490
444
  end
491
- end
492
445
 
493
- ### Task for debugging the #get_target_args helper
494
- task :show_targets do
495
- $stdout.puts "Targets from ARGV (%p): %p" % [ARGV, get_target_args()]
496
- end
497
446
 
447
+ desc "Switch the working copy to the named branch"
448
+ task :switch, [:name] do |task, args|
449
+ branches = get_branch_names().collect {|name| name.chomp('/') }
498
450
 
499
- desc "Generate a commit log"
500
- task :commitlog => [COMMIT_MSG_FILE]
501
-
502
- desc "Show the (pre-edited) commit log for the current directory"
503
- task :show_commitlog do
504
- puts make_svn_commit_log()
505
- end
506
-
451
+ unless args.name
452
+ log "Branches are:\n" + branches.collect {|br| " #{br}" }.join( "\n" )
507
453
 
508
- file COMMIT_MSG_FILE do
509
- diff = make_svn_commit_log()
510
-
511
- File.open( COMMIT_MSG_FILE, File::WRONLY|File::EXCL|File::CREAT ) do |fh|
512
- fh.print( diff )
513
- end
454
+ begin
455
+ oldproc = Readline.completion_proc
456
+ abbrev = branches.abbrev
457
+ Readline.completion_proc = lambda{|string| abbrev[string] }
458
+
459
+ name = prompt( "Branch to switch to" ) or abort
460
+ args.with_defaults( :name => name )
461
+ ensure
462
+ Readline.completion_proc = oldproc unless oldproc.nil?
463
+ end
464
+ end
514
465
 
515
- editor = ENV['EDITOR'] || ENV['VISUAL'] || DEFAULT_EDITOR
516
- system editor, COMMIT_MSG_FILE
517
- unless $?.success?
518
- fail "Editor exited uncleanly."
466
+ svninfo = get_svn_info()
467
+ abort "Branch '#{args.name}' does not exist" unless branches.include?( args.name )
468
+ branchuri = Pathname.new( svninfo['Repository Root'] ) + SVN_BRANCHES_DIR + args.name
469
+ run 'svn', 'sw', branchuri
519
470
  end
520
- end
471
+ task :sw => :switch
521
472
 
522
473
 
523
- desc "Update from Subversion"
524
- task :update do
525
- run 'svn', 'up', '--ignore-externals'
526
- end
474
+ desc "Switch to the trunk if the working copy isn't there already."
475
+ task :trunk do
476
+ svninfo = get_svn_info()
477
+ svntrunk = Pathname.new( svninfo['Repository Root'] ) + SVN_TRUNK_DIR
478
+
479
+ if svninfo['URL'] != svntrunk.to_s
480
+ log "Switching to #{svntrunk}"
481
+ run 'svn', 'sw', svntrunk
482
+ else
483
+ log "You are already on trunk (#{svntrunk})"
484
+ end
485
+ end
527
486
 
528
487
 
529
- desc "Add/ignore any files that are unknown in the working copy"
530
- task :newfiles do
531
- log "Checking for new files..."
532
- entries = get_svn_status()
533
-
534
- unless entries.empty?
535
- files_to_add = []
536
- files_to_ignore = []
537
- files_to_delete = []
538
-
539
- entries.find_all {|entry| entry[0] == '?'}.each do |entry|
540
- action = prompt_with_default( " #{entry[1]}: (a)dd, (i)gnore, (s)kip (d)elete", 's' )
541
- case action
542
- when 'a'
543
- files_to_add << entry[1]
544
- when 'i'
545
- files_to_ignore << entry[1]
546
- when 'd'
547
- files_to_delete << entry[1]
488
+ desc "Copy the most recent tag to #{SVN_RELEASES_DIR}/#{PKG_VERSION}"
489
+ task :prep_release do
490
+ last_tag = get_latest_svn_timestamp_tag()
491
+ svninfo = get_svn_info()
492
+ svnroot = Pathname.new( svninfo['Repository Root'] )
493
+ svntrunk = svnroot + SVN_TRUNK_DIR
494
+ svnrel = svnroot + SVN_RELEASES_DIR
495
+ release = PKG_VERSION
496
+ svnrelease = svnrel + release
497
+
498
+ unless svn_ls( svnrel.dirname ).include?( svnrel.basename.to_s + '/' )
499
+ log "Releases path #{svnrel} does not exist."
500
+ ask_for_confirmation( "To continue I'll need to create it." ) do
501
+ run 'svn', 'mkdir', svnrel, '-m', 'Creating releases/ directory'
548
502
  end
503
+ else
504
+ trace "Found release dir #{svnrel}"
549
505
  end
550
-
551
- unless files_to_add.empty?
552
- run 'svn', 'add', *files_to_add
506
+
507
+ releases = svn_ls( svnrel ).collect {|name| name.sub(%r{/$}, '') }
508
+ trace "Releases: %p" % [releases]
509
+ if releases.include?( release )
510
+ error "Version #{release} already has a branch (#{svnrelease}). Did you mean " +
511
+ "to increment the version in #{VERSION_FILE}?"
512
+ fail
513
+ else
514
+ trace "No #{release} version currently exists"
515
+ end
516
+
517
+ desc = "Tagging trunk as #{svnrelease}..."
518
+ ask_for_confirmation( desc ) do
519
+ msg = prompt_with_default( "Commit log: ", "Branching for release" )
520
+ run 'svn', 'cp', '-m', msg, svntrunk, svnrelease
553
521
  end
554
-
555
- unless files_to_ignore.empty?
556
- svn_ignore_files( *files_to_ignore )
522
+ end
523
+
524
+ ### Task for debugging the #get_target_args helper
525
+ task :show_targets do
526
+ $stdout.puts "Targets from ARGV (%p): %p" % [ARGV, get_target_args()]
527
+ end
528
+
529
+
530
+ desc "Update from Subversion"
531
+ task :update do
532
+ run 'svn', 'up', '--ignore-externals'
533
+ end
534
+
535
+
536
+ desc "Add/ignore any files that are unknown in the working copy"
537
+ task :newfiles do
538
+ log "Checking for new files..."
539
+ entries = get_svn_status()
540
+
541
+ unless entries.empty?
542
+ files_to_add = []
543
+ files_to_ignore = []
544
+ files_to_delete = []
545
+
546
+ entries.find_all {|entry| entry[0] == '?'}.each do |entry|
547
+ action = prompt_with_default( " #{entry[1]}: (a)dd, (i)gnore, (s)kip (d)elete", 's' )
548
+ case action
549
+ when 'a'
550
+ files_to_add << entry[1]
551
+ when 'i'
552
+ files_to_ignore << entry[1]
553
+ when 'd'
554
+ files_to_delete << entry[1]
555
+ end
556
+ end
557
+
558
+ unless files_to_add.empty?
559
+ run 'svn', 'add', *files_to_add
560
+ end
561
+
562
+ unless files_to_ignore.empty?
563
+ svn_ignore_files( *files_to_ignore )
564
+ end
565
+
566
+ unless files_to_delete.empty?
567
+ delete_extra_files( files_to_delete )
568
+ end
557
569
  end
570
+ end
571
+ task :add => :newfiles
558
572
 
559
- unless files_to_delete.empty?
560
- delete_extra_files( files_to_delete )
573
+
574
+ desc "Check in all the changes in your current working copy"
575
+ task :checkin => ['svn:update', 'svn:newfiles', 'test', 'svn:fix_keywords', COMMIT_MSG_FILE] do
576
+ targets = get_target_args()
577
+ $stderr.puts '---', File.read( COMMIT_MSG_FILE ), '---'
578
+ ask_for_confirmation( "Continue with checkin?" ) do
579
+ run 'svn', 'ci', '-F', COMMIT_MSG_FILE, targets
580
+ rm_f COMMIT_MSG_FILE
561
581
  end
562
582
  end
563
- end
564
- task :add => :newfiles
565
-
566
-
567
- desc "Check in all the changes in your current working copy"
568
- task :checkin => ['svn:update', 'svn:newfiles', 'test', 'svn:fix_keywords', COMMIT_MSG_FILE] do
569
- targets = get_target_args()
570
- $stderr.puts '---', File.read( COMMIT_MSG_FILE ), '---'
571
- ask_for_confirmation( "Continue with checkin?" ) do
572
- run 'svn', 'ci', '-F', COMMIT_MSG_FILE, targets
583
+ task :commit => :checkin
584
+ task :ci => :checkin
585
+
586
+
587
+ task :clean do
573
588
  rm_f COMMIT_MSG_FILE
574
589
  end
575
- end
576
- task :commit => :checkin
577
- task :ci => :checkin
578
-
579
-
580
- task :clean do
581
- rm_f COMMIT_MSG_FILE
582
- end
583
590
 
584
-
585
- desc "Check and fix any missing keywords for any files in the project which need them"
586
- task :fix_keywords do
587
- log "Checking subversion keywords..."
588
- paths = get_svn_filelist( BASEDIR ).
589
- select {|path| path.file? && path.to_s =~ KEYWORDED_FILEPATTERN }
590
-
591
- trace "Looking at %d paths for keywords:\n %p" % [paths.length, paths]
592
- kwmap = get_svn_keyword_map( paths )
593
-
594
- buf = ''
595
- PP.pp( kwmap, buf, 132 )
596
- trace "keyword map is: %s" % [buf]
597
-
598
- files_needing_fixups = paths.find_all do |path|
599
- (kwmap[path.to_s] & DEFAULT_KEYWORDS) != DEFAULT_KEYWORDS
600
- end
601
-
602
- unless files_needing_fixups.empty?
603
- $stderr.puts "Files needing keyword fixes: ",
604
- files_needing_fixups.collect {|f|
605
- " %s: %s" % [f, kwmap[f] ? kwmap[f].join(' ') : "(no keywords)"]
606
- }
607
- ask_for_confirmation( "Will add default keywords to these files." ) do
608
- run 'svn', 'ps', 'svn:keywords', DEFAULT_KEYWORDS.join(' '), *files_needing_fixups
591
+
592
+ desc "Check and fix any missing keywords for any files in the project which need them"
593
+ task :fix_keywords do
594
+ log "Checking subversion keywords..."
595
+ paths = get_svn_filelist( BASEDIR ).
596
+ select {|path| path.file? && path.to_s =~ KEYWORDED_FILEPATTERN }
597
+
598
+ trace "Looking at %d paths for keywords:\n %p" % [paths.length, paths]
599
+ kwmap = get_svn_keyword_map( paths )
600
+
601
+ buf = ''
602
+ PP.pp( kwmap, buf, 132 )
603
+ trace "keyword map is: %s" % [buf]
604
+
605
+ files_needing_fixups = paths.find_all do |path|
606
+ (kwmap[path.to_s] & DEFAULT_KEYWORDS) != DEFAULT_KEYWORDS
607
+ end
608
+
609
+ unless files_needing_fixups.empty?
610
+ $stderr.puts "Files needing keyword fixes: ",
611
+ files_needing_fixups.collect {|f|
612
+ " %s: %s" % [f, kwmap[f] ? kwmap[f].join(' ') : "(no keywords)"]
613
+ }
614
+ ask_for_confirmation( "Will add default keywords to these files." ) do
615
+ run 'svn', 'ps', 'svn:keywords', DEFAULT_KEYWORDS.join(' '), *files_needing_fixups
616
+ end
617
+ else
618
+ log "Keywords are all up to date."
609
619
  end
610
- else
611
- log "Keywords are all up to date."
612
620
  end
621
+
622
+
623
+ task :debug_helpers do
624
+ methods = [
625
+ :get_last_changed_rev,
626
+ :get_latest_release_tag,
627
+ :get_latest_svn_timestamp_tag,
628
+ :get_svn_diff,
629
+ :get_svn_filelist,
630
+ :get_svn_info,
631
+ :get_svn_keyword_map,
632
+ :get_svn_path,
633
+ :get_svn_repo_root,
634
+ :get_svn_rev,
635
+ :get_svn_status,
636
+ :get_svn_url,
637
+ :svn_ls,
638
+ ]
639
+ maxlen = methods.collect {|sym| sym.to_s.length }.max
640
+
641
+ methods.each do |meth|
642
+ res = send( meth )
643
+ puts "%*s => %p" % [ maxlen, colorize(meth.to_s, :cyan), res ]
644
+ end
645
+ end
646
+
613
647
  end
614
648
 
615
-
616
- task :debug_helpers do
617
- methods = [
618
- :get_last_changed_rev,
619
- :get_latest_release_tag,
620
- :get_latest_svn_timestamp_tag,
621
- :get_svn_diff,
622
- :get_svn_filelist,
623
- :get_svn_info,
624
- :get_svn_keyword_map,
625
- :get_svn_path,
626
- :get_svn_repo_root,
627
- :get_svn_rev,
628
- :get_svn_status,
629
- :get_svn_url,
630
- :svn_ls,
631
- ]
632
- maxlen = methods.collect {|sym| sym.to_s.length }.max
633
-
634
- methods.each do |meth|
635
- res = send( meth )
636
- puts "%*s => %p" % [ maxlen, colorize(meth.to_s, :cyan), res ]
649
+ if SVN_DOTDIR.exist?
650
+ trace "Defining subversion VCS tasks"
651
+
652
+ desc "Check in all the changes in your current working copy"
653
+ task :ci => 'svn:ci'
654
+ desc "Check in all the changes in your current working copy"
655
+ task :checkin => 'svn:ci'
656
+
657
+ desc "Tag a release"
658
+ task :prep_release => 'svn:prep_release'
659
+
660
+ file COMMIT_MSG_FILE do
661
+ edit_commit_log()
637
662
  end
663
+ else
664
+ trace "Not defining subversion tasks: no #{SVN_DOTDIR}"
638
665
  end
639
- end
666
+
667
+ end # unless defined?( SVN_DOTDIR )
640
668