blob: f3df7354ffe2bfbd8cef3608f182f0ef532ff620 [file] [log] [blame]
#!/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 <kmradke@gmail.com>
#
# 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$
# $LastChangedDate$
# $LastChangedBy$
# $LastChangedRevision$
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 as e:
sys.stderr.write("ERROR: Unable to import Subversion's Python bindings: '%s'\n" % 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)