Пример #1
0
    def _set_subdir(self, subdir):
        subdir = util.forceutf8(subdir)
        if subdir:
            subdir = '/'.join(p for p in subdir.split('/') if p)

        self.__subdir = None
        subdirfile = os.path.join(self.metapath, 'subdir')

        if os.path.isfile(subdirfile):
            stored_subdir = util.load(subdirfile)
            assert stored_subdir is not None
            if subdir is None:
                self.__subdir = stored_subdir
            elif subdir and subdir != stored_subdir:
                raise hgerror.Abort(
                    'unable to work on a different path in the '
                    'repository')
            else:
                self.__subdir = subdir
        elif subdir is not None:
            util.dump(subdir, subdirfile)
            self.__subdir = subdir
        elif not self._skiperror:
            raise hgerror.Abort("hgsubversion metadata unavailable; "
                                "please run 'hg svn rebuildmeta'")
Пример #2
0
 def delbranch(self, branch, node, rev):
     extra = self.genextra(rev.revnum, branch)
     self.mapbranch(extra, True)
     ctx = context.memctx(self.repo, (node, revlog.nullid),
                          self.getmessage(rev), [], lambda x, y, z: None,
                          util.forceutf8(self.authors[rev.author]),
                          self.fixdate(rev.date), extra)
     self.repo.svn_commitctx(ctx)
     self.ui.status('Marked branch %s as closed.\n' % (branch or 'default'))
Пример #3
0
 def fixdate(self, date):
     if date is not None:
         date = util.forceutf8(date)
         date = date.replace('T', ' ').replace('Z', '').split('.')[0]
         date += ' -0000'
         self.lastdate = date
     else:
         date = self.lastdate
     return date
Пример #4
0
    def genextra(self, revnum, branch):
        extra = {}
        subdir = self.subdir
        if subdir and subdir[-1] == '/':
            subdir = subdir[:-1]
        if subdir and subdir[0] != '/':
            subdir = '/' + subdir

        path = self.layoutobj.remotepath(branch, subdir)

        if branch:
            extra['branch'] = branch

        extra['convert_revision'] = 'svn:%(uuid)s%(path)s@%(rev)s' % {
            'uuid': util.forceutf8(self.uuid),
            'path': util.forceutf8(path),
            'rev': revnum,
        }
        return extra
Пример #5
0
    def getmessage(self, rev):
        msg = util.forceutf8(rev.message)

        if msg:
            try:
                msg.decode('utf-8')
                return msg

            except UnicodeDecodeError:
                # ancient svn failed to enforce utf8 encoding
                return msg.decode('iso-8859-1').encode('utf-8')
        else:
            return self.defaultmessage
Пример #6
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 compathacks.makememfilectx(repo,
                                              memctx=memctx,
                                              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),
                             self.getmessage(rev), [
                                 '.hgtags',
                             ], hgtagsfn,
                             util.forceutf8(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)
Пример #7
0
 def normalize(self, path):
     '''Normalize a path to strip of leading slashes and our subdir if we
     have one.
     '''
     path = util.forceutf8(path)
     if self.subdir and path == self.subdir[:-1]:
         return ''
     if path and path[0] == '/':
         path = path[1:]
     if path == self.subdir:
         return ''
     if path and path.startswith(self.subdir + '/'):
         path = path[len(self.subdir):]
     if path and path[0] == '/':
         path = path[1:]
     return path
Пример #8
0
def convert_rev(ui, meta, svn, r, tbdelta, firstrun):
    if svnwrap.subversion_version >= (1, 9, 0):
        raise hgerror.Abort(
            "hgsubversion doesn't support stupid mode with Subversion 1.9."
            ' Please email [email protected] and let us know you'
            ' saw this, otherwise we may remove stupid mode entirely.')
    # this server fails at replay

    if meta.filemap:
        raise hgerror.Abort('filemaps currently unsupported with stupid replay.')

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

        if meta.skipbranch(b):
            continue

        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.revmap.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,
                                                  memctx=memctx,
                                                  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.branchmap()
                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],
            util.forceutf8(meta.getmessage(r)),
            [util.forceutf8(f) for f in files_touched],
            filectxfn,
            util.forceutf8(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)
Пример #9
0
    def committags(self, rev, endbranches):
        if not self.addedtags and not self.deletedtags:
            return
        date = self.fixdate(rev.date)
        # determine additions/deletions per branch
        branches = {}
        for tags in (self.addedtags, self.deletedtags):
            for tag, (branch, srcrev) in tags.iteritems():
                op = srcrev is None and 'rm' or 'add'
                branches.setdefault(branch, []).append((op, tag, srcrev))

        for b, tags in branches.iteritems():

            # modify parent's .hgtags source

            parent = self.repo[self.get_parent_revision(rev.revnum, b)]
            if '.hgtags' not in parent:
                src = ''
            else:
                src = parent['.hgtags'].data()

            fromtag = self.get_path_tag(self.remotename(b))
            for op, tag, r in sorted(tags, reverse=True):

                if tag in self.tagmap and not self.tagmap[tag]:
                    continue

                tagged = node.hex(node.nullid)  # op != 'add'
                if op == 'add':
                    if fromtag:
                        if fromtag in self.tags:
                            tagged = node.hex(self.tags[fromtag])
                    else:
                        tagged = node.hex(
                            self.revmap[self.get_parent_svn_branch_and_rev(
                                r, b)])

                src += '%s %s\n' % (tagged, self.tagmap.get(tag, tag))
                self.tags[tag] = node.bin(tagged), rev.revnum

            # add new changeset containing updated .hgtags
            def fctxfun(repo, memctx, path):
                return compathacks.makememfilectx(repo,
                                                  memctx=memctx,
                                                  path='.hgtags',
                                                  data=src,
                                                  islink=False,
                                                  isexec=False,
                                                  copied=None)

            extra = self.genextra(rev.revnum, b)
            if fromtag:
                extra['branch'] = parent.extra().get('branch', 'default')
            self.mapbranch(extra, b in endbranches or fromtag)

            ctx = context.memctx(self.repo, (parent.node(), node.nullid),
                                 self.getmessage(rev), ['.hgtags'], fctxfun,
                                 util.forceutf8(self.authors[rev.author]),
                                 date, extra)
            new = self.repo.svn_commitctx(ctx)

            if not fromtag and (rev.revnum, b) not in self.revmap:
                self.revmap[rev.revnum, b] = new
            if b in endbranches:
                endbranches.pop(b)
                bname = b or 'default'
                self.ui.status('Marked branch %s as closed.\n' % bname)
Пример #10
0
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.revmap.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.revmap.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 hgerror.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.revnum)
        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 meta.repo.branchmap() and not files):
                continue

        parentctx = meta.repo[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,
                                              memctx=memctx,
                                              path=path,
                                              data=data,
                                              islink=islink,
                                              isexec=isexec,
                                              copied=copied)

        meta.mapbranch(extra)
        if 'branch' not in extra:
            extra['branch'] = 'default'
        current_ctx = context.memctx(meta.repo, parents,
                                     util.forceutf8(meta.getmessage(rev)),
                                     [util.forceutf8(f)
                                      for f in files.keys()], filectxfn,
                                     util.forceutf8(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

        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),
                                     util.forceutf8(meta.getmessage(rev)),
                                     [util.forceutf8(f)
                                      for f in files], del_all_files,
                                     util.forceutf8(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