comparison hgsubversion/stupid.py @ 588:2723152c8111

stupid: fix getcopies() logic getcopies() assumed that copies where happening withing the current branch. This is wrong when a branch replaces another, and used to generate wrong copy records when copy sources existed in parent revision but were coming from an unrelated revision.
author Patrick Mezard <pmezard@gmail.com>
date Tue, 02 Mar 2010 17:06:06 +0100
parents a016b253910b
children 0fe490ce2fbb
comparison
equal deleted inserted replaced
587:c06f59441f8e 588:2723152c8111
252 return context.memfilectx(path=path, data=data, islink=islink, 252 return context.memfilectx(path=path, data=data, islink=islink,
253 isexec=isexe, copied=copied) 253 isexec=isexe, copied=copied)
254 254
255 return list(touched_files), filectxfn 255 return list(touched_files), filectxfn
256 256
257 def makecopyfinder(r, branchpath, rootdir): 257 def makecopyfinder(meta, r, branchpath):
258 """Return a function detecting copies. 258 """Return a function detecting copies.
259 259
260 Returned copyfinder(path) returns None if no copy information can 260 Returned copyfinder(path) returns None if no copy information can
261 be found or ((source, sourcerev), sourcepath) where "sourcepath" is the 261 be found or ((source, sourcerev), sourcepath) where "sourcepath" is the
262 copy source path, "sourcerev" the source svn revision and "source" is the 262 copy source path, "sourcerev" the source svn revision and "source" is the
263 copy record path causing the copy to occur. If a single file was copied 263 copy record path causing the copy to occur. If a single file was copied
264 "sourcepath" and "source" are the same, while file copies dectected from 264 "sourcepath" and "source" are the same, while file copies dectected from
265 directory copies return the copied source directory in "source". 265 directory copies return the copied source directory in "source".
266 """ 266 """
267 # cache changeset contexts and map them to source svn revisions
268 ctxs = {}
269 def getctx(branch, svnrev):
270 if svnrev in ctxs:
271 return ctxs[svnrev]
272 changeid = meta.get_parent_revision(svnrev + 1, branch, True)
273 ctx = None
274 if changeid != revlog.nullid:
275 ctx = meta.repo.changectx(changeid)
276 ctxs[svnrev] = ctx
277 return ctx
278
267 # filter copy information for current branch 279 # filter copy information for current branch
268 branchpath = (branchpath and branchpath + '/') or '' 280 branchpath = (branchpath and branchpath + '/') or ''
269 fullbranchpath = rootdir + branchpath
270 copies = [] 281 copies = []
271 for path, e in r.paths.iteritems(): 282 for path, e in r.paths.iteritems():
272 if not e.copyfrom_path: 283 if not e.copyfrom_path:
273 continue 284 continue
274 if not path.startswith(branchpath): 285 if not path.startswith(branchpath):
275 continue 286 continue
276 if not e.copyfrom_path.startswith(fullbranchpath): 287 # compute converted source path and revision
277 # ignore cross branch copies 288 frompath, frombranch = meta.split_branch_path(e.copyfrom_path)[:2]
278 continue 289 if frompath is None:
279 dest = path[len(branchpath):] 290 continue
280 source = e.copyfrom_path[len(fullbranchpath):] 291 fromctx = getctx(frombranch, e.copyfrom_rev)
281 copies.append((dest, (source, e.copyfrom_rev))) 292 if fromctx is None:
293 continue
294 destpath = path[len(branchpath):]
295 copies.append((destpath, (frompath, fromctx)))
282 296
283 copies.sort(reverse=True) 297 copies.sort(reverse=True)
284 exactcopies = dict(copies) 298 exactcopies = dict(copies)
285 299
286 def finder(path): 300 def finder(path):
287 if path in exactcopies: 301 if path in exactcopies:
288 return exactcopies[path], exactcopies[path][0] 302 return exactcopies[path], exactcopies[path][0]
289 # look for parent directory copy, longest first 303 # look for parent directory copy, longest first
290 for dest, (source, sourcerev) in copies: 304 for dest, (source, sourcectx) in copies:
291 dest = dest + '/' 305 dest = dest + '/'
292 if not path.startswith(dest): 306 if not path.startswith(dest):
293 continue 307 continue
294 sourcepath = source + '/' + path[len(dest):] 308 sourcepath = source + '/' + path[len(dest):]
295 return (source, sourcerev), sourcepath 309 return (source, sourcectx), sourcepath
296 return None 310 return None
297 311
298 return finder 312 return finder
299 313
300 def getcopies(svn, meta, branch, branchpath, r, files, parentctx): 314 def getcopies(svn, meta, branch, branchpath, r, files, parentctx):
307 # The idea is to duplicate the replay behaviour where copies are 321 # The idea is to duplicate the replay behaviour where copies are
308 # evaluated per copy event (one event for all files in a directory copy, 322 # evaluated per copy event (one event for all files in a directory copy,
309 # one event for single file copy). We assume that copy events match 323 # one event for single file copy). We assume that copy events match
310 # copy sources in revision info. 324 # copy sources in revision info.
311 svncopies = {} 325 svncopies = {}
312 finder = makecopyfinder(r, branchpath, svn.subdir) 326 finder = makecopyfinder(meta, r, branchpath)
313 for f in files: 327 for f in files:
314 copy = finder(f) 328 copy = finder(f)
315 if copy: 329 if copy:
316 svncopies.setdefault(copy[0], []).append((f, copy[1])) 330 svncopies.setdefault(copy[0], []).append((f, copy[1]))
317 if not svncopies: 331 if not svncopies:
318 return {} 332 return {}
319 333
320 # cache changeset contexts and map them to source svn revisions
321 ctxs = {}
322 def getctx(svnrev):
323 if svnrev in ctxs:
324 return ctxs[svnrev]
325 changeid = meta.get_parent_revision(svnrev + 1, branch)
326 ctx = None
327 if changeid != revlog.nullid:
328 ctx = meta.repo.changectx(changeid)
329 ctxs[svnrev] = ctx
330 return ctx
331
332 # check svn copies really make sense in mercurial 334 # check svn copies really make sense in mercurial
333 hgcopies = {} 335 hgcopies = {}
334 for (sourcepath, rev), copies in svncopies.iteritems(): 336 for (sourcepath, sourcectx), copies in svncopies.iteritems():
335 sourcectx = getctx(rev)
336 if sourcectx is None:
337 continue
338 for k, v in copies: 337 for k, v in copies:
339 if not util.issamefile(sourcectx, parentctx, v): 338 if not util.issamefile(sourcectx, parentctx, v):
340 continue 339 continue
341 hgcopies.update({k: v}) 340 hgcopies.update({k: v})
342 return hgcopies 341 return hgcopies