diff --git a/Tools/scripts/treesync.py b/Tools/scripts/treesync.py new file mode 100755 index 00000000000..1cad0e5b08c --- /dev/null +++ b/Tools/scripts/treesync.py @@ -0,0 +1,200 @@ +#! /usr/bin/env python + +"""Script to synchronize two source trees. + +Invoke with two arguments: + +python treesync.py slave master + +The assumption is that "master" contains CVS administration while +slave doesn't. All files in the slave tree that have a CVS/Entries +entry in the master tree are synchronized. This means: + + If the files differ: + if the slave file is newer: + normalize the slave file + if the files still differ: + copy the slave to the master + else (the master is newer): + copy the master to the slave + + normalizing the slave means replacing CRLF with LF when the master + doesn't use CRLF + +""" + +import os, sys, stat, string, getopt + +# Interactivity options +default_answer = "ask" +create_files = "yes" +create_directories = "no" +write_slave = "ask" +write_master = "ask" + +def main(): + global always_no, always_yes + global create_directories, write_master, write_slave + opts, args = getopt.getopt(sys.argv[1:], "nym:s:d:f:a:") + for o, a in opts: + if o == '-y': + default_answer = "yes" + if o == '-n': + default_answer = "no" + if o == '-s': + write_slave = a + if o == '-m': + write_master = a + if o == '-d': + create_directories = a + if o == '-f': + create_files = a + if o == '-a': + create_files = create_directories = write_slave = write_master = a + try: + [slave, master] = args + except ValueError: + print "usage: python", sys.argv[0] or "treesync.py", + print "[-n] [-y] [-m y|n|a] [-s y|n|a] [-d y|n|a] [-f n|y|a]", + print "slavedir masterdir" + return + process(slave, master) + +def process(slave, master): + cvsdir = os.path.join(master, "CVS") + if not os.path.isdir(cvsdir): + print "skipping master subdirectory", master + print "-- not under CVS" + return + print "-"*40 + print "slave ", slave + print "master", master + if not os.path.isdir(slave): + if not okay("create slave directory %s?" % slave, + answer=create_directories): + print "skipping master subdirectory", master + print "-- no corresponding slave", slave + return + print "creating slave directory", slave + try: + os.mkdir(slave) + except os.error, msg: + print "can't make slave directory", slave, ":", msg + return + else: + print "made slave directory", slave + cvsdir = None + subdirs = [] + names = os.listdir(master) + for name in names: + mastername = os.path.join(master, name) + slavename = os.path.join(slave, name) + if name == "CVS": + cvsdir = mastername + else: + if os.path.isdir(mastername) and not os.path.islink(mastername): + subdirs.append((slavename, mastername)) + if cvsdir: + entries = os.path.join(cvsdir, "Entries") + for e in open(entries).readlines(): + words = string.split(e, '/') + if words[0] == '' and words[1:]: + name = words[1] + s = os.path.join(slave, name) + m = os.path.join(master, name) + compare(s, m) + for (s, m) in subdirs: + process(s, m) + +def compare(slave, master): + try: + sf = open(slave, 'rb') + except IOError: + sf = None + try: + mf = open(master, 'rb') + except IOError: + mf = None + if not sf: + if not mf: + print "Not updating missing master", master + return + print "Creating missing slave", slave + copy(master, slave, answer=create_files) + return + if sf and mf: + if identical(sf, mf): + return + sft = mtime(sf) + mft = mtime(mf) + if mft > sft: + # Master is newer -- copy master to slave + sf.close() + mf.close() + print "Master ", master + print "is newer than slave", slave + copy(master, slave, answer=write_slave) + return + # Slave is newer -- copy slave to master + # But first check what to do about CRLF + mf.seek(0) + fun = funnychars(mf) + mf.close() + sf.close() + if fun: + print "***UPDATING MASTER (BINARY COPY)***" + copy(slave, master, "rb", write_master) + else: + print "***UPDATING MASTER***" + copy(slave, master, "r", write_master) + +BUFSIZE = 16*1024 + +def identical(sf, mf): + while 1: + sd = sf.read(BUFSIZE) + md = mf.read(BUFSIZE) + if sd != md: return 0 + if not sd: break + return 1 + +def mtime(f): + st = os.fstat(f.fileno()) + return st[stat.ST_MTIME] + +def funnychars(f): + while 1: + buf = f.read(BUFSIZE) + if not buf: break + if '\r' in buf or '\0' in buf: return 1 + return 0 + +def copy(src, dst, rmode="rb", wmode="wb", answer='ask'): + print "copying", src + print " to", dst + if not okay("okay to copy? ", answer): + return + f = open(src, rmode) + g = open(dst, wmode) + while 1: + buf = f.read(BUFSIZE) + if not buf: break + g.write(buf) + f.close() + g.close() + +def okay(prompt, answer='ask'): + answer = string.lower(string.strip(answer)) + if not answer or answer[0] not in 'ny': + answer = raw_input(prompt) + answer = string.lower(string.strip(answer)) + if not answer: + answer = default_answer + if answer[:1] == 'y': + return 1 + if answer[:1] == 'n': + return 0 + print "Yes or No please -- try again:" + return okay(prompt) + +main()