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:
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
26 import os
, sys
, stat
, string
, getopt
28 # Interactivity options
29 default_answer
= "ask"
31 create_directories
= "no"
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:")
41 default_answer
= "yes"
49 create_directories
= a
53 create_files
= create_directories
= write_slave
= write_master
= a
55 [slave
, master
] = args
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"
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"
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
78 print "creating slave directory", slave
82 print "can't make slave directory", slave
, ":", msg
85 print "made slave directory", slave
88 names
= os
.listdir(master
)
90 mastername
= os
.path
.join(master
, name
)
91 slavename
= os
.path
.join(slave
, name
)
95 if os
.path
.isdir(mastername
) and not os
.path
.islink(mastername
):
96 subdirs
.append((slavename
, mastername
))
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:]:
103 s
= os
.path
.join(slave
, name
)
104 m
= os
.path
.join(master
, name
)
106 for (s
, m
) in subdirs
:
109 def compare(slave
, master
):
111 sf
= open(slave
, 'r')
115 mf
= open(master
, 'rb')
120 print "Neither master nor slave exists", master
122 print "Creating missing slave", slave
123 copy(master
, slave
, answer
=create_files
)
126 print "Not updating missing master", master
129 if identical(sf
, mf
):
134 # Master is newer -- copy master to slave
137 print "Master ", master
138 print "is newer than slave", slave
139 copy(master
, slave
, answer
=write_slave
)
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
149 print "***UPDATING MASTER (BINARY COPY)***"
150 copy(slave
, master
, "rb", answer
=write_master
)
152 print "***UPDATING MASTER***"
153 copy(slave
, master
, "r", answer
=write_master
)
157 def identical(sf
, mf
):
159 sd
= sf
.read(BUFSIZE
)
160 md
= mf
.read(BUFSIZE
)
161 if sd
!= md
: return 0
166 st
= os
.fstat(f
.fileno())
167 return st
[stat
.ST_MTIME
]
171 buf
= f
.read(BUFSIZE
)
173 if '\r' in buf
or '\0' in buf
: return 1
176 def copy(src
, dst
, rmode
="rb", wmode
="wb", answer
='ask'):
179 if not okay("okay to copy? ", answer
):
184 buf
= f
.read(BUFSIZE
)
190 def 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
))
196 answer
= default_answer
197 if answer
[:1] == 'y':
199 if answer
[:1] == 'n':
201 print "Yes or No please -- try again:"