/Tools/scripts/treesync.py

http://unladen-swallow.googlecode.com/ · Python · 205 lines · 181 code · 13 blank · 11 comment · 56 complexity · 80da9c526ee690ac8407b70befa048b4 MD5 · raw file

  1. #! /usr/bin/env python
  2. """Script to synchronize two source trees.
  3. Invoke with two arguments:
  4. python treesync.py slave master
  5. The assumption is that "master" contains CVS administration while
  6. slave doesn't. All files in the slave tree that have a CVS/Entries
  7. entry in the master tree are synchronized. This means:
  8. If the files differ:
  9. if the slave file is newer:
  10. normalize the slave file
  11. if the files still differ:
  12. copy the slave to the master
  13. else (the master is newer):
  14. copy the master to the slave
  15. normalizing the slave means replacing CRLF with LF when the master
  16. doesn't use CRLF
  17. """
  18. import os, sys, stat, getopt
  19. # Interactivity options
  20. default_answer = "ask"
  21. create_files = "yes"
  22. create_directories = "no"
  23. write_slave = "ask"
  24. write_master = "ask"
  25. def main():
  26. global always_no, always_yes
  27. global create_directories, write_master, write_slave
  28. opts, args = getopt.getopt(sys.argv[1:], "nym:s:d:f:a:")
  29. for o, a in opts:
  30. if o == '-y':
  31. default_answer = "yes"
  32. if o == '-n':
  33. default_answer = "no"
  34. if o == '-s':
  35. write_slave = a
  36. if o == '-m':
  37. write_master = a
  38. if o == '-d':
  39. create_directories = a
  40. if o == '-f':
  41. create_files = a
  42. if o == '-a':
  43. create_files = create_directories = write_slave = write_master = a
  44. try:
  45. [slave, master] = args
  46. except ValueError:
  47. print "usage: python", sys.argv[0] or "treesync.py",
  48. print "[-n] [-y] [-m y|n|a] [-s y|n|a] [-d y|n|a] [-f n|y|a]",
  49. print "slavedir masterdir"
  50. return
  51. process(slave, master)
  52. def process(slave, master):
  53. cvsdir = os.path.join(master, "CVS")
  54. if not os.path.isdir(cvsdir):
  55. print "skipping master subdirectory", master
  56. print "-- not under CVS"
  57. return
  58. print "-"*40
  59. print "slave ", slave
  60. print "master", master
  61. if not os.path.isdir(slave):
  62. if not okay("create slave directory %s?" % slave,
  63. answer=create_directories):
  64. print "skipping master subdirectory", master
  65. print "-- no corresponding slave", slave
  66. return
  67. print "creating slave directory", slave
  68. try:
  69. os.mkdir(slave)
  70. except os.error, msg:
  71. print "can't make slave directory", slave, ":", msg
  72. return
  73. else:
  74. print "made slave directory", slave
  75. cvsdir = None
  76. subdirs = []
  77. names = os.listdir(master)
  78. for name in names:
  79. mastername = os.path.join(master, name)
  80. slavename = os.path.join(slave, name)
  81. if name == "CVS":
  82. cvsdir = mastername
  83. else:
  84. if os.path.isdir(mastername) and not os.path.islink(mastername):
  85. subdirs.append((slavename, mastername))
  86. if cvsdir:
  87. entries = os.path.join(cvsdir, "Entries")
  88. for e in open(entries).readlines():
  89. words = e.split('/')
  90. if words[0] == '' and words[1:]:
  91. name = words[1]
  92. s = os.path.join(slave, name)
  93. m = os.path.join(master, name)
  94. compare(s, m)
  95. for (s, m) in subdirs:
  96. process(s, m)
  97. def compare(slave, master):
  98. try:
  99. sf = open(slave, 'r')
  100. except IOError:
  101. sf = None
  102. try:
  103. mf = open(master, 'rb')
  104. except IOError:
  105. mf = None
  106. if not sf:
  107. if not mf:
  108. print "Neither master nor slave exists", master
  109. return
  110. print "Creating missing slave", slave
  111. copy(master, slave, answer=create_files)
  112. return
  113. if not mf:
  114. print "Not updating missing master", master
  115. return
  116. if sf and mf:
  117. if identical(sf, mf):
  118. return
  119. sft = mtime(sf)
  120. mft = mtime(mf)
  121. if mft > sft:
  122. # Master is newer -- copy master to slave
  123. sf.close()
  124. mf.close()
  125. print "Master ", master
  126. print "is newer than slave", slave
  127. copy(master, slave, answer=write_slave)
  128. return
  129. # Slave is newer -- copy slave to master
  130. print "Slave is", sft-mft, "seconds newer than master"
  131. # But first check what to do about CRLF
  132. mf.seek(0)
  133. fun = funnychars(mf)
  134. mf.close()
  135. sf.close()
  136. if fun:
  137. print "***UPDATING MASTER (BINARY COPY)***"
  138. copy(slave, master, "rb", answer=write_master)
  139. else:
  140. print "***UPDATING MASTER***"
  141. copy(slave, master, "r", answer=write_master)
  142. BUFSIZE = 16*1024
  143. def identical(sf, mf):
  144. while 1:
  145. sd = sf.read(BUFSIZE)
  146. md = mf.read(BUFSIZE)
  147. if sd != md: return 0
  148. if not sd: break
  149. return 1
  150. def mtime(f):
  151. st = os.fstat(f.fileno())
  152. return st[stat.ST_MTIME]
  153. def funnychars(f):
  154. while 1:
  155. buf = f.read(BUFSIZE)
  156. if not buf: break
  157. if '\r' in buf or '\0' in buf: return 1
  158. return 0
  159. def copy(src, dst, rmode="rb", wmode="wb", answer='ask'):
  160. print "copying", src
  161. print " to", dst
  162. if not okay("okay to copy? ", answer):
  163. return
  164. f = open(src, rmode)
  165. g = open(dst, wmode)
  166. while 1:
  167. buf = f.read(BUFSIZE)
  168. if not buf: break
  169. g.write(buf)
  170. f.close()
  171. g.close()
  172. def okay(prompt, answer='ask'):
  173. answer = answer.strip().lower()
  174. if not answer or answer[0] not in 'ny':
  175. answer = raw_input(prompt)
  176. answer = answer.strip().lower()
  177. if not answer:
  178. answer = default_answer
  179. if answer[:1] == 'y':
  180. return 1
  181. if answer[:1] == 'n':
  182. return 0
  183. print "Yes or No please -- try again:"
  184. return okay(prompt)
  185. if __name__ == '__main__':
  186. main()