summaryrefslogtreecommitdiff
path: root/tools/update_tree_in_svn.py
blob: 855ed0492cb740e263891428f200fb35f10a4843 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/usr/bin/python

"""
After building can be used to replace documentation,
and jars with the newly built versions in SVN.
"""

import filecmp
import os
import pipes
import sys

FILE = 'f'
DIR = 'd'
NO_EXIST = 'n'

MIME_TYPES_BY_EXTENSION = {
  'html': 'text/html;charset=UTF-8',
  'txt': 'text/plain;charset=UTF-8',
  'css': 'text/css;charset=UTF-8',
  'js': 'text/javascript;charset=UTF-8',
  'jar': 'application/x-java-archive',
  'xsl': 'text/xml;charset=UTF-8',
  'gif': 'image/gif',
  'png': 'image/png'
  }

def sync(src_to_dest):
  """
  Syncrhonize the destination file tree with the source file tree
  in both the current client and in subversion.
  """

  def classify(path):
    if not os.path.exists(path): return NO_EXIST
    if os.path.isdir(path): return DIR
    return FILE

  # If we see a case where (conflict) is present, then we need to be
  # sure to do svn deletes in a separate commit before svn adds.
  conflict = False
  # Keep track of changes to make in subversion
  svn_adds = []
  svn_deletes = []
  svn_propsets = {}

  # A bunch of actions that can be taken to synchronize one aspect
  # of a source file and a destination file
  def run(argv):
    """
    Prints out a command line that needs to be run.
    """
    print ' '.join([pipes.quote(arg) for arg in argv])

  def svn(verb_and_flags, args):
    cmd = ['svn']
    cmd.extend(verb_and_flags)
    cmd.extend(args)
    run(cmd)

  def remove(src, dst): run(['rm', dst])

  def svn_delete(src, dst): svn_deletes.append(dst)

  def recurse(src, dst):
    children = set()
    if os.path.isdir(src): children.update(os.listdir(src))
    if os.path.isdir(dst):
      children.update(os.listdir(dst))
    children.discard('.svn')
    for child in children:
      handle(os.path.join(src, child), os.path.join(dst, child))

  def copy(src, dst): run(['cp', '-f', src, dst])

  def copy_if_different(src, dst):
    if not filecmp.cmp(src, dst, shallow=0): copy(src, dst)

  def svn_add(src, dst):
    svn_adds.append(dst)
    dot = dst.rfind('.')
    if dot >= 0:
      mime_type = MIME_TYPES_BY_EXTENSION.get(dst[dot+1:])
      if mime_type is not None:
        key = ('svn:mime-type', mime_type)
        if key not in svn_propsets:
          svn_propsets[key] = []
        svn_propsets[key].append(dst) 

  def cnf(src, dst): conflict = True

  def mkdir(src, dst): run(['mkdir', dst])

  # The below table contains the actions to take for each possible
  # scenario.
  actions = {
  # src        dst        actions
    (NO_EXIST, NO_EXIST): (),
    (NO_EXIST, FILE)    : (remove, svn_delete,),
    (NO_EXIST, DIR)     : (recurse, remove, svn_delete,),
    (FILE,     NO_EXIST): (copy, svn_add,),
    (FILE,     FILE)    : (copy_if_different,),
    (FILE,     DIR)     : (recurse, remove, svn_delete, copy, svn_add, cnf),
    (DIR,      NO_EXIST): (mkdir, svn_add, recurse,),
    (DIR,      FILE)    : (remove, svn_delete, mkdir, svn_add, recurse, cnf),
    (DIR,      DIR)     : (recurse,),
    }

  # Walk the file tree (see recurse action above) and synchronize it at
  # each step.
  def handle(src, dst):
    src_t = classify(src)
    dst_t = classify(dst)
    for action in actions[(src_t, dst_t)]: action(src, dst)

  for (src, dst) in src_to_dest:
    handle(src, dst)

  if len(svn_deletes):
    svn(['delete'], svn_deletes)
    if conflict: 
      svn(['commit', '-m', 'remove obsolete files from the snapshot tree'],
          commit_args)
  if len(svn_adds):
    svn(['add', '--depth=empty'], svn_adds)
  for ((propname, propvalue), files) in svn_propsets.items():
    svn(['propset', propname, propvalue], files)

if '__main__' == __name__:
  sync([(sys.argv[1], sys.argv[2])])