Mercurial > hgsubversion
comparison fetch_command.py @ 73:9c1b53abefcb
fetch_command: support svn copy detection in stupid mode
author | Patrick Mezard <pmezard@gmail.com> |
---|---|
date | Wed, 05 Nov 2008 13:37:08 +0100 |
parents | 2e30b59a9c19 |
children | 450d5d9d3b80 |
comparison
equal
deleted
inserted
replaced
72:9ec2a12c12ae | 73:9c1b53abefcb |
---|---|
215 def make_diff_path(b): | 215 def make_diff_path(b): |
216 if b == None: | 216 if b == None: |
217 return 'trunk' | 217 return 'trunk' |
218 return 'branches/' + b | 218 return 'branches/' + b |
219 | 219 |
220 def makecopyfinder(r, branchpath, rootdir): | |
221 """Return a function detecting copies. | |
222 | |
223 Returned copyfinder(path) returns None if no copy information can | |
224 be found or ((source, sourcerev), sourcepath) where "sourcepath" is the | |
225 copy source path, "sourcerev" the source svn revision and "source" is the | |
226 copy record path causing the copy to occur. If a single file was copied | |
227 "sourcepath" and "source" are the same, while file copies dectected from | |
228 directory copies return the copied source directory in "source". | |
229 """ | |
230 # filter copy information for current branch | |
231 branchpath = branchpath + '/' | |
232 fullbranchpath = rootdir + branchpath | |
233 copies = [] | |
234 for path, e in r.paths.iteritems(): | |
235 if not e.copyfrom_path: | |
236 continue | |
237 if not path.startswith(branchpath): | |
238 continue | |
239 if not e.copyfrom_path.startswith(fullbranchpath): | |
240 # ignore cross branch copies | |
241 continue | |
242 dest = path[len(branchpath):] | |
243 source = e.copyfrom_path[len(fullbranchpath):] | |
244 copies.append((dest, (source, e.copyfrom_rev))) | |
245 | |
246 copies.sort() | |
247 copies.reverse() | |
248 exactcopies = dict(copies) | |
249 | |
250 def finder(path): | |
251 if path in exactcopies: | |
252 return exactcopies[path], exactcopies[path][0] | |
253 # look for parent directory copy, longest first | |
254 for dest, (source, sourcerev) in copies: | |
255 dest = dest + '/' | |
256 if not path.startswith(dest): | |
257 continue | |
258 sourcepath = source + '/' + path[len(dest):] | |
259 return (source, sourcerev), sourcepath | |
260 return None | |
261 | |
262 return finder | |
263 | |
264 def getcopies(svn, hg_editor, branch, branchpath, r, files, parentid): | |
265 """Return a mapping {dest: source} for every file copied into r. | |
266 """ | |
267 if parentid == revlog.nullid: | |
268 return {} | |
269 | |
270 # Extract svn copy information, group them by copy source. | |
271 # The idea is to duplicate the replay behaviour where copies are | |
272 # evaluated per copy event (one event for all files in a directory copy, | |
273 # one event for single file copy). We assume that copy events match | |
274 # copy sources in revision info. | |
275 svncopies = {} | |
276 finder = makecopyfinder(r, branchpath, svn.subdir) | |
277 for f in files: | |
278 copy = finder(f) | |
279 if copy: | |
280 svncopies.setdefault(copy[0], []).append((f, copy[1])) | |
281 if not svncopies: | |
282 return {} | |
283 | |
284 # cache changeset contexts and map them to source svn revisions | |
285 parentctx = hg_editor.repo.changectx(parentid) | |
286 ctxs = {} | |
287 def getctx(svnrev): | |
288 if svnrev in ctxs: | |
289 return ctxs[svnrev] | |
290 changeid = hg_editor.get_parent_revision(svnrev + 1, branch) | |
291 ctx = None | |
292 if changeid != revlog.nullid: | |
293 ctx = hg_editor.repo.changectx(changeid) | |
294 ctxs[svnrev] = ctx | |
295 return ctx | |
296 | |
297 # check svn copies really make sense in mercurial | |
298 hgcopies = {} | |
299 for (sourcepath, rev), copies in svncopies.iteritems(): | |
300 sourcectx = getctx(rev) | |
301 if sourcectx is None: | |
302 continue | |
303 sources = [s[1] for s in copies] | |
304 if not hg_editor.aresamefiles(sourcectx, parentctx, sources): | |
305 continue | |
306 hgcopies.update(copies) | |
307 return hgcopies | |
220 | 308 |
221 def stupid_svn_server_pull_rev(ui, svn, hg_editor, r): | 309 def stupid_svn_server_pull_rev(ui, svn, hg_editor, r): |
222 used_diff = True | 310 used_diff = True |
223 delete_all_files = False | 311 delete_all_files = False |
224 # this server fails at replay | 312 # this server fails at replay |
448 files_touched.add(p_real) | 536 files_touched.add(p_real) |
449 for p in hg_editor.repo[parent_ha].manifest().iterkeys(): | 537 for p in hg_editor.repo[parent_ha].manifest().iterkeys(): |
450 # TODO this might not be a required step. | 538 # TODO this might not be a required step. |
451 if p: | 539 if p: |
452 files_touched.add(p) | 540 files_touched.add(p) |
541 | |
542 copies = getcopies(svn, hg_editor, b, branches[b], r, files_touched, | |
543 parent_ha) | |
544 | |
453 date = r.date.replace('T', ' ').replace('Z', '').split('.')[0] | 545 date = r.date.replace('T', ' ').replace('Z', '').split('.')[0] |
454 date += ' -0000' | 546 date += ' -0000' |
455 def filectxfn(repo, memctx, path): | 547 def filectxfn(repo, memctx, path): |
456 disk_path = os.path.join(our_tempdir, path) | 548 disk_path = os.path.join(our_tempdir, path) |
457 if path in link_files: | 549 if path in link_files: |
460 copied=False) | 552 copied=False) |
461 fp = open(disk_path) | 553 fp = open(disk_path) |
462 exe = exec_files.get(path, None) | 554 exe = exec_files.get(path, None) |
463 if exe is None and path in hg_editor.repo[parent_ha]: | 555 if exe is None and path in hg_editor.repo[parent_ha]: |
464 exe = 'x' in hg_editor.repo[parent_ha].filectx(path).flags() | 556 exe = 'x' in hg_editor.repo[parent_ha].filectx(path).flags() |
557 copied = copies.get(path) | |
465 return context.memfilectx(path=path, data=fp.read(), islink=False, | 558 return context.memfilectx(path=path, data=fp.read(), islink=False, |
466 isexec=exe, copied=False) | 559 isexec=exe, copied=copied) |
467 extra = {} | 560 extra = {} |
468 if b: | 561 if b: |
469 extra['branch'] = b | 562 extra['branch'] = b |
470 if '' in files_touched: | 563 if '' in files_touched: |
471 files_touched.remove('') | 564 files_touched.remove('') |