blob: 26c3cd5ea7e07ece19d5858c78901a1c2231ef01 [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:
Guido van Rossum79b20381997-11-04 17:35:43 +0000111 sf = open(slave, 'r')
Guido van Rossuma5541071997-08-14 20:15:20 +0000112 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:
Guido van Rossum79b20381997-11-04 17:35:43 +0000120 print "Neither master nor slave exists", master
Guido van Rossuma5541071997-08-14 20:15:20 +0000121 return
122 print "Creating missing slave", slave
123 copy(master, slave, answer=create_files)
124 return
Guido van Rossum79b20381997-11-04 17:35:43 +0000125 if not mf:
126 print "Not updating missing master", master
127 return
Guido van Rossuma5541071997-08-14 20:15:20 +0000128 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
Guido van Rossum79b20381997-11-04 17:35:43 +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:
149 print "***UPDATING MASTER (BINARY COPY)***"
Guido van Rossum92dfa4f1997-08-18 20:56:10 +0000150 copy(slave, master, "rb", answer=write_master)
Guido van Rossuma5541071997-08-14 20:15:20 +0000151 else:
152 print "***UPDATING MASTER***"
Guido van Rossum92dfa4f1997-08-18 20:56:10 +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:
159 sd = sf.read(BUFSIZE)
160 md = mf.read(BUFSIZE)
161 if sd != md: return 0
162 if not sd: break
163 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:
171 buf = f.read(BUFSIZE)
172 if not buf: break
173 if '\r' in buf or '\0' in buf: return 1
174 return 0
175
176def 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()
189
190def okay(prompt, answer='ask'):
191 answer = string.lower(string.strip(answer))
192 if not answer or answer[0] not in 'ny':
193 answer = raw_input(prompt)
194 answer = string.lower(string.strip(answer))
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)
203
204main()