示例#1
0
 def delbranch(self, branch, node, rev):
     pctx = self.repo[node]
     files = pctx.manifest().keys()
     extra = self.genextra(rev.revnum, branch)
     self.mapbranch(extra, True)
     ctx = context.memctx(self.repo, (node, revlog.nullid), rev.message
                          or util.default_commit_msg(self.ui), [],
                          lambda x, y, z: None, self.authors[rev.author],
                          self.fixdate(rev.date), extra)
     new = self.repo.commitctx(ctx)
     self.ui.status('Marked branch %s as closed.\n' % (branch or 'default'))
示例#2
0
 def delbranch(self, branch, node, rev):
     pctx = self.repo[node]
     files = pctx.manifest().keys()
     extra = self.genextra(rev.revnum, branch)
     self.mapbranch(extra, True)
     ctx = context.memctx(self.repo,
                          (node, revlog.nullid),
                          rev.message or util.default_commit_msg(self.ui),
                          [],
                          lambda x, y, z: None,
                          self.authors[rev.author],
                          self.fixdate(rev.date),
                          extra)
     new = self.repo.svn_commitctx(ctx)
     self.ui.status('Marked branch %s as closed.\n' % (branch or 'default'))
示例#3
0
    def movetag(self, tag, hash, rev, date):
        if tag in self.tags and self.tags[tag] == hash:
            return

        # determine branch from earliest unclosed ancestor
        branchparent = self.repo[hash]
        while branchparent.extra().get('close'):
            branchparent = branchparent.parents()[0]
        branch = self.get_source_rev(ctx=branchparent)[1]

        parentctx = self.repo[self.get_parent_revision(rev.revnum + 1, branch)]
        if '.hgtags' in parentctx:
            tagdata = parentctx.filectx('.hgtags').data()
        else:
            tagdata = ''
        tagdata += '%s %s\n' % (node.hex(hash), self.tagmap.get(tag, tag))
        def hgtagsfn(repo, memctx, path):
            assert path == '.hgtags'
            return context.memfilectx(path=path,
                                      data=tagdata,
                                      islink=False,
                                      isexec=False,
                                      copied=False)
        revnum, branch = self.get_source_rev(ctx=parentctx)[:2]
        newparent = None
        for child in parentctx.children():
            if (self.get_source_rev(ctx=child)[1] == branch
                and child.extra().get('close', False)):
                newparent = child
        if newparent:
            parentctx = newparent
            revnum, branch = self.get_source_rev(ctx=parentctx)[:2]
        ctx = context.memctx(self.repo,
                             (parentctx.node(), node.nullid),
                             rev.message or util.default_commit_msg(self.ui),
                             ['.hgtags', ],
                             hgtagsfn,
                             self.authors[rev.author],
                             date,
                             parentctx.extra())
        new_hash = self.repo.svn_commitctx(ctx)
        if not newparent:
            assert self.revmap[revnum, branch] == parentctx.node()
            self.revmap[revnum, branch] = new_hash
        self.tags[tag] = hash, rev.revnum
        util.describe_commit(self.ui, new_hash, branch)
示例#4
0
    def movetag(self, tag, hash, rev, date):
        if tag in self.tags and self.tags[tag] == hash:
            return

        # determine branch from earliest unclosed ancestor
        branchparent = self.repo[hash]
        while branchparent.extra().get('close'):
            branchparent = branchparent.parents()[0]
        branch = self.get_source_rev(ctx=branchparent)[1]

        parentctx = self.repo[self.get_parent_revision(rev.revnum + 1, branch)]
        if '.hgtags' in parentctx:
            tagdata = parentctx.filectx('.hgtags').data()
        else:
            tagdata = ''
        tagdata += '%s %s\n' % (node.hex(hash), self.tagmap.get(tag, tag))

        def hgtagsfn(repo, memctx, path):
            assert path == '.hgtags'
            return context.memfilectx(path=path,
                                      data=tagdata,
                                      islink=False,
                                      isexec=False,
                                      copied=False)

        revnum, branch = self.get_source_rev(ctx=parentctx)[:2]
        newparent = None
        for child in parentctx.children():
            if (self.get_source_rev(ctx=child)[1] == branch
                    and child.extra().get('close', False)):
                newparent = child
        if newparent:
            parentctx = newparent
            revnum, branch = self.get_source_rev(ctx=parentctx)[:2]
        ctx = context.memctx(self.repo, (parentctx.node(), node.nullid),
                             rev.message or util.default_commit_msg(self.ui), [
                                 '.hgtags',
                             ], hgtagsfn, self.authors[rev.author], date,
                             parentctx.extra())
        new_hash = self.repo.commitctx(ctx)
        if not newparent:
            assert self.revmap[revnum, branch] == parentctx.node()
            self.revmap[revnum, branch] = new_hash
        self.tags[tag] = hash, rev.revnum
        util.describe_commit(self.ui, new_hash, branch)
