blob: ab9324baeee8717e57cbf5a37d9360ffbc8e5aa6 [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:
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:
Guido van Rossumed5b3d81998-03-24 05:30:29 +000057 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
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):
Guido van Rossumed5b3d81998-03-24 05:30:29 +000066 print "skipping master subdirectory", master
67 print "-- not under CVS"
68 return
Guido van Rossuma5541071997-08-14 20:15:20 +000069 print "-"*40
70 print "slave ", slave
71 print "master", master
72 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):
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
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:
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
Guido van Rossum79b20381997-11-04 17:35:43 +0000125 if not mf:
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000126 print "Not updating missing master", master
127 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()
137 print "Master ", master
138 print "is newer than slave", slave
139 copy(master, slave, answer=write_slave)
140 return
Guido van Rossuma5541071997-08-14 20:15:20 +0000141 # 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:
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000149 print "***UPDATING MASTER (BINARY COPY)***"
150 copy(slave, master, "rb", answer=write_master)
Guido van Rossuma5541071997-08-14 20:15:20 +0000151 else:
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000152 print "***UPDATING MASTER***"
153 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'):
177 print "copying", src
178 print " to", dst
179 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
190def okay(prompt, answer='ask'):
Walter Dörwaldaaab30e2002-09-11 20:36:02 +0000191 answer = answer.strip().lower()
Guido van Rossuma5541071997-08-14 20:15:20 +0000192 if not answer or answer[0] not in 'ny':
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000193 answer = raw_input(prompt)
Walter Dörwaldaaab30e2002-09-11 20:36:02 +0000194 answer = answer.strip().lower()
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000195 if not answer:
196 answer = default_answer
Guido van Rossuma5541071997-08-14 20:15:20 +0000197 if answer[:1] == 'y':
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000198 return 1
Guido van Rossuma5541071997-08-14 20:15:20 +0000199 if answer[:1] == 'n':
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000200 return 0
Guido van Rossuma5541071997-08-14 20:15:20 +0000201 print "Yes or No please -- try again:"
202 return okay(prompt)
203
Andrew M. Kuchlinge236b382004-08-09 17:27:55 +0000204if __name__ == '__main__':
205 main()