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."