|  | #! /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, 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 = e.split('/') | 
|  | 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, 'r') | 
|  | except IOError: | 
|  | sf = None | 
|  | try: | 
|  | mf = open(master, 'rb') | 
|  | except IOError: | 
|  | mf = None | 
|  | if not sf: | 
|  | if not mf: | 
|  | print "Neither master nor slave exists", master | 
|  | return | 
|  | print "Creating missing slave", slave | 
|  | copy(master, slave, answer=create_files) | 
|  | return | 
|  | if not mf: | 
|  | print "Not updating missing master", master | 
|  | 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 | 
|  | print "Slave is", sft-mft, "seconds newer than 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", answer=write_master) | 
|  | else: | 
|  | print "***UPDATING MASTER***" | 
|  | copy(slave, master, "r", answer=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 = answer.strip().lower() | 
|  | if not answer or answer[0] not in 'ny': | 
|  | answer = raw_input(prompt) | 
|  | answer = answer.strip().lower() | 
|  | 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) | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | main() |