#!/usr/bin/env python # # Adds missing svn:needs-lock property directly on repository files. # Direct access to the repository is required. # # Specify -d to perform a "dry run". This just indicates what needs to be done. # Specify -i REGEXP to only include file names matching the regular expression. # (Defaults to all files) # Specify -e REGEXP to exclude file names matching the regular expression. # Specify -r REV to operate only on the files added in the specified revision. # # Example: Add the svn:needs-lock property to any non .c files in /trunk # # add-needs-lock.py -i "/trunk/.*" -e ".*\.c" /path/to/repo # # # Copyright 2008 Kevin Radke # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # $HeadURL: http://svn.apache.org/repos/asf/subversion/branches/1.6.x/contrib/server-side/add-needs-lock.py $ # $LastChangedDate: 2008-07-30 15:08:35 +0000 (Wed, 30 Jul 2008) $ # $LastChangedBy: blair $ # $LastChangedRevision: 872408 $ import sys import os import re import getopt try: my_getopt = getopt.gnu_getopt except AttributeError: my_getopt = getopt.getopt try: import svn.core import svn.fs import svn.repos except ImportError, e: print >> sys.stderr, "ERROR: Unable to import Subversion's Python bindings: '%s'" % e sys.exit(1) # Walk a tree returning file paths ################################################################################ def walk_tree(root, path): files = [] for name in svn.fs.dir_entries(root, path).keys(): full = path + '/' + name if svn.fs.is_dir(root, full): subfiles = walk_tree(root, full) for subfile in subfiles: files.append(subfile) else: files.append(full) return files # Get a list of files ################################################################################ def get_file_list(root, included, excluded): files = [] regexp = re.compile(included) regexpout = re.compile(excluded) all_files = walk_tree(root, '') for path in all_files: # Must match include and not match exclude regexp if regexp.match(path) and not regexpout.match(path): files.append(path) return files # Get a list of files added in the specified revision ################################################################################ def get_rev_file_list(revroot, included, excluded): files = [] regexp = re.compile(included) regexpout = re.compile(excluded) for path, change in svn.fs.paths_changed(revroot).iteritems(): # Must be an add or replace if (change.change_kind == svn.fs.path_change_add or change.change_kind == svn.fs.path_change_replace): # Must be a file if (svn.fs.check_path(revroot, path) == svn.core.svn_node_file): # Must match include and not match exclude regexp if regexp.match(path) and not regexpout.match(path): files.append(path) return files # Add missing svn:needs-lock to any files directly in the repository ################################################################################ def addneedslock(repos_path, uname='', commitmsg='', included='.*', excluded='^$', rev=None, dryrun=None): canon_path = svn.core.svn_path_canonicalize(repos_path) repos_ptr = svn.repos.open(canon_path) fsob = svn.repos.fs(repos_ptr) # Get the HEAD revision headrev = svn.fs.youngest_rev(fsob) root = svn.fs.revision_root(fsob, headrev) if rev is None: # Get list of all latest files in repository files = get_file_list(root, included, excluded) else: # Get list of all files changed in the revision revroot = svn.fs.revision_root(fsob, rev) files = get_rev_file_list(revroot, included, excluded) interesting_files = [] print 'Searching ' + str(len(files)) + ' file(s)...' for path in files: locked_val = svn.fs.get_lock(fsob, path) # Must not be locked if locked_val is None: needslock_prop_val = svn.fs.node_prop(root, path, svn.core.SVN_PROP_NEEDS_LOCK) # Must not already have svn:needs-lock property set if needslock_prop_val is None: interesting_files.append(path) if interesting_files: if dryrun: for path in interesting_files: print "Need to add svn:needs-lock to '" + path + "'" else: # open a transaction against HEAD headrev = svn.fs.youngest_rev(fsob) txn = svn.repos.fs_begin_txn_for_commit(repos_ptr, headrev, uname, commitmsg) root = svn.fs.txn_root(txn) for path in interesting_files: print "Adding svn:needs-lock to '" + path + "'..." svn.fs.change_node_prop(root, path, svn.core.SVN_PROP_NEEDS_LOCK, '*') conflict, newrev = svn.fs.commit_txn(txn) if conflict: raise Exception("Conflict encountered (%s)" % conflict) print 'Created revision: ', newrev else: print 'Nothing changed. Current Revision: ', headrev ################################################################################ def usage(): print "USAGE: add-needs-lock.py [-u username] [-m commitmsg] [-i includeregexp] [-e excluderegexp] [-r REV] [-d] REPOS-PATH" sys.exit(1) ################################################################################ def main(): opts, args = my_getopt(sys.argv[1:], 'u:m:i:e:r:d') uname = 'svnadmin' commitmsg = 'Added missing svn:needs-lock property' included = '.*' excluded = '^$' rev = None dryrun = None for name, value in opts: if name == '-u': uname = value if name == '-m': commitmsg = value if name == '-i': included = value if name == '-e': excluded = value if name == '-r': rev = int(value) if name == '-d': print 'Performing dry run...' dryrun = 1 if rev is None: print 'Searching all files...' else: print 'Searching revision: ' + str(rev) + '...' if len(args) == 1: addneedslock(args[0], uname, commitmsg, included, excluded, rev, dryrun) else: usage() if __name__ == '__main__': main() sys.exit(0)