def convert_rev(ui, meta, svn, r, tbdelta, firstrun): # this server fails at replay if meta.filemap: raise hgutil.Abort( 'filemaps currently unsupported with stupid replay.') branches = branches_in_paths(meta, tbdelta, r.paths, r.revnum, svn.checkpath, svn.list_files, firstrun) brpaths = branches.values() bad_branch_paths = {} for br, bp in branches.iteritems(): bad_branch_paths[br] = [] # This next block might be needed, but for now I'm omitting it until it # can be proven necessary. # for bad in brpaths: # if bad.startswith(bp) and len(bad) > len(bp): # bad_branch_paths[br].append(bad[len(bp)+1:]) # We've go a branch that contains other branches. We have to be careful # to get results similar to real replay in this case. for existingbr in meta.branches: bad = meta.remotename(existingbr) if bad.startswith(bp) and len(bad) > len(bp): bad_branch_paths[br].append(bad[len(bp) + 1:]) deleted_branches = {} for p in r.paths: tag = meta.get_path_tag(p) if tag and tag not in meta.tags: continue branch = meta.localname(p) if not (r.paths[p].action == 'R' and branch in meta.branches): continue # Check the branch is not being replaced by one of its # ancestors, it happens a lot with project-wide reverts. frompath = r.paths[p].copyfrom_path frompath, frombranch = meta.split_branch_path(frompath, existing=False)[:2] if frompath == '': fromnode = meta.get_parent_revision(r.paths[p].copyfrom_rev + 1, frombranch, exact=True) if fromnode != node.nullid: fromctx = meta.repo[fromnode] pctx = meta.repo[meta.get_parent_revision(r.revnum, branch, exact=True)] if util.isancestor(pctx, fromctx): continue closed = checkbranch(meta, r, branch) if closed is not None: deleted_branches[branch] = closed date = meta.fixdate(r.date) check_deleted_branches = set(tbdelta['branches'][1]) for b in branches: parentctx = meta.repo[meta.get_parent_revision(r.revnum, b)] tag = meta.get_path_tag(meta.remotename(b)) kind = svn.checkpath(branches[b], r.revnum) if kind != 'd': if not tag: # Branch does not exist at this revision. Get parent # revision and remove everything. deleted_branches[b] = parentctx.node() continue # The nullrev check might not be necessary in theory but svn < # 1.7 failed to diff branch creation so the diff_branchrev() # path does not support this case with svn >= 1.7. We can fix # it, or we can force the existing fetch_branchrev() path. Do # the latter for now. incremental = (meta.firstpulled > 0 and parentctx.rev() != node.nullrev and not firstrun) if incremental: try: files_touched, filectxfn2 = diff_branchrev( ui, svn, meta, b, branches[b], r, parentctx) except BadPatchApply, e: # Either this revision or the previous one does not exist. ui.note("Fetching entire revision: %s.\n" % e.args[0]) incremental = False if not incremental: files_touched, filectxfn2 = fetch_branchrev( svn, meta, b, branches[b], r, parentctx) externals = {} if meta.layout != 'single': externals = fetch_externals(ui, svn, branches[b], r, parentctx) externals = svnexternals.getchanges(ui, meta.repo, parentctx, externals) files_touched.extend(externals) def filectxfn(repo, memctx, path): if path in externals: if externals[path] is None: raise IOError(errno.ENOENT, 'no externals') return compathacks.makememfilectx(repo, path=path, data=externals[path], islink=False, isexec=False, copied=None) for bad in bad_branch_paths[b]: if path.startswith(bad): raise IOError(errno.ENOENT, 'Path %s is bad' % path) return filectxfn2(repo, memctx, path) if '' in files_touched: files_touched.remove('') excluded = [f for f in files_touched if f not in meta.filemap] for f in excluded: files_touched.remove(f) if b: # Regular tag without modifications, it will be committed by # svnmeta.committag(), we can skip the whole branch for now if (tag and tag not in meta.tags and b not in meta.branches and b not in compathacks.branchset(meta.repo) and not files_touched): continue if parentctx.node() == node.nullid and not files_touched: meta.repo.ui.debug( 'skipping commit since parent is null and no files touched.\n') continue for f in files_touched: if f: # this is a case that really shouldn't ever happen, it means # something is very wrong assert f[0] != '/' extra = meta.genextra(r.revnum, b) if tag: if parentctx.node() == node.nullid: continue extra.update({ 'branch': parentctx.extra().get('branch', None), 'close': 1 }) origbranch = extra.get('branch', None) meta.mapbranch(extra) current_ctx = context.memctx(meta.repo, [parentctx.node(), revlog.nullid], meta.getmessage(r), files_touched, filectxfn, meta.authors[r.author], date, extra) ha = meta.repo.svn_commitctx(current_ctx) if not tag: if (not origbranch in meta.branches and not meta.get_path_tag(meta.remotename(origbranch))): meta.branches[origbranch] = None, 0, r.revnum meta.revmap[r.revnum, b] = ha else: meta.movetag(tag, ha, r, date) meta.addedtags.pop(tag, None) util.describe_commit(ui, ha, b)
def _convert_rev(ui, meta, svn, r, tbdelta, firstrun): editor = meta.editor editor.current.clear() editor.current.rev = r editor.setsvn(svn) if firstrun and meta.firstpulled <= 0: # We know nothing about this project, so fetch everything before # trying to apply deltas. ui.debug('replay: fetching full revision\n') svn.get_revision(r.revnum, editor) else: svn.get_replay(r.revnum, editor, meta.firstpulled) editor.close() current = editor.current updateexternals(ui, meta, current) if current.exception is not None: # pragma: no cover traceback.print_exception(*current.exception) raise ReplayException() files_to_commit = current.files() branch_batches = {} rev = current.rev date = meta.fixdate(rev.date) # build up the branches that have files on them failoninvalid = ui.configbool('hgsubversion', 'failoninvalidreplayfile', False) for f in files_to_commit: if not meta.is_path_valid(f): if failoninvalid: raise hgutil.Abort('file %s should not be in commit list' % f) continue p, b = meta.split_branch_path(f)[:2] if b not in branch_batches: branch_batches[b] = [] if p: branch_batches[b].append((p, f)) closebranches = {} for branch in tbdelta['branches'][1]: branchedits = meta.revmap.branchedits(branch, rev) if len(branchedits) < 1: # can't close a branch that never existed continue ha = branchedits[0][1] closebranches[branch] = ha extraempty = (set(tbdelta['branches'][0]) - (set(current.emptybranches) | set(branch_batches.keys()))) current.emptybranches.update([(x, False) for x in extraempty]) # 1. handle normal commits closedrevs = closebranches.values() for branch, files in branch_batches.iteritems(): if branch in current.emptybranches and files: del current.emptybranches[branch] if meta.skipbranch(branch): # make sure we also get rid of it from emptybranches if branch in current.emptybranches: del current.emptybranches[branch] continue files = dict(files) parents = meta.get_parent_revision(rev.revnum, branch), revlog.nullid if parents[0] in closedrevs and branch in meta.closebranches: continue extra = meta.genextra(rev.revnum, branch) tag = None if branch is not None: # New regular tag without modifications, it will be committed by # svnmeta.committag(), we can skip the whole branch for now tag = meta.get_path_tag(meta.remotename(branch)) if (tag and tag not in meta.tags and branch not in meta.branches and branch not in compathacks.branchset(meta.repo) and not files): continue parentctx = meta.repo.changectx(parents[0]) if tag: if parentctx.node() == node.nullid: continue extra.update({ 'branch': parentctx.extra().get('branch', None), 'close': 1 }) def filectxfn(repo, memctx, path): current_file = files[path] try: data, isexec, islink, copied = current.pop(current_file) except IOError: return compathacks.filectxfn_deleted_reraise(memctx) if isexec is None or islink is None: flags = parentctx.flags(path) if isexec is None: isexec = 'x' in flags if islink is None: islink = 'l' in flags if data is not None: if islink: if data.startswith('link '): data = data[len('link '):] else: ui.debug('file marked as link, but may contain data: ' '%s\n' % current_file) else: data = parentctx.filectx(path).data() return compathacks.makememfilectx(repo, path=path, data=data, islink=islink, isexec=isexec, copied=copied) meta.mapbranch(extra) current_ctx = context.memctx(meta.repo, parents, meta.getmessage(rev), files.keys(), filectxfn, meta.authors[rev.author], date, extra) new_hash = meta.repo.svn_commitctx(current_ctx) util.describe_commit(ui, new_hash, branch) if (rev.revnum, branch) not in meta.revmap and not tag: meta.revmap[rev.revnum, branch] = new_hash if tag: meta.movetag(tag, new_hash, rev, date) meta.addedtags.pop(tag, None) # 2. handle branches that need to be committed without any files for branch in current.emptybranches: if meta.skipbranch(branch): continue ha = meta.get_parent_revision(rev.revnum, branch) if ha == node.nullid: continue parent_ctx = meta.repo.changectx(ha) files = [] def del_all_files(*args): raise IOError(errno.ENOENT, 'deleting all files') # True here means nuke all files. This happens when you # replace a branch root with an empty directory if current.emptybranches[branch]: files = meta.repo[ha].files() extra = meta.genextra(rev.revnum, branch) meta.mapbranch(extra) current_ctx = context.memctx(meta.repo, (ha, node.nullid), meta.getmessage(rev), files, del_all_files, meta.authors[rev.author], date, extra) new_hash = meta.repo.svn_commitctx(current_ctx) util.describe_commit(ui, new_hash, branch) if (rev.revnum, branch) not in meta.revmap: meta.revmap[rev.revnum, branch] = new_hash return closebranches
def convert_rev(ui, meta, svn, r, tbdelta, firstrun): # this server fails at replay if meta.filemap: raise hgutil.Abort('filemaps currently unsupported with stupid replay.') branches = branches_in_paths(meta, tbdelta, r.paths, r.revnum, svn.checkpath, svn.list_files, firstrun) brpaths = branches.values() bad_branch_paths = {} for br, bp in branches.iteritems(): bad_branch_paths[br] = [] # This next block might be needed, but for now I'm omitting it until it # can be proven necessary. # for bad in brpaths: # if bad.startswith(bp) and len(bad) > len(bp): # bad_branch_paths[br].append(bad[len(bp)+1:]) # We've go a branch that contains other branches. We have to be careful # to get results similar to real replay in this case. for existingbr in meta.branches: bad = meta.remotename(existingbr) if bad.startswith(bp) and len(bad) > len(bp): bad_branch_paths[br].append(bad[len(bp)+1:]) deleted_branches = {} for p in r.paths: tag = meta.get_path_tag(p) if tag and tag not in meta.tags: continue branch = meta.localname(p) if not (r.paths[p].action == 'R' and branch in meta.branches): continue # Check the branch is not being replaced by one of its # ancestors, it happens a lot with project-wide reverts. frompath = r.paths[p].copyfrom_path frompath, frombranch = meta.split_branch_path( frompath, existing=False)[:2] if frompath == '': fromnode = meta.get_parent_revision( r.paths[p].copyfrom_rev + 1, frombranch, exact=True) if fromnode != node.nullid: fromctx = meta.repo[fromnode] pctx = meta.repo[meta.get_parent_revision( r.revnum, branch, exact=True)] if util.isancestor(pctx, fromctx): continue closed = checkbranch(meta, r, branch) if closed is not None: deleted_branches[branch] = closed date = meta.fixdate(r.date) check_deleted_branches = set(tbdelta['branches'][1]) for b in branches: parentctx = meta.repo[meta.get_parent_revision(r.revnum, b)] tag = meta.get_path_tag(meta.remotename(b)) kind = svn.checkpath(branches[b], r.revnum) if kind != 'd': if not tag: # Branch does not exist at this revision. Get parent # revision and remove everything. deleted_branches[b] = parentctx.node() continue # The nullrev check might not be necessary in theory but svn < # 1.7 failed to diff branch creation so the diff_branchrev() # path does not support this case with svn >= 1.7. We can fix # it, or we can force the existing fetch_branchrev() path. Do # the latter for now. incremental = (meta.firstpulled > 0 and parentctx.rev() != node.nullrev and not firstrun) if incremental: try: files_touched, filectxfn2 = diff_branchrev( ui, svn, meta, b, branches[b], r, parentctx) except BadPatchApply, e: # Either this revision or the previous one does not exist. ui.note("Fetching entire revision: %s.\n" % e.args[0]) incremental = False if not incremental: files_touched, filectxfn2 = fetch_branchrev( svn, meta, b, branches[b], r, parentctx) externals = {} if meta.layout != 'single': externals = fetch_externals(ui, svn, branches[b], r, parentctx) externals = svnexternals.getchanges(ui, meta.repo, parentctx, externals) files_touched.extend(externals) def filectxfn(repo, memctx, path): if path in externals: if externals[path] is None: raise IOError(errno.ENOENT, 'no externals') return compathacks.makememfilectx(repo, path=path, data=externals[path], islink=False, isexec=False, copied=None) for bad in bad_branch_paths[b]: if path.startswith(bad): raise IOError(errno.ENOENT, 'Path %s is bad' % path) return filectxfn2(repo, memctx, path) if '' in files_touched: files_touched.remove('') excluded = [f for f in files_touched if f not in meta.filemap] for f in excluded: files_touched.remove(f) if b: # Regular tag without modifications, it will be committed by # svnmeta.committag(), we can skip the whole branch for now if (tag and tag not in meta.tags and b not in meta.branches and b not in compathacks.branchset(meta.repo) and not files_touched): continue if parentctx.node() == node.nullid and not files_touched: meta.repo.ui.debug('skipping commit since parent is null and no files touched.\n') continue for f in files_touched: if f: # this is a case that really shouldn't ever happen, it means # something is very wrong assert f[0] != '/' extra = meta.genextra(r.revnum, b) if tag: if parentctx.node() == node.nullid: continue extra.update({'branch': parentctx.extra().get('branch', None), 'close': 1}) origbranch = extra.get('branch', None) meta.mapbranch(extra) current_ctx = context.memctx(meta.repo, [parentctx.node(), revlog.nullid], meta.getmessage(r), files_touched, filectxfn, meta.authors[r.author], date, extra) ha = meta.repo.svn_commitctx(current_ctx) if not tag: if (not origbranch in meta.branches and not meta.get_path_tag(meta.remotename(origbranch))): meta.branches[origbranch] = None, 0, r.revnum meta.revmap[r.revnum, b] = ha else: meta.movetag(tag, ha, r, date) meta.addedtags.pop(tag, None) util.describe_commit(ui, ha, b)
def _convert_rev(ui, meta, svn, r, tbdelta, firstrun): editor = meta.editor editor.current.clear() editor.current.rev = r editor.setsvn(svn) if firstrun and meta.firstpulled <= 0: # We know nothing about this project, so fetch everything before # trying to apply deltas. ui.debug('replay: fetching full revision\n') svn.get_revision(r.revnum, editor) else: svn.get_replay(r.revnum, editor, meta.firstpulled) editor.close() current = editor.current updateexternals(ui, meta, current) if current.exception is not None: # pragma: no cover traceback.print_exception(*current.exception) raise ReplayException() files_to_commit = current.files() branch_batches = {} rev = current.rev date = meta.fixdate(rev.date) # build up the branches that have files on them failoninvalid = ui.configbool('hgsubversion', 'failoninvalidreplayfile', False) for f in files_to_commit: if not meta.is_path_valid(f): if failoninvalid: raise hgutil.Abort('file %s should not be in commit list' % f) continue p, b = meta.split_branch_path(f)[:2] if b not in branch_batches: branch_batches[b] = [] if p: branch_batches[b].append((p, f)) closebranches = {} for branch in tbdelta['branches'][1]: branchedits = meta.revmap.branchedits(branch, rev) if len(branchedits) < 1: # can't close a branch that never existed continue ha = branchedits[0][1] closebranches[branch] = ha extraempty = (set(tbdelta['branches'][0]) - (set(current.emptybranches) | set(branch_batches.keys()))) current.emptybranches.update([(x, False) for x in extraempty]) # 1. handle normal commits closedrevs = closebranches.values() for branch, files in branch_batches.iteritems(): if branch in current.emptybranches and files: del current.emptybranches[branch] if meta.skipbranch(branch): # make sure we also get rid of it from emptybranches if branch in current.emptybranches: del current.emptybranches[branch] continue files = dict(files) parents = meta.get_parent_revision(rev.revnum, branch), revlog.nullid if parents[0] in closedrevs and branch in meta.closebranches: continue extra = meta.genextra(rev.revnum, branch) tag = None if branch is not None: # New regular tag without modifications, it will be committed by # svnmeta.committag(), we can skip the whole branch for now tag = meta.get_path_tag(meta.remotename(branch)) if (tag and tag not in meta.tags and branch not in meta.branches and branch not in compathacks.branchset(meta.repo) and not files): continue parentctx = meta.repo.changectx(parents[0]) if tag: if parentctx.node() == node.nullid: continue extra.update({'branch': parentctx.extra().get('branch', None), 'close': 1}) def filectxfn(repo, memctx, path): current_file = files[path] try: data, isexec, islink, copied = current.pop(current_file) except IOError: return compathacks.filectxfn_deleted_reraise(memctx) if isexec is None or islink is None: flags = parentctx.flags(path) if isexec is None: isexec = 'x' in flags if islink is None: islink = 'l' in flags if data is not None: if islink: if data.startswith('link '): data = data[len('link '):] else: ui.debug('file marked as link, but may contain data: ' '%s\n' % current_file) else: data = parentctx.filectx(path).data() return compathacks.makememfilectx(repo, path=path, data=data, islink=islink, isexec=isexec, copied=copied) meta.mapbranch(extra) current_ctx = context.memctx(meta.repo, parents, meta.getmessage(rev), files.keys(), filectxfn, meta.authors[rev.author], date, extra) new_hash = meta.repo.svn_commitctx(current_ctx) util.describe_commit(ui, new_hash, branch) if (rev.revnum, branch) not in meta.revmap and not tag: meta.revmap[rev.revnum, branch] = new_hash if tag: meta.movetag(tag, new_hash, rev, date) meta.addedtags.pop(tag, None) # 2. handle branches that need to be committed without any files for branch in current.emptybranches: if meta.skipbranch(branch): continue ha = meta.get_parent_revision(rev.revnum, branch) if ha == node.nullid: continue parent_ctx = meta.repo.changectx(ha) files = [] def del_all_files(*args): raise IOError(errno.ENOENT, 'deleting all files') # True here means nuke all files. This happens when you # replace a branch root with an empty directory if current.emptybranches[branch]: files = meta.repo[ha].files() extra = meta.genextra(rev.revnum, branch) meta.mapbranch(extra) current_ctx = context.memctx(meta.repo, (ha, node.nullid), meta.getmessage(rev), files, del_all_files, meta.authors[rev.author], date, extra) new_hash = meta.repo.svn_commitctx(current_ctx) util.describe_commit(ui, new_hash, branch) if (rev.revnum, branch) not in meta.revmap: meta.revmap[rev.revnum, branch] = new_hash return closebranches