blob: 652d3940a49a403f26719d5d4529376b4600a59e [file] [log] [blame]
Benjamin Peterson90f5ba52010-03-11 22:53:45 +00001#! /usr/bin/env python3
Guido van Rossuma5541071997-08-14 20:15:20 +00002
3"""Script to synchronize two source trees.
4
5Invoke with two arguments:
6
7python treesync.py slave master
8
9The assumption is that "master" contains CVS administration while
10slave doesn't. All files in the slave tree that have a CVS/Entries
11entry in the master tree are synchronized. This means:
12
13 If the files differ:
14 if the slave file is newer:
Guido van Rossumed5b3d81998-03-24 05:30:29 +000015 normalize the slave file
16 if the files still differ:
17 copy the slave to the master
18 else (the master is newer):
19 copy the master to the slave
Tim Peters70c43782001-01-17 08:48:39 +000020
Guido van Rossuma5541071997-08-14 20:15:20 +000021 normalizing the slave means replacing CRLF with LF when the master
22 doesn't use CRLF
23
24"""
25
Walter Dörwaldaaab30e2002-09-11 20:36:02 +000026import os, sys, stat, getopt
Guido van Rossuma5541071997-08-14 20:15:20 +000027
28# Interactivity options
29default_answer = "ask"
30create_files = "yes"
31create_directories = "no"
32write_slave = "ask"
33write_master = "ask"
34
35def main():
36 global always_no, always_yes
37 global create_directories, write_master, write_slave
38 opts, args = getopt.getopt(sys.argv[1:], "nym:s:d:f:a:")
39 for o, a in opts:
Guido van Rossumed5b3d81998-03-24 05:30:29 +000040 if o == '-y':
41 default_answer = "yes"
42 if o == '-n':
43 default_answer = "no"
44 if o == '-s':
45 write_slave = a
46 if o == '-m':
47 write_master = a
48 if o == '-d':
49 create_directories = a
50 if o == '-f':
51 create_files = a
52 if o == '-a':
53 create_files = create_directories = write_slave = write_master = a
Guido van Rossuma5541071997-08-14 20:15:20 +000054 try:
Guido van Rossumed5b3d81998-03-24 05:30:29 +000055 [slave, master] = args
Guido van Rossuma5541071997-08-14 20:15:20 +000056 except ValueError:
Collin Winter6afaeb72007-08-03 17:06:41 +000057 print("usage: python", sys.argv[0] or "treesync.py", end=' ')
58 print("[-n] [-y] [-m y|n|a] [-s y|n|a] [-d y|n|a] [-f n|y|a]", end=' ')
59 print("slavedir masterdir")
Guido van Rossumed5b3d81998-03-24 05:30:29 +000060 return
Guido van Rossuma5541071997-08-14 20:15:20 +000061 process(slave, master)
Tim Peters70c43782001-01-17 08:48:39 +000062
Guido van Rossuma5541071997-08-14 20:15:20 +000063def process(slave, master):
64 cvsdir = os.path.join(master, "CVS")
65 if not os.path.isdir(cvsdir):
Collin Winter6afaeb72007-08-03 17:06:41 +000066 print("skipping master subdirectory", master)
67 print("-- not under CVS")
Guido van Rossumed5b3d81998-03-24 05:30:29 +000068 return
Collin Winter6afaeb72007-08-03 17:06:41 +000069 print("-"*40)
70 print("slave ", slave)
71 print("master", master)
Guido van Rossuma5541071997-08-14 20:15:20 +000072 if not os.path.isdir(slave):
Guido van Rossumed5b3d81998-03-24 05:30:29 +000073 if not okay("create slave directory %s?" % slave,
74 answer=create_directories):
Collin Winter6afaeb72007-08-03 17:06:41 +000075 print("skipping master subdirectory", master)
76 print("-- no corresponding slave", slave)
Guido van Rossumed5b3d81998-03-24 05:30:29 +000077 return
Collin Winter6afaeb72007-08-03 17:06:41 +000078 print("creating slave directory", slave)
Guido van Rossumed5b3d81998-03-24 05:30:29 +000079 try:
80 os.mkdir(slave)
Andrew Svetlov8b33dd82012-12-24 19:58:48 +020081 except OSError as msg:
Collin Winter6afaeb72007-08-03 17:06:41 +000082 print("can't make slave directory", slave, ":", msg)
Guido van Rossumed5b3d81998-03-24 05:30:29 +000083 return
84 else:
Collin Winter6afaeb72007-08-03 17:06:41 +000085 print("made slave directory", slave)
Guido van Rossuma5541071997-08-14 20:15:20 +000086 cvsdir = None
87 subdirs = []
88 names = os.listdir(master)
89 for name in names:
Guido van Rossumed5b3d81998-03-24 05:30:29 +000090 mastername = os.path.join(master, name)
91 slavename = os.path.join(slave, name)
92 if name == "CVS":
93 cvsdir = mastername
94 else:
95 if os.path.isdir(mastername) and not os.path.islink(mastername):
96 subdirs.append((slavename, mastername))
Guido van Rossuma5541071997-08-14 20:15:20 +000097 if cvsdir:
Guido van Rossumed5b3d81998-03-24 05:30:29 +000098 entries = os.path.join(cvsdir, "Entries")
99 for e in open(entries).readlines():
Walter Dörwaldaaab30e2002-09-11 20:36:02 +0000100 words = e.split('/')
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000101 if words[0] == '' and words[1:]:
102 name = words[1]
103 s = os.path.join(slave, name)
104 m = os.path.join(master, name)
105 compare(s, m)
Guido van Rossuma5541071997-08-14 20:15:20 +0000106 for (s, m) in subdirs:
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000107 process(s, m)
Guido van Rossuma5541071997-08-14 20:15:20 +0000108
109def compare(slave, master):
110 try:
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000111 sf = open(slave, 'r')
Guido van Rossuma5541071997-08-14 20:15:20 +0000112 except IOError:
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000113 sf = None
Guido van Rossuma5541071997-08-14 20:15:20 +0000114 try:
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000115 mf = open(master, 'rb')
Guido van Rossuma5541071997-08-14 20:15:20 +0000116 except IOError:
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000117 mf = None
Guido van Rossuma5541071997-08-14 20:15:20 +0000118 if not sf:
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000119 if not mf:
Collin Winter6afaeb72007-08-03 17:06:41 +0000120 print("Neither master nor slave exists", master)
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000121 return
Collin Winter6afaeb72007-08-03 17:06:41 +0000122 print("Creating missing slave", slave)
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000123 copy(master, slave, answer=create_files)
124 return
Guido van Rossum79b20381997-11-04 17:35:43 +0000125 if not mf:
Collin Winter6afaeb72007-08-03 17:06:41 +0000126 print("Not updating missing master", master)
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000127 return
Guido van Rossuma5541071997-08-14 20:15:20 +0000128 if sf and mf:
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000129 if identical(sf, mf):
130 return
Guido van Rossuma5541071997-08-14 20:15:20 +0000131 sft = mtime(sf)
132 mft = mtime(mf)
133 if mft > sft:
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000134 # Master is newer -- copy master to slave
135 sf.close()
136 mf.close()
Collin Winter6afaeb72007-08-03 17:06:41 +0000137 print("Master ", master)
138 print("is newer than slave", slave)
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000139 copy(master, slave, answer=write_slave)
140 return
Guido van Rossuma5541071997-08-14 20:15:20 +0000141 # Slave is newer -- copy slave to master
Collin Winter6afaeb72007-08-03 17:06:41 +0000142 print("Slave is", sft-mft, "seconds newer than master")
Guido van Rossuma5541071997-08-14 20:15:20 +0000143 # But first check what to do about CRLF
144 mf.seek(0)
145 fun = funnychars(mf)
146 mf.close()
147 sf.close()
148 if fun:
Collin Winter6afaeb72007-08-03 17:06:41 +0000149 print("***UPDATING MASTER (BINARY COPY)***")
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000150 copy(slave, master, "rb", answer=write_master)
Guido van Rossuma5541071997-08-14 20:15:20 +0000151 else:
Collin Winter6afaeb72007-08-03 17:06:41 +0000152 print("***UPDATING MASTER***")
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000153 copy(slave, master, "r", answer=write_master)
Guido van Rossuma5541071997-08-14 20:15:20 +0000154
155BUFSIZE = 16*1024
156
157def identical(sf, mf):
158 while 1:
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000159 sd = sf.read(BUFSIZE)
160 md = mf.read(BUFSIZE)
161 if sd != md: return 0
162 if not sd: break
Guido van Rossuma5541071997-08-14 20:15:20 +0000163 return 1
164
165def mtime(f):
166 st = os.fstat(f.fileno())
167 return st[stat.ST_MTIME]
168
169def funnychars(f):
170 while 1:
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000171 buf = f.read(BUFSIZE)
172 if not buf: break
173 if '\r' in buf or '\0' in buf: return 1
Guido van Rossuma5541071997-08-14 20:15:20 +0000174 return 0
175
176def copy(src, dst, rmode="rb", wmode="wb", answer='ask'):
Collin Winter6afaeb72007-08-03 17:06:41 +0000177 print("copying", src)
178 print(" to", dst)
Guido van Rossuma5541071997-08-14 20:15:20 +0000179 if not okay("okay to copy? ", answer):
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000180 return
Guido van Rossuma5541071997-08-14 20:15:20 +0000181 f = open(src, rmode)
182 g = open(dst, wmode)
183 while 1:
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000184 buf = f.read(BUFSIZE)
185 if not buf: break
186 g.write(buf)
Guido van Rossuma5541071997-08-14 20:15:20 +0000187 f.close()
188 g.close()
189
Neal Norwitzce96f692006-03-17 06:49:51 +0000190def raw_input(prompt):
191 sys.stdout.write(prompt)
192 sys.stdout.flush()
193 return sys.stdin.readline()
194
Guido van Rossuma5541071997-08-14 20:15:20 +0000195def okay(prompt, answer='ask'):
Walter Dörwaldaaab30e2002-09-11 20:36:02 +0000196 answer = answer.strip().lower()
Guido van Rossuma5541071997-08-14 20:15:20 +0000197 if not answer or answer[0] not in 'ny':
Georg Brandl8efadf52008-05-16 15:23:30 +0000198 answer = input(prompt)
Walter Dörwaldaaab30e2002-09-11 20:36:02 +0000199 answer = answer.strip().lower()
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000200 if not answer:
201 answer = default_answer
Guido van Rossuma5541071997-08-14 20:15:20 +0000202 if answer[:1] == 'y':
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000203 return 1
Guido van Rossuma5541071997-08-14 20:15:20 +0000204 if answer[:1] == 'n':
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000205 return 0
Collin Winter6afaeb72007-08-03 17:06:41 +0000206 print("Yes or No please -- try again:")
Guido van Rossuma5541071997-08-14 20:15:20 +0000207 return okay(prompt)
208
Andrew M. Kuchlinge236b382004-08-09 17:27:55 +0000209if __name__ == '__main__':
210 main()