示例#5
0
def pull(repo, source, heads=[], force=False):
    """pull new revisions from Subversion"""
    assert source.capable('subversion')
    svn_url = source.svnurl

    # Split off #rev
    svn_url, heads, checkout = util.parseurl(svn_url, heads)
    old_encoding = util.swap_out_encoding()

    try:
        stopat_rev = int(checkout or 0)
    except ValueError:
        raise hgutil.Abort('unrecognised Subversion revision %s: '
                           'only numbers work.' % checkout)

    have_replay = not repo.ui.configbool('hgsubversion', 'stupid')
    if not have_replay:
        repo.ui.note('fetching stupidly...\n')

    svn = source.svn
    meta = repo.svnmeta(svn.uuid, svn.subdir)

    layout = repo.ui.config('hgsubversion', 'layout', 'auto')
    if layout == 'auto':
        rootlist = svn.list_dir('', revision=(stopat_rev or None))
        if sum(map(lambda x: x in rootlist, ('branches', 'tags', 'trunk'))):
            layout = 'standard'
        else:
            layout = 'single'
        repo.ui.setconfig('hgsubversion', 'layout', layout)
        repo.ui.note('using %s layout\n' % layout)

    branch = repo.ui.config('hgsubversion', 'branch')
    if branch:
        if layout != 'single':
            msg = ('branch cannot be specified for Subversion clones using '
                   'standard directory layout')
            raise hgutil.Abort(msg)

        meta.branchmap['default'] = branch

    ui = repo.ui
    start = meta.revmap.youngest
    origrevcount = len(meta.revmap)

    if start <= 0:
        # we are initializing a new repository
        start = repo.ui.config('hgsubversion', 'startrev', 0)
        if isinstance(start, str) and start.upper() == 'HEAD':
            start = svn.last_changed_rev
        else:
            start = int(start)

        if start > 0:
            if layout == 'standard':
                raise hgutil.Abort('non-zero start revisions are only '
                                   'supported for single-directory clones.')
            ui.note('starting at revision %d; any prior will be ignored\n'
                    % start)
            # fetch all revisions *including* the one specified...
            start -= 1

        # anything less than zero makes no sense
        if start < 0:
            start = 0

    oldrevisions = len(meta.revmap)
    if stopat_rev:
        total = stopat_rev - start
    else:
        total = svn.HEAD - start
    try:
        try:
            # start converting revisions
            firstrun = True
            for r in svn.revisions(start=start, stop=stopat_rev):
                if (r.author is None and
                    r.message == 'This is an empty revision for padding.'):
                    continue
                tbdelta = meta.update_branch_tag_map_for_rev(r)
                # got a 502? Try more than once!
                tries = 0
                converted = False
                while not converted:
                    try:
                        msg = ''
                        if r.message:
                            msg = r.message.strip()
                        if not msg:
                            msg = util.default_commit_msg(ui)
                        else:
                            msg = [s.strip() for s in msg.splitlines() if s][0]
                        if getattr(ui, 'termwidth', False):
                            w = ui.termwidth()
                        else:
                            w = hgutil.termwidth()
                        bits = (r.revnum, r.author, msg)
                        ui.status(('[r%d] %s: %s' % bits)[:w] + '\n')
                        util.progress(ui, 'pull', r.revnum - start, total=total)

                        meta.save_tbdelta(tbdelta)
                        close = pullfuns[have_replay](ui, meta, svn, r, tbdelta,
                                                      firstrun)
                        meta.committags(r, close)
                        for branch, parent in close.iteritems():
                            if parent in (None, node.nullid):
                                continue
                            meta.delbranch(branch, parent, r)

                        meta.save()
                        converted = True
                        firstrun = False

                    except svnwrap.SubversionRepoCanNotReplay, e: #pragma: no cover
                        ui.status('%s\n' % e.message)
                        stupidmod.print_your_svn_is_old_message(ui)
                        have_replay = False
                    except svnwrap.SubversionException, e: #pragma: no cover
                        if (e.args[1] == svnwrap.ERR_RA_DAV_REQUEST_FAILED
                            and '502' in str(e)
                            and tries < 3):
                            tries += 1
                            ui.status('Got a 502, retrying (%s)\n' % tries)
                        else:
                            ui.traceback()
                            raise hgutil.Abort(*e.args)
