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('')