Mercurial > hgsubversion
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 |