示例#6
0
def convert_rev(ui, meta, svn, r, tbdelta, firstrun):
    # this server fails at replay

    branches = branches_in_paths(meta, tbdelta, r.paths, r.revnum,
                                 svn.checkpath, svn.list_files)
    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
        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

        incremental = (meta.revmap.oldest > 0)

        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 context.memfilectx(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 meta.repo.branchtags()
                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],
                                     r.message or util.default_commit_msg(ui),
                                     files_touched,
                                     filectxfn,
                                     meta.authors[r.author],
                                     date,
                                     extra)
        ha = meta.repo.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)
示例#7
0
def pull(repo, source, heads=[], force=False):
    """pull new revisions from Subversion"""
    assert source.capable('subversion')
    svn_url = source.svnurl

    # Split off #rev
    svn_url, heads, checkout = util.parseurl(svn_url, heads)
    old_encoding = util.swap_out_encoding()

    try:
        stopat_rev = int(checkout or 0)
    except ValueError:
        raise hgutil.Abort('unrecognised Subversion revision %s: '
                           'only numbers work.' % checkout)

    have_replay = not repo.ui.configbool('hgsubversion', 'stupid')
    if not have_replay:
        repo.ui.note('fetching stupidly...\n')

    svn = source.svn
    meta = repo.svnmeta(svn.uuid, svn.subdir)

    layout = repo.ui.config('hgsubversion', 'layout', 'auto')
    if layout == 'auto':
        rootlist = svn.list_dir('', revision=(stopat_rev or None))
        if sum(map(lambda x: x in rootlist, ('branches', 'tags', 'trunk'))):
            layout = 'standard'
        else:
            layout = 'single'
        repo.ui.setconfig('hgsubversion', 'layout', layout)
        repo.ui.note('using %s layout\n' % layout)

    branch = repo.ui.config('hgsubversion', 'branch')
    if branch:
        if layout != 'single':
            msg = ('branch cannot be specified for Subversion clones using '
                   'standard directory layout')
            raise hgutil.Abort(msg)

        meta.branchmap['default'] = branch

    ui = repo.ui
    start = meta.revmap.youngest
    origrevcount = len(meta.revmap)

    if start <= 0:
        # we are initializing a new repository
        start = repo.ui.config('hgsubversion', 'startrev', 0)
        if isinstance(start, str) and start.upper() == 'HEAD':
            start = svn.last_changed_rev
        else:
            start = int(start)

        if start > 0:
            if layout == 'standard':
                raise hgutil.Abort('non-zero start revisions are only '
                                   'supported for single-directory clones.')
            ui.note('starting at revision %d; any prior will be ignored\n' %
                    start)
            # fetch all revisions *including* the one specified...
            start -= 1

        # anything less than zero makes no sense
        if start < 0:
            start = 0

    oldrevisions = len(meta.revmap)
    if stopat_rev:
        total = stopat_rev - start
    else:
        total = svn.HEAD - start
    try:
        try:
            # start converting revisions
            firstrun = True
            for r in svn.revisions(start=start, stop=stopat_rev):
                if (r.author is None and r.message
                        == 'This is an empty revision for padding.'):
                    continue
                tbdelta = meta.update_branch_tag_map_for_rev(r)
                # got a 502? Try more than once!
                tries = 0
                converted = False
                while not converted:
                    try:
                        msg = ''
                        if r.message:
                            msg = r.message.strip()
                        if not msg:
                            msg = util.default_commit_msg(ui)
                        else:
                            msg = [s.strip() for s in msg.splitlines() if s][0]
                        if getattr(ui, 'termwidth', False):
                            w = ui.termwidth()
                        else:
                            w = hgutil.termwidth()
                        bits = (r.revnum, r.author, msg)
                        ui.status(('[r%d] %s: %s' % bits)[:w] + '\n')
                        util.progress(ui,
                                      'pull',
                                      r.revnum - start,
                                      total=total)

                        meta.save_tbdelta(tbdelta)
                        close = pullfuns[have_replay](ui, meta, svn, r,
                                                      tbdelta, firstrun)
                        meta.committags(r, close)
                        for branch, parent in close.iteritems():
                            if parent in (None, node.nullid):
                                continue
                            meta.delbranch(branch, parent, r)

                        meta.save()
                        converted = True
                        firstrun = False

                    except svnwrap.SubversionRepoCanNotReplay, e:  #pragma: no cover
                        ui.status('%s\n' % e.message)
                        stupidmod.print_your_svn_is_old_message(ui)
                        have_replay = False
                    except svnwrap.SubversionException, e:  #pragma: no cover
                        if (e.args[1] == svnwrap.ERR_RA_DAV_REQUEST_FAILED
                                and '502' in str(e) and tries < 3):
                            tries += 1
                            ui.status('Got a 502, retrying (%s)\n' % tries)
                        else:
                            ui.traceback()
                            raise hgutil.Abort(*e.args)
示例#8
0
def convert_rev(ui, meta, svn, r, tbdelta, firstrun):

    editor = meta.editor
    editor.current.clear()
    editor.current.rev = r

    if firstrun and meta.revmap.oldest <= 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.revmap.oldest)

    current = editor.current
    current.findmissing(svn)

    updateexternals(ui, meta, current)

    if current.exception is not None:  #pragma: no cover
        traceback.print_exception(*current.exception)
        raise ReplayException()
    if current.missing:
        raise MissingPlainTextError()

    # paranoidly generate the list of files to commit
    files_to_commit = set(current.files.keys())
    files_to_commit.update(current.symlinks.keys())
    files_to_commit.update(current.execfiles.keys())
    files_to_commit.update(current.deleted.keys())
    # back to a list and sort so we get sane behavior
    files_to_commit = list(files_to_commit)
    files_to_commit.sort()
    branch_batches = {}
    rev = current.rev
    date = meta.fixdate(rev.date)

    # build up the branches that have files on them
    for f in files_to_commit:
        if not meta.is_path_valid(f):
            continue
        p, b = meta.split_branch_path(f)[:2]
        if b not in branch_batches:
            branch_batches[b] = []
        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]

        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 meta.repo.branchtags() 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]
            if current_file in current.deleted:
                raise IOError(errno.ENOENT, '%s is deleted' % path)
            copied = current.copies.get(current_file)
            flags = parentctx.flags(path)
            is_exec = current.execfiles.get(current_file, 'x' in flags)
            is_link = current.symlinks.get(current_file, 'l' in flags)
            if current_file in current.files:
                data = current.files[current_file]
                if is_link and data.startswith('link '):
                    data = data[len('link '):]
                elif is_link:
                    ui.debug('file marked as link, but may contain data: '
                             '%s (%r)\n' % (current_file, flags))
            else:
                data = parentctx.filectx(path).data()
            return context.memfilectx(path=path,
                                      data=data,
                                      islink=is_link,
                                      isexec=is_exec,
                                      copied=copied)

        meta.mapbranch(extra)
        current_ctx = context.memctx(
            meta.repo, parents, rev.message or util.default_commit_msg(ui),
            files.keys(), filectxfn, meta.authors[rev.author], date, extra)

        new_hash = meta.repo.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:

        ha = meta.get_parent_revision(rev.revnum, branch)
        if ha == node.nullid:
            continue

        parent_ctx = meta.repo.changectx(ha)

        def del_all_files(*args):
            raise IOError(errno.ENOENT, 'deleting all files')

        # True here meant nuke all files, shouldn't happen with branch closing
        if current.emptybranches[branch]:  #pragma: no cover
            raise hgutil.Abort('Empty commit to an open branch attempted. '
                               'Please report this issue.')

        extra = meta.genextra(rev.revnum, branch)
        meta.mapbranch(extra)

        current_ctx = context.memctx(meta.repo, (ha, node.nullid), rev.message
                                     or ' ', [], del_all_files,
                                     meta.authors[rev.author], date, extra)
        new_hash = meta.repo.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
