incremental-update.py [plain text]
SVN_BINARY='svn'
import sys
import os
import re
def print_error(err):
sys.stderr.write("ERROR: %s\n\n" % (err))
def usage_and_exit(err=None):
if err:
stream = sys.stderr
print_error(err)
else:
stream = sys.stdout
stream.write("""Usage: %s [OPTIONS] WC-DIR
Update WC-DIR in an incremental fashion, starting will smaller
subtrees of it, and working up toward WC-DIR itself. SVN_UP_ARGS are
command-line parameters passed straight through to the Subversion
command-line client (svn) as parameters to its update command.
WARNING: Speed of operation is explicitly *NOT* of interest to this
script. Use it only when a typical 'svn update' isn't working for you
due to the complexity of your working copy's mixed-revision state.
Options:
--username USER Specify the username used to connect to the repository
--password PASS Specify the PASSWORD used to connect to the repository
""" % (os.path.basename(sys.argv[0])))
sys.exit(err and 1 or 0)
def get_head_revision(path, args):
"""Return the current HEAD revision for the repository associated
with PATH. ARGS are extra arguments to provide to the svn
client."""
lines = os.popen('%s status --show-updates --non-recursive %s %s'
% (SVN_BINARY, args, path)).readlines()
if lines and lines[-1].startswith('Status against revision:'):
return int(lines[-1][24:].strip())
raise Exception, "Unable to fetch HEAD revision number."
def compare_paths(path1, path2):
"""This is a sort() helper function for two paths."""
path1_len = len (path1);
path2_len = len (path2);
min_len = min(path1_len, path2_len)
i = 0
if path1 == path2:
return 0
while (i < min_len) and (path1[i] == path2[i]):
i = i + 1
char1 = '\0'
char2 = '\0'
if (i < path1_len):
char1 = path1[i]
if (i < path2_len):
char2 = path2[i]
if (char1 == '/') and (i == path2_len):
return 1
if (char2 == '/') and (i == path1_len):
return -1
if (i < path1_len) and (char1 == '/'):
return -1
if (i < path2_len) and (char2 == '/'):
return 1
return cmp(char1, char2)
def harvest_dirs(path):
"""Return a list of versioned directories under working copy
directory PATH, inclusive."""
rm = re.compile('^([!MACDRUG_ ][MACDRUG_ ])([L ])([+ ])([S ])([KOBT ]) ' \
'([* ]) [^0-9-]*(\d+|-|\?) +(\d|-|\?)+ +(\S+) +(.+)')
dirs = []
fp = os.popen('%s status --verbose %s' % (SVN_BINARY, path))
while 1:
line = fp.readline()
if not line:
break
line = line.rstrip()
if line.startswith('Performing'):
break
match = rm.search(line)
if match:
stpath = match.group(10)
try:
if os.path.isdir(stpath):
dirs.append(stpath)
except:
pass
return dirs
def main():
argc = len(sys.argv)
if argc < 2:
usage_and_exit("No working copy directory specified")
if '--help' in sys.argv:
usage_and_exit(None)
path = sys.argv[-1]
args = ' '.join(sys.argv[1:-1] + ['--non-interactive'])
print "Fetch HEAD revision...",
head_revision = get_head_revision(path, args)
print "done."
print "Updating to revision %d" % (head_revision)
print "Harvesting the list of subdirectories...",
dirs = harvest_dirs(path)
print "done."
dirs.sort(compare_paths)
dirs.reverse()
print "Update the tree, one subdirectory at a time. This could take " \
"a while."
num_dirs = len(dirs)
width = len(str(num_dirs))
format_string = '[%%%dd/%%%dd] Updating %%s' % (width, width)
current = 0
for dir in dirs:
current = current + 1
print format_string % (current, num_dirs, dir)
os.system('%s update --quiet --revision %d %s %s'
% (SVN_BINARY, head_revision, args, dir))
if __name__ == "__main__":
try:
main()
except SystemExit:
raise
except Exception, e:
print_error(str(e))
sys.exit(1)