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 |