示例#9
0
def convert_rev(ui, meta, svn, r, tbdelta, firstrun):

    editor = meta.editor
    editor.current.clear()
    editor.current.rev = r

    if firstrun and meta.revmap.oldest <= 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.revmap.oldest)

    current = editor.current
    current.findmissing(svn)

    updateexternals(ui, meta, current)

    if current.exception is not None:  #pragma: no cover
        traceback.print_exception(*current.exception)
        raise ReplayException()
    if current.missing:
        raise MissingPlainTextError()

    # paranoidly generate the list of files to commit
    files_to_commit = set(current.files.keys())
    files_to_commit.update(current.symlinks.keys())
    files_to_commit.update(current.execfiles.keys())
    files_to_commit.update(current.deleted.keys())
    # back to a list and sort so we get sane behavior
    files_to_commit = list(files_to_commit)
    files_to_commit.sort()
    branch_batches = {}
    rev = current.rev
    date = meta.fixdate(rev.date)

    # build up the branches that have files on them
    for f in files_to_commit:
        if not meta.is_path_valid(f):
            continue
        p, b = meta.split_branch_path(f)[:2]
        if b not in branch_batches:
            branch_batches[b] = []
        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]

        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 meta.repo.branchtags()
                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]
            if current_file in current.deleted:
                raise IOError(errno.ENOENT, '%s is deleted' % path)
            copied = current.copies.get(current_file)
            flags = parentctx.flags(path)
            is_exec = current.execfiles.get(current_file, 'x' in flags)
            is_link = current.symlinks.get(current_file, 'l' in flags)
            if current_file in current.files:
                data = current.files[current_file]
                if is_link and data.startswith('link '):
                    data = data[len('link '):]
                elif is_link:
                    ui.debug('file marked as link, but may contain data: '
                             '%s (%r)\n' % (current_file, flags))
            else:
                data = parentctx.filectx(path).data()
            return context.memfilectx(path=path,
                                      data=data,
                                      islink=is_link, isexec=is_exec,
                                      copied=copied)

        meta.mapbranch(extra)
        current_ctx = context.memctx(meta.repo,
                                     parents,
                                     rev.message or util.default_commit_msg(ui),
                                     files.keys(),
                                     filectxfn,
                                     meta.authors[rev.author],
                                     date,
                                     extra)

        new_hash = meta.repo.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:

        ha = meta.get_parent_revision(rev.revnum, branch)
        if ha == node.nullid:
            continue

        parent_ctx = meta.repo.changectx(ha)
        def del_all_files(*args):
            raise IOError(errno.ENOENT, 'deleting all files')

        # True here meant nuke all files, shouldn't happen with branch closing
        if current.emptybranches[branch]: #pragma: no cover
            raise hgutil.Abort('Empty commit to an open branch attempted. '
                               'Please report this issue.')

        extra = meta.genextra(rev.revnum, branch)
        meta.mapbranch(extra)

        current_ctx = context.memctx(meta.repo,
                                     (ha, node.nullid),
                                     rev.message or ' ',
                                     [],
                                     del_all_files,
                                     meta.authors[rev.author],
                                     date,
                                     extra)
        new_hash = meta.repo.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