Kill a couple of "<>"
[python.git] / Tools / scripts / treesync.py
blobab9324baeee8717e57cbf5a37d9360ffbc8e5aa6
1 #! /usr/bin/env python
3 """Script to synchronize two source trees.
5 Invoke with two arguments:
7 python treesync.py slave master
9 The assumption is that "master" contains CVS administration while
10 slave doesn't. All files in the slave tree that have a CVS/Entries
11 entry in the master tree are synchronized. This means:
13 If the files differ:
14 if the slave file is newer:
15 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
21 normalizing the slave means replacing CRLF with LF when the master
22 doesn't use CRLF
24 """
26 import os, sys, stat, getopt
28 # Interactivity options
29 default_answer = "ask"
30 create_files = "yes"
31 create_directories = "no"
32 write_slave = "ask"
33 write_master = "ask"
35 def 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:
40 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
54 try:
55 [slave, master] = args
56 except ValueError:
57 print "usage: python", sys.argv[0] or "treesync.py",
58 print "[-n] [-y] [-m y|n|a] [-s y|n|a] [-d y|n|a] [-f n|y|a]",
59 print "slavedir masterdir"
60 return
61 process(slave, master)
63 def process(slave, master):
64 cvsdir = os.path.join(master, "CVS")
65 if not os.path.isdir(cvsdir):
66 print "skipping master subdirectory", master
67 print "-- not under CVS"
68 return
69 print "-"*40
70 print "slave ", slave
71 print "master", master
72 if not os.path.isdir(slave):
73 if not okay("create slave directory %s?" % slave,
74 answer=create_directories):
75 print "skipping master subdirectory", master
76 print "-- no corresponding slave", slave
77 return
78 print "creating slave directory", slave
79 try:
80 os.mkdir(slave)
81 except os.error, msg:
82 print "can't make slave directory", slave, ":", msg
83 return
84 else:
85 print "made slave directory", slave
86 cvsdir = None
87 subdirs = []
88 names = os.listdir(master)
89 for name in names:
90 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))
97 if cvsdir:
98 entries = os.path.join(cvsdir, "Entries")
99 for e in open(entries).readlines():
100 words = e.split('/')
101 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)
106 for (s, m) in subdirs:
107 process(s, m)
109 def compare(slave, master):
110 try:
111 sf = open(slave, 'r')
112 except IOError:
113 sf = None
114 try:
115 mf = open(master, 'rb')
116 except IOError:
117 mf = None
118 if not sf:
119 if not mf:
120 print "Neither master nor slave exists", master
121 return
122 print "Creating missing slave", slave
123 copy(master, slave, answer=create_files)
124 return
125 if not mf:
126 print "Not updating missing master", master
127 return
128 if sf and mf:
129 if identical(sf, mf):
130 return
131 sft = mtime(sf)
132 mft = mtime(mf)
133 if mft > sft:
134 # Master is newer -- copy master to slave
135 sf.close()
136 mf.close()
137 print "Master ", master
138 print "is newer than slave", slave
139 copy(master, slave, answer=write_slave)
140 return
141 # Slave is newer -- copy slave to master
142 print "Slave is", sft-mft, "seconds newer than master"
143 # 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:
149 print "***UPDATING MASTER (BINARY COPY)***"
150 copy(slave, master, "rb", answer=write_master)
151 else:
152 print "***UPDATING MASTER***"
153 copy(slave, master, "r", answer=write_master)
155 BUFSIZE = 16*1024
157 def identical(sf, mf):
158 while 1:
159 sd = sf.read(BUFSIZE)
160 md = mf.read(BUFSIZE)
161 if sd != md: return 0
162 if not sd: break
163 return 1
165 def mtime(f):
166 st = os.fstat(f.fileno())
167 return st[stat.ST_MTIME]
169 def funnychars(f):
170 while 1:
171 buf = f.read(BUFSIZE)
172 if not buf: break
173 if '\r' in buf or '\0' in buf: return 1
174 return 0
176 def copy(src, dst, rmode="rb", wmode="wb", answer='ask'):
177 print "copying", src
178 print " to", dst
179 if not okay("okay to copy? ", answer):
180 return
181 f = open(src, rmode)
182 g = open(dst, wmode)
183 while 1:
184 buf = f.read(BUFSIZE)
185 if not buf: break
186 g.write(buf)
187 f.close()
188 g.close()
190 def okay(prompt, answer='ask'):
191 answer = answer.strip().lower()
192 if not answer or answer[0] not in 'ny':
193 answer = raw_input(prompt)
194 answer = answer.strip().lower()
195 if not answer:
196 answer = default_answer
197 if answer[:1] == 'y':
198 return 1
199 if answer[:1] == 'n':
200 return 0
201 print "Yes or No please -- try again:"
202 return okay(prompt)
204 if __name__ == '__main__':
205 main()