comparison push_cmd.py @ 83:6c9b7cf1c5aa

push_cmd: delete empty svn directories, refactor directory creation
author Patrick Mezard <pmezard@gmail.com>
date Fri, 14 Nov 2008 16:18:24 -0600
parents 49b7cbe4c8e3
children 05a0c4f6060f
comparison
equal deleted inserted replaced
82:71de43e9f614 83:6c9b7cf1c5aa
66 outgoing = utility_commands.outgoing_revisions(ui, repo, hge, 66 outgoing = utility_commands.outgoing_revisions(ui, repo, hge,
67 svn_commit_hashes) 67 svn_commit_hashes)
68 merc_util._encoding = oldencoding 68 merc_util._encoding = oldencoding
69 return 0 69 return 0
70 70
71 71 def _getdirchanges(svn, branchpath, parentctx, ctx, changedfiles):
72 def _findmissing(dirname, svn, branch_path): 72 """Compute directories to add or delete when moving from parentctx
73 """Find missing directories in svn. dirname *must* end in a / 73 to ctx, assuming only 'changedfiles' files changed.
74
75 Return (added, deleted) where 'added' is the list of all added
76 directories and 'deleted' the list of deleted directories.
77 Intermediate directories are included: if a/b/c is new and requires
78 the addition of a/b and a, those will be listed too. Intermediate
79 deleted directories are also listed, but item order of undefined
80 in either list.
74 """ 81 """
75 assert dirname[-1] == '/' 82 def exists(svndir):
76 missing = []
77 keep_checking = True
78 # check and see if the dir exists svn-side.
79 path = dirname
80 while keep_checking:
81 try: 83 try:
82 assert svn.list_dir('%s/%s' % (branch_path, path)) 84 svn.list_dir('%s/%s' % (branchpath, svndir))
83 keep_checking = False 85 return True
84 except core.SubversionException, e: 86 except core.SubversionException:
85 # dir must not exist 87 return False
86 missing.append(path[:-1]) 88
87 path = '/'.join(path.split('/')[:-2] + ['']) 89 def finddirs(path):
88 return missing 90 pos = path.rfind('/')
91 while pos != -1:
92 yield path[:pos]
93 pos = path.rfind('/', 0, pos)
94
95 def getctxdirs(ctx, keptdirs):
96 dirs = {}
97 for f in ctx.manifest():
98 for d in finddirs(f):
99 if d in dirs:
100 break
101 if d in keptdirs:
102 dirs[d] = 1
103 return dirs
104
105 deleted, added = [], []
106 if not changedfiles:
107 return added, deleted
108 changeddirs = {}
109 for f in changedfiles:
110 for d in finddirs(f):
111 changeddirs[d] = 1
112 olddirs = getctxdirs(parentctx, changeddirs)
113 newdirs = getctxdirs(ctx, changeddirs)
114
115 for d in newdirs:
116 if d not in olddirs and not exists(d):
117 added.append(d)
118
119 for d in olddirs:
120 if d not in newdirs and exists(d):
121 deleted.append(d)
122
123 return added, deleted
124
89 125
90 def commit_from_rev(ui, repo, rev_ctx, hg_editor, svn_url, base_revision): 126 def commit_from_rev(ui, repo, rev_ctx, hg_editor, svn_url, base_revision):
91 """Build and send a commit from Mercurial to Subversion. 127 """Build and send a commit from Mercurial to Subversion.
92 """ 128 """
93 file_data = {} 129 file_data = {}
97 branch_path = 'trunk' 133 branch_path = 'trunk'
98 134
99 if parent_branch and parent_branch != 'default': 135 if parent_branch and parent_branch != 'default':
100 branch_path = 'branches/%s' % parent_branch 136 branch_path = 'branches/%s' % parent_branch
101 137
102 added_dirs = [] 138 addeddirs, deleteddirs = _getdirchanges(svn, branch_path, parent,
139 rev_ctx, rev_ctx.files())
140 deleteddirs = set(deleteddirs)
141
103 props = {} 142 props = {}
104 copies = {} 143 copies = {}
105 for file in rev_ctx.files(): 144 for file in rev_ctx.files():
106 new_data = base_data = '' 145 new_data = base_data = ''
107 action = '' 146 action = ''
122 copies[file] = renamed[0] 161 copies[file] = renamed[0]
123 base_data = parent[renamed[0]].data() 162 base_data = parent[renamed[0]].data()
124 163
125 action = 'add' 164 action = 'add'
126 dirname = '/'.join(file.split('/')[:-1] + ['']) 165 dirname = '/'.join(file.split('/')[:-1] + [''])
127 # check for new directories
128 if not list(parent.walk(util.PrefixMatch(dirname))):
129 added_dirs += _findmissing(dirname, svn, branch_path)
130 else: 166 else:
131 base_data = parent.filectx(file).data() 167 base_data = parent.filectx(file).data()
132 if ('x' in parent.filectx(file).flags() 168 if ('x' in parent.filectx(file).flags()
133 and 'x' not in rev_ctx.filectx(file).flags()): 169 and 'x' not in rev_ctx.filectx(file).flags()):
134 props.setdefault(file, {})['svn:executable'] = None 170 props.setdefault(file, {})['svn:executable'] = None
135 if ('l' in parent.filectx(file).flags() 171 if ('l' in parent.filectx(file).flags()
136 and 'l' not in rev_ctx.filectx(file).flags()): 172 and 'l' not in rev_ctx.filectx(file).flags()):
137 props.setdefault(file, {})['svn:special'] = None 173 props.setdefault(file, {})['svn:special'] = None
138 action = 'modify' 174 action = 'modify'
139 else: 175 else:
176 pos = file.rfind('/')
177 if pos >= 0:
178 if file[:pos] in deleteddirs:
179 # This file will be removed when its directory is removed
180 continue
140 base_data = parent.filectx(file).data() 181 base_data = parent.filectx(file).data()
141 action = 'delete' 182 action = 'delete'
142 file_data[file] = base_data, new_data, action 183 file_data[file] = base_data, new_data, action
143 184
144 # TODO check for directory deletes here 185 # Now we are done with files, we can prune deleted directories
186 # against themselves: ignore a/b if a/ is already removed
187 deleteddirs2 = list(deleteddirs)
188 deleteddirs2.sort()
189 deleteddirs2.reverse()
190 for d in deleteddirs2:
191 pos = d.rfind('/')
192 if pos >= 0 and d[:pos] in deleteddirs:
193 deleteddirs.remove(d[:pos])
194
145 def svnpath(p): 195 def svnpath(p):
146 return '%s/%s' % (branch_path, p) 196 return '%s/%s' % (branch_path, p)
147 197
148 newcopies = {} 198 newcopies = {}
149 for source, dest in copies.iteritems(): 199 for source, dest in copies.iteritems():
150 newcopies[svnpath(source)] = (svnpath(dest), base_revision) 200 newcopies[svnpath(source)] = (svnpath(dest), base_revision)
151 201
152 new_target_files = [svnpath(f) for f in rev_ctx.files()] 202 new_target_files = [svnpath(f) for f in file_data]
153 for tf, ntf in zip(rev_ctx.files(), new_target_files): 203 for tf, ntf in zip(file_data, new_target_files):
154 if tf in file_data: 204 if tf in file_data:
155 file_data[ntf] = file_data[tf] 205 file_data[ntf] = file_data[tf]
156 if tf in props: 206 if tf in props:
157 props[ntf] = props[tf] 207 props[ntf] = props[tf]
158 del props[tf] 208 del props[tf]
159 if merc_util.binary(file_data[ntf][1]): 209 if merc_util.binary(file_data[ntf][1]):
160 props.setdefault(ntf, {}).update(props.get(ntf, {})) 210 props.setdefault(ntf, {}).update(props.get(ntf, {}))
161 props.setdefault(ntf, {})['svn:mime-type'] = 'application/octet-stream' 211 props.setdefault(ntf, {})['svn:mime-type'] = 'application/octet-stream'
162 del file_data[tf] 212 del file_data[tf]
163 added_dirs = ['%s/%s' % (branch_path, f) for f in added_dirs] 213
164 added_dirs = set(added_dirs) 214 addeddirs = [svnpath(d) for d in addeddirs]
165 new_target_files += added_dirs 215 deleteddirs = [svnpath(d) for d in deleteddirs]
216 new_target_files += addeddirs + deleteddirs
166 try: 217 try:
167 svn.commit(new_target_files, rev_ctx.description(), file_data, 218 svn.commit(new_target_files, rev_ctx.description(), file_data,
168 base_revision, set(added_dirs), props, newcopies) 219 base_revision, set(addeddirs), set(deleteddirs),
220 props, newcopies)
169 except core.SubversionException, e: 221 except core.SubversionException, e:
170 if hasattr(e, 'apr_err') and e.apr_err == 160028: 222 if hasattr(e, 'apr_err') and e.apr_err == 160028:
171 raise merc_util.Abort('Base text was out of date, maybe rebase?') 223 raise merc_util.Abort('Base text was out of date, maybe rebase?')
172 else: 224 else:
173 raise 225 raise