blob: f0393858df3f0599d78b0f8862603240fe823671 [file] [log] [blame]
Guido van Rossuma5541071997-08-14 20:15:20 +00001#! /usr/bin/env python
2
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:
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
20
21 normalizing the slave means replacing CRLF with LF when the master
22 doesn't use CRLF
23
24"""
25
26import os, sys, stat, string, getopt
27
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:
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)
62
63def 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 = string.split(e, '/')
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)
108
109def compare(slave, master):
110 try:
111 sf = open(slave, 'rb')
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 "Not updating missing master", master
121 return
122 print "Creating missing slave", slave
123 copy(master, slave, answer=create_files)
124 return
125 if sf and mf:
126 if identical(sf, mf):
127 return
128 sft = mtime(sf)
129 mft = mtime(mf)
130 if mft > sft:
131 # Master is newer -- copy master to slave
132 sf.close()
133 mf.close()
134 print "Master ", master
135 print "is newer than slave", slave
136 copy(master, slave, answer=write_slave)
137 return
138 # Slave is newer -- copy slave to master
139 # But first check what to do about CRLF
140 mf.seek(0)
141 fun = funnychars(mf)
142 mf.close()
143 sf.close()
144 if fun:
145 print "***UPDATING MASTER (BINARY COPY)***"
Guido van Rossum92dfa4f1997-08-18 20:56:10 +0000146 copy(slave, master, "rb", answer=write_master)
Guido van Rossuma5541071997-08-14 20:15:20 +0000147 else:
148 print "***UPDATING MASTER***"
Guido van Rossum92dfa4f1997-08-18 20:56:10 +0000149 copy(slave, master, "r", answer=write_master)
Guido van Rossuma5541071997-08-14 20:15:20 +0000150
151BUFSIZE = 16*1024
152
153def identical(sf, mf):
154 while 1:
155 sd = sf.read(BUFSIZE)
156 md = mf.read(BUFSIZE)
157 if sd != md: return 0
158 if not sd: break
159 return 1
160
161def mtime(f):
162 st = os.fstat(f.fileno())
163 return st[stat.ST_MTIME]
164
165def funnychars(f):
166 while 1:
167 buf = f.read(BUFSIZE)
168 if not buf: break
169 if '\r' in buf or '\0' in buf: return 1
170 return 0
171
172def copy(src, dst, rmode="rb", wmode="wb", answer='ask'):
173 print "copying", src
174 print " to", dst
175 if not okay("okay to copy? ", answer):
176 return
177 f = open(src, rmode)
178 g = open(dst, wmode)
179 while 1:
180 buf = f.read(BUFSIZE)
181 if not buf: break
182 g.write(buf)
183 f.close()
184 g.close()
185
186def okay(prompt, answer='ask'):
187 answer = string.lower(string.strip(answer))
188 if not answer or answer[0] not in 'ny':
189 answer = raw_input(prompt)
190 answer = string.lower(string.strip(answer))
191 if not answer:
192 answer = default_answer
193 if answer[:1] == 'y':
194 return 1
195 if answer[:1] == 'n':
196 return 0
197 print "Yes or No please -- try again:"
198 return okay(prompt)
199
200main()