Mercurial > hgsubversion
comparison hgsubversion/editor.py @ 938:f9014e28721b
editor: start separating svn copies from open files
The separation is not complete as we still have to update the
RevisionData deleted set when registering svn copies. This will be
cleaned up once open files are themselves separated from RevisionData.
Copied symlinks are also being prefixed with 'link '.
author | Patrick Mezard <patrick@mezard.eu> |
---|---|
date | Wed, 03 Oct 2012 21:27:02 +0200 |
parents | fb6f6b7fa5a5 |
children | 997de286ba0c |
comparison
equal
deleted
inserted
replaced
937:fb6f6b7fa5a5 | 938:f9014e28721b |
---|---|
115 | 115 |
116 def _clear(self): | 116 def _clear(self): |
117 self._filecounter = 0 | 117 self._filecounter = 0 |
118 self._filebatons = {} | 118 self._filebatons = {} |
119 self._files = {} | 119 self._files = {} |
120 # A mapping of svn paths to (data, isexec, islink, copypath) tuples | |
121 self._svncopies = {} | |
120 | 122 |
121 def _addfilebaton(self, path): | 123 def _addfilebaton(self, path): |
122 # XXX: enforce unicity here. This cannot be done right now | 124 # XXX: enforce unicity here. This cannot be done right now |
123 # because copied files are mixed with open files and the cleanup | 125 # because copied files are mixed with open files and the cleanup |
124 # rules in delete_entry() requires the distinction to be made to | 126 # rules in delete_entry() requires the distinction to be made to |
135 if br_path == '': | 137 if br_path == '': |
136 if self.meta.get_path_tag(path): | 138 if self.meta.get_path_tag(path): |
137 # Tag deletion is not handled as branched deletion | 139 # Tag deletion is not handled as branched deletion |
138 return | 140 return |
139 self.meta.closebranches.add(branch) | 141 self.meta.closebranches.add(branch) |
142 | |
143 # Delete copied entries, no need to check they exist in hg | |
144 # parent revision. | |
145 if path in self._svncopies: | |
146 del self._svncopies[path] | |
147 prefix = path + '/' | |
148 for f in list(self._svncopies): | |
149 if f.startswith(prefix): | |
150 del self._svncopies[f] | |
151 | |
140 if br_path is not None: | 152 if br_path is not None: |
141 ha = self.meta.get_parent_revision(self.current.rev.revnum, branch) | 153 ha = self.meta.get_parent_revision(self.current.rev.revnum, branch) |
142 if ha == revlog.nullid: | 154 if ha == revlog.nullid: |
143 return | 155 return |
144 ctx = self.repo.changectx(ha) | 156 ctx = self.repo.changectx(ha) |
146 br_path2 = '' | 158 br_path2 = '' |
147 if br_path != '': | 159 if br_path != '': |
148 br_path2 = br_path + '/' | 160 br_path2 = br_path + '/' |
149 # assuming it is a directory | 161 # assuming it is a directory |
150 self.current.externals[path] = None | 162 self.current.externals[path] = None |
151 prefix = path + '/' | |
152 map(self.current.delete, | |
153 [pat for pat in self.current.files.iterkeys() | |
154 if pat.startswith(prefix)]) | |
155 for f in ctx.walk(util.PrefixMatch(br_path2)): | 163 for f in ctx.walk(util.PrefixMatch(br_path2)): |
156 f_p = '%s/%s' % (path, f[len(br_path2):]) | 164 f_p = '%s/%s' % (path, f[len(br_path2):]) |
157 if f_p not in self.current.files: | 165 if f_p not in self.current.files: |
158 self.current.delete(f_p) | 166 self.current.delete(f_p) |
159 # Remove copied but deleted files | 167 if f_p in self._svncopies: |
160 for f in list(self._files): | 168 del self._svncopies[f_p] |
161 if f.startswith(prefix): | |
162 del self._filebatons[self._files.pop(f)] | |
163 self.current.delete(path) | 169 self.current.delete(path) |
164 if path in self._files: | |
165 del self._filebatons[self._files.pop(path)] | |
166 | 170 |
167 @svnwrap.ieditor | 171 @svnwrap.ieditor |
168 def open_file(self, path, parent_baton, base_revision, p=None): | 172 def open_file(self, path, parent_baton, base_revision, p=None): |
169 fpath, branch = self.meta.split_branch_path(path)[:2] | 173 fpath, branch = self.meta.split_branch_path(path)[:2] |
170 if not fpath: | 174 if not fpath: |
171 self.ui.debug('WARNING: Opening non-existant file %s\n' % path) | 175 self.ui.debug('WARNING: Opening non-existant file %s\n' % path) |
172 return None | 176 return None |
173 | 177 |
174 if path in self._files: | |
175 # Remove this when copied files are no longer registered as | |
176 # open files. | |
177 return self._files[path] | |
178 | |
179 self.ui.note('M %s\n' % path) | 178 self.ui.note('M %s\n' % path) |
179 | |
180 if path in self._svncopies: | |
181 base, isexec, islink, copypath = self._svncopies.pop(path) | |
182 self.current.set(path, base, isexec, islink) | |
183 self.current.copies[path] = copypath | |
184 return self._addfilebaton(path) | |
180 | 185 |
181 if not self.meta.is_path_valid(path): | 186 if not self.meta.is_path_valid(path): |
182 return None | 187 return None |
183 | 188 |
184 baserev = base_revision | 189 baserev = base_revision |
201 return self._addfilebaton(path) | 206 return self._addfilebaton(path) |
202 | 207 |
203 @svnwrap.ieditor | 208 @svnwrap.ieditor |
204 def add_file(self, path, parent_baton=None, copyfrom_path=None, | 209 def add_file(self, path, parent_baton=None, copyfrom_path=None, |
205 copyfrom_revision=None, file_pool=None): | 210 copyfrom_revision=None, file_pool=None): |
211 if path in self._svncopies: | |
212 raise EditingError('trying to replace copied file %s' % path) | |
206 if path in self.current.deleted: | 213 if path in self.current.deleted: |
207 del self.current.deleted[path] | 214 del self.current.deleted[path] |
208 fpath, branch = self.meta.split_branch_path(path, existing=False)[:2] | 215 fpath, branch = self.meta.split_branch_path(path, existing=False)[:2] |
209 if not fpath: | 216 if not fpath: |
210 return None | 217 return None |
281 fromctx = self.repo.changectx(new_hash) | 288 fromctx = self.repo.changectx(new_hash) |
282 if frompath != '/' and frompath != '': | 289 if frompath != '/' and frompath != '': |
283 frompath = '%s/' % frompath | 290 frompath = '%s/' % frompath |
284 else: | 291 else: |
285 frompath = '' | 292 frompath = '' |
293 svncopies = {} | |
286 copies = {} | 294 copies = {} |
287 for f in fromctx: | 295 for f in fromctx: |
288 if not f.startswith(frompath): | 296 if not f.startswith(frompath): |
289 continue | 297 continue |
290 fctx = fromctx.filectx(f) | 298 fctx = fromctx.filectx(f) |
291 dest = path + '/' + f[len(frompath):] | 299 dest = path + '/' + f[len(frompath):] |
292 self.current.set(dest, fctx.data(), 'x' in fctx.flags(), 'l' in fctx.flags()) | 300 flags = fctx.flags() |
293 # Put copied files with open files for now, they should | 301 islink = 'l' in flags |
294 # really be separated to reduce resource usage. | 302 data = fctx.data() |
295 self._addfilebaton(dest) | 303 if islink: |
304 data = 'link ' + data | |
305 svncopies[dest] = (data, 'x' in flags, islink, None) | |
296 if dest in self.current.deleted: | 306 if dest in self.current.deleted: |
307 # Remove this once svn copies and edited files are | |
308 # clearly separated. | |
297 del self.current.deleted[dest] | 309 del self.current.deleted[dest] |
298 if branch == source_branch: | 310 if branch == source_branch: |
299 copies[dest] = f | 311 copies[dest] = f |
300 if copies: | 312 if copies: |
301 # Preserve the directory copy records if no file was changed between | 313 # Preserve the directory copy records if no file was changed between |
302 # the source and destination revisions, or discard it completely. | 314 # the source and destination revisions, or discard it completely. |
303 parentid = self.meta.get_parent_revision(self.current.rev.revnum, branch) | 315 parentid = self.meta.get_parent_revision( |
316 self.current.rev.revnum, branch) | |
304 if parentid != revlog.nullid: | 317 if parentid != revlog.nullid: |
305 parentctx = self.repo.changectx(parentid) | 318 parentctx = self.repo.changectx(parentid) |
306 for k, v in copies.iteritems(): | 319 for k, v in copies.iteritems(): |
307 if util.issamefile(parentctx, fromctx, v): | 320 if util.issamefile(parentctx, fromctx, v): |
308 self.current.copies[k] = v | 321 data, isexec, islink = svncopies[k][:-1] |
322 svncopies[k] = (data, isexec, islink, v) | |
323 self._svncopies.update(svncopies) | |
324 | |
309 # Copy the externals definitions of copied directories | 325 # Copy the externals definitions of copied directories |
310 fromext = svnexternals.parse(self.ui, fromctx) | 326 fromext = svnexternals.parse(self.ui, fromctx) |
311 for p, v in fromext.iteritems(): | 327 for p, v in fromext.iteritems(): |
312 pp = p and (p + '/') or '' | 328 pp = p and (p + '/') or '' |
313 if pp.startswith(frompath): | 329 if pp.startswith(frompath): |
412 except: # pragma: no cover | 428 except: # pragma: no cover |
413 self._exception_info = sys.exc_info() | 429 self._exception_info = sys.exc_info() |
414 raise | 430 raise |
415 return txdelt_window | 431 return txdelt_window |
416 | 432 |
433 def close(self): | |
434 for c in self._svncopies.iteritems(): | |
435 dest, (data, isexec, islink, copied) = c | |
436 self.current.set(dest, data, isexec, islink) | |
437 self.current.copies[dest] = copied | |
438 self._svncopies.clear() | |
439 | |
417 _TXDELT_WINDOW_HANDLER_FAILURE_MSG = ( | 440 _TXDELT_WINDOW_HANDLER_FAILURE_MSG = ( |
418 "Your SVN repository may not be supplying correct replay deltas." | 441 "Your SVN repository may not be supplying correct replay deltas." |
419 " It is strongly" | 442 " It is strongly" |
420 "\nadvised that you repull the entire SVN repository using" | 443 "\nadvised that you repull the entire SVN repository using" |
421 " hg pull --stupid." | 444 " hg pull --stupid." |