예제 #1
0
def clonenarrowcmd(orig, ui, repo, *args, **opts):
    """Wraps clone command, so 'hg clone' first wraps localrepo.clone()."""
    opts = pycompat.byteskwargs(opts)
    wrappedextraprepare = util.nullcontextmanager()
    opts_narrow = opts['narrow']
    if opts_narrow:

        def pullbundle2extraprepare_widen(orig, pullop, kwargs):
            # Create narrow spec patterns from clone flags
            includepats = narrowspec.parsepatterns(opts['include'])
            excludepats = narrowspec.parsepatterns(opts['exclude'])

            # If necessary, ask the server to expand the narrowspec.
            includepats, excludepats = expandpull(pullop, includepats,
                                                  excludepats)

            if not includepats and excludepats:
                # If nothing was included, we assume the user meant to include
                # everything, except what they asked to exclude.
                includepats = {'path:.'}

            pullop.repo.setnarrowpats(includepats, excludepats)

            # This will populate 'includepats' etc with the values from the
            # narrowspec we just saved.
            orig(pullop, kwargs)

            if opts.get('depth'):
                kwargs['depth'] = opts['depth']

        wrappedextraprepare = extensions.wrappedfunction(
            exchange, '_pullbundle2extraprepare',
            pullbundle2extraprepare_widen)

    def pullnarrow(orig, repo, *args, **kwargs):
        if opts_narrow:
            repo.requirements.add(changegroup.NARROW_REQUIREMENT)
            repo._writerequirements()

        return orig(repo, *args, **kwargs)

    wrappedpull = extensions.wrappedfunction(exchange, 'pull', pullnarrow)

    with wrappedextraprepare, wrappedpull:
        return orig(ui, repo, *args, **pycompat.strkwargs(opts))
예제 #2
0
def forcedraftcommits():
    """Context manager that forces new commits to at least draft,
    regardless of configuration.

    """
    with extensions.wrappedfunction(
            phases,
            'newcommitphase',
            lambda orig, ui: phases.draft,
    ):
        yield
예제 #3
0
def pullnarrowcmd(orig, ui, repo, *args, **opts):
    """Wraps pull command to allow modifying narrow spec."""
    wrappedextraprepare = util.nullcontextmanager()
    if repository.NARROW_REQUIREMENT in repo.requirements:

        def pullbundle2extraprepare_widen(orig, pullop, kwargs):
            orig(pullop, kwargs)
            if opts.get(r'depth'):
                kwargs['depth'] = opts[r'depth']
        wrappedextraprepare = extensions.wrappedfunction(exchange,
            '_pullbundle2extraprepare', pullbundle2extraprepare_widen)

    with wrappedextraprepare:
        return orig(ui, repo, *args, **opts)
예제 #4
0
def _widen(ui, repo, remote, commoninc, newincludes, newexcludes):
    newmatch = narrowspec.match(repo.root, newincludes, newexcludes)

    # TODO(martinvonz): Get expansion working with widening/narrowing.
    if narrowspec.needsexpansion(newincludes):
        raise error.Abort('Expansion not yet supported on pull')

    def pullbundle2extraprepare_widen(orig, pullop, kwargs):
        orig(pullop, kwargs)
        # The old{in,ex}cludepats have already been set by orig()
        kwargs['includepats'] = newincludes
        kwargs['excludepats'] = newexcludes

    wrappedextraprepare = extensions.wrappedfunction(
        exchange, '_pullbundle2extraprepare', pullbundle2extraprepare_widen)

    # define a function that narrowbundle2 can call after creating the
    # backup bundle, but before applying the bundle from the server
    def setnewnarrowpats():
        repo.setnarrowpats(newincludes, newexcludes)

    repo.setnewnarrowpats = setnewnarrowpats

    ds = repo.dirstate
    p1, p2 = ds.p1(), ds.p2()
    with ds.parentchange():
        ds.setparents(node.nullid, node.nullid)
    common = commoninc[0]
    with wrappedextraprepare:
        exchange.pull(repo, remote, heads=common)
    with ds.parentchange():
        ds.setparents(p1, p2)

    actions = {k: [] for k in 'a am f g cd dc r dm dg m e k p pr'.split()}
    addgaction = actions['g'].append

    mf = repo['.'].manifest().matches(newmatch)
    for f, fn in mf.iteritems():
        if f not in repo.dirstate:
            addgaction(
                (f, (mf.flags(f), False), "add from widened narrow clone"))

    merge.applyupdates(repo,
                       actions,
                       wctx=repo[None],
                       mctx=repo['.'],
                       overwrite=False)
    merge.recordupdates(repo, actions, branchmerge=False)
예제 #5
0
def phase_heads_handler(op, inpart):
    # If the push has changegroup data, we'll generate a changegroup replication
    # message and the corresponding `hg pull` will update phases automatically.
    # So we don't need to do anything special for replication.
    if (not util.safehasattr(op.repo, r'_replicationinfo') or
        op.repo._replicationinfo[r'changegroup']):
        return _ORIG_PHASE_HEADS_HANDLER(op, inpart)

    # Else this looks like a push without a changegroup. (A phase only push.)
    # We monkeypatch the function for handling phase updates to record what
    # changes were made. Then we convert the changes into pushkey messages.

    # We make assumptions later that we only update from the draft phase. Double
    # check that the source repo doesn't have any secret, etc phase roots.
    seen_phases = set(i for i, v
                      in enumerate(op.repo.unfiltered()._phasecache.phaseroots)
                      if v)
    supported_phases = {phases.public, phases.draft}

    if seen_phases - supported_phases:
        raise error.Abort(_(b'only draft and public phases are supported'))

    moves = {}

    def wrapped_advanceboundary(orig, repo, tr, targetphase, nodes):
        if targetphase in moves:
            raise error.ProgrammingError(b'already handled phase %r' %
                                         targetphase)

        if targetphase not in supported_phases:
            raise error.Abort(_(b'only draft and public phases are supported'))

        moves[targetphase] = nodes

        return orig(repo, tr, targetphase, nodes)

    with extensions.wrappedfunction(phases, b'advanceboundary',
                                    wrapped_advanceboundary):
        _ORIG_PHASE_HEADS_HANDLER(op, inpart)

    for phase, nodes in sorted(moves.items()):
        for node in nodes:
            op.repo._replicationinfo['pushkey'].append(
                ('phases', pycompat.sysstr(hex(node)),
                 '%d' % phases.draft, '%d' % phase, 0))
예제 #6
0
def clonenarrowcmd(orig, ui, repo, *args, **opts):
    """Wraps clone command, so 'hg clone' first wraps localrepo.clone()."""
    opts = pycompat.byteskwargs(opts)
    wrappedextraprepare = util.nullcontextmanager()
    narrowspecfile = opts[b'narrowspec']

    if narrowspecfile:
        filepath = os.path.join(encoding.getcwd(), narrowspecfile)
        ui.status(_(b"reading narrowspec from '%s'\n") % filepath)
        try:
            fdata = util.readfile(filepath)
        except IOError as inst:
            raise error.Abort(
                _(b"cannot read narrowspecs from '%s': %s") %
                (filepath, encoding.strtolocal(inst.strerror)))

        includes, excludes, profiles = sparse.parseconfig(ui, fdata, b'narrow')
        if profiles:
            raise error.Abort(
                _(b"cannot specify other files using '%include' in"
                  b" narrowspec"))

        narrowspec.validatepatterns(includes)
        narrowspec.validatepatterns(excludes)

        # narrowspec is passed so we should assume that user wants narrow clone
        opts[b'narrow'] = True
        opts[b'include'].extend(includes)
        opts[b'exclude'].extend(excludes)

    if opts[b'narrow']:

        def pullbundle2extraprepare_widen(orig, pullop, kwargs):
            orig(pullop, kwargs)

            if opts.get(b'depth'):
                kwargs[b'depth'] = opts[b'depth']

        wrappedextraprepare = extensions.wrappedfunction(
            exchange, b'_pullbundle2extraprepare',
            pullbundle2extraprepare_widen)

    with wrappedextraprepare:
        return orig(ui, repo, *args, **pycompat.strkwargs(opts))
예제 #7
0
def _widen(ui, repo, remote, commoninc, oldincludes, oldexcludes,
           newincludes, newexcludes):
    newmatch = narrowspec.match(repo.root, newincludes, newexcludes)

    # for now we assume that if a server has ellipses enabled, we will be
    # exchanging ellipses nodes. In future we should add ellipses as a client
    # side requirement (maybe) to distinguish a client is shallow or not and
    # then send that information to server whether we want ellipses or not.
    # Theoretically a non-ellipses repo should be able to use narrow
    # functionality from an ellipses enabled server
    ellipsesremote = wireprototypes.ELLIPSESCAP in remote.capabilities()

    def pullbundle2extraprepare_widen(orig, pullop, kwargs):
        orig(pullop, kwargs)
        # The old{in,ex}cludepats have already been set by orig()
        kwargs['includepats'] = newincludes
        kwargs['excludepats'] = newexcludes
    wrappedextraprepare = extensions.wrappedfunction(exchange,
        '_pullbundle2extraprepare', pullbundle2extraprepare_widen)

    # define a function that narrowbundle2 can call after creating the
    # backup bundle, but before applying the bundle from the server
    def setnewnarrowpats():
        repo.setnarrowpats(newincludes, newexcludes)
    repo.setnewnarrowpats = setnewnarrowpats
    # silence the devel-warning of applying an empty changegroup
    overrides = {('devel', 'all-warnings'): False}

    with ui.uninterruptable():
        common = commoninc[0]
        if ellipsesremote:
            ds = repo.dirstate
            p1, p2 = ds.p1(), ds.p2()
            with ds.parentchange():
                ds.setparents(node.nullid, node.nullid)
            with wrappedextraprepare,\
                 repo.ui.configoverride(overrides, 'widen'):
                exchange.pull(repo, remote, heads=common)
            with ds.parentchange():
                ds.setparents(p1, p2)
        else:
            with remote.commandexecutor() as e:
                bundle = e.callcommand('narrow_widen', {
                    'oldincludes': oldincludes,
                    'oldexcludes': oldexcludes,
                    'newincludes': newincludes,
                    'newexcludes': newexcludes,
                    'cgversion': '03',
                    'commonheads': common,
                    'known': [],
                    'ellipses': False,
                }).result()

            with repo.transaction('widening') as tr,\
                 repo.ui.configoverride(overrides, 'widen'):
                tgetter = lambda: tr
                bundle2.processbundle(repo, bundle,
                        transactiongetter=tgetter)

        repo.setnewnarrowpats()
        actions = {k: [] for k in 'a am f g cd dc r dm dg m e k p pr'.split()}
        addgaction = actions['g'].append

        mf = repo['.'].manifest().matches(newmatch)
        for f, fn in mf.iteritems():
            if f not in repo.dirstate:
                addgaction((f, (mf.flags(f), False),
                            "add from widened narrow clone"))

        merge.applyupdates(repo, actions, wctx=repo[None],
                           mctx=repo['.'], overwrite=False)
        merge.recordupdates(repo, actions, branchmerge=False)
예제 #8
0
def _widen(
    ui,
    repo,
    remote,
    commoninc,
    oldincludes,
    oldexcludes,
    newincludes,
    newexcludes,
):
    # for now we assume that if a server has ellipses enabled, we will be
    # exchanging ellipses nodes. In future we should add ellipses as a client
    # side requirement (maybe) to distinguish a client is shallow or not and
    # then send that information to server whether we want ellipses or not.
    # Theoretically a non-ellipses repo should be able to use narrow
    # functionality from an ellipses enabled server
    remotecap = remote.capabilities()
    ellipsesremote = any(cap in remotecap
                         for cap in wireprototypes.SUPPORTED_ELLIPSESCAP)

    # check whether we are talking to a server which supports old version of
    # ellipses capabilities
    isoldellipses = (ellipsesremote
                     and wireprototypes.ELLIPSESCAP1 in remotecap
                     and wireprototypes.ELLIPSESCAP not in remotecap)

    def pullbundle2extraprepare_widen(orig, pullop, kwargs):
        orig(pullop, kwargs)
        # The old{in,ex}cludepats have already been set by orig()
        kwargs[b'includepats'] = newincludes
        kwargs[b'excludepats'] = newexcludes

    wrappedextraprepare = extensions.wrappedfunction(
        exchange, b'_pullbundle2extraprepare', pullbundle2extraprepare_widen)

    # define a function that narrowbundle2 can call after creating the
    # backup bundle, but before applying the bundle from the server
    def setnewnarrowpats():
        repo.setnarrowpats(newincludes, newexcludes)

    repo.setnewnarrowpats = setnewnarrowpats
    # silence the devel-warning of applying an empty changegroup
    overrides = {(b'devel', b'all-warnings'): False}

    common = commoninc[0]
    with ui.uninterruptible():
        if ellipsesremote:
            ds = repo.dirstate
            p1, p2 = ds.p1(), ds.p2()
            with ds.parentchange():
                ds.setparents(node.nullid, node.nullid)
        if isoldellipses:
            with wrappedextraprepare:
                exchange.pull(repo, remote, heads=common)
        else:
            known = []
            if ellipsesremote:
                known = [
                    ctx.node() for ctx in repo.set(b'::%ln', common)
                    if ctx.node() != node.nullid
                ]
            with remote.commandexecutor() as e:
                bundle = e.callcommand(
                    b'narrow_widen',
                    {
                        b'oldincludes': oldincludes,
                        b'oldexcludes': oldexcludes,
                        b'newincludes': newincludes,
                        b'newexcludes': newexcludes,
                        b'cgversion': b'03',
                        b'commonheads': common,
                        b'known': known,
                        b'ellipses': ellipsesremote,
                    },
                ).result()

            trmanager = exchange.transactionmanager(repo, b'widen',
                                                    remote.url())
            with trmanager, repo.ui.configoverride(overrides, b'widen'):
                op = bundle2.bundleoperation(repo,
                                             trmanager.transaction,
                                             source=b'widen')
                # TODO: we should catch error.Abort here
                bundle2.processbundle(repo, bundle, op=op)

        if ellipsesremote:
            with ds.parentchange():
                ds.setparents(p1, p2)

        with repo.transaction(b'widening'):
            repo.setnewnarrowpats()
            narrowspec.updateworkingcopy(repo)
            narrowspec.copytoworkingcopy(repo)
예제 #9
0
def getbundlechangegrouppart_narrow(bundler,
                                    repo,
                                    source,
                                    bundlecaps=None,
                                    b2caps=None,
                                    heads=None,
                                    common=None,
                                    **kwargs):
    cgversions = b2caps.get('changegroup')
    if cgversions:  # 3.1 and 3.2 ship with an empty value
        cgversions = [
            v for v in cgversions
            if v in changegroup.supportedoutgoingversions(repo)
        ]
        if not cgversions:
            raise ValueError(_('no common changegroup version'))
        version = max(cgversions)
    else:
        raise ValueError(
            _("server does not advertise changegroup version,"
              " can't negotiate support for ellipsis nodes"))

    include = sorted(filter(bool, kwargs.get(r'includepats', [])))
    exclude = sorted(filter(bool, kwargs.get(r'excludepats', [])))
    newmatch = narrowspec.match(repo.root, include=include, exclude=exclude)
    if not repo.ui.configbool("experimental", "narrowservebrokenellipses"):
        outgoing = exchange._computeoutgoing(repo, heads, common)
        if not outgoing.missing:
            return

        def wrappedgetbundler(orig, *args, **kwargs):
            bundler = orig(*args, **kwargs)
            bundler._narrow_matcher = lambda: newmatch
            return bundler

        with extensions.wrappedfunction(changegroup, 'getbundler',
                                        wrappedgetbundler):
            cg = changegroup.makestream(repo, outgoing, version, source)
        part = bundler.newpart('changegroup', data=cg)
        part.addparam('version', version)
        if 'treemanifest' in repo.requirements:
            part.addparam('treemanifest', '1')

        if include or exclude:
            narrowspecpart = bundler.newpart(_SPECPART)
            if include:
                narrowspecpart.addparam(_SPECPART_INCLUDE,
                                        '\n'.join(include),
                                        mandatory=True)
            if exclude:
                narrowspecpart.addparam(_SPECPART_EXCLUDE,
                                        '\n'.join(exclude),
                                        mandatory=True)

        return

    depth = kwargs.get(r'depth', None)
    if depth is not None:
        depth = int(depth)
        if depth < 1:
            raise error.Abort(_('depth must be positive, got %d') % depth)

    heads = set(heads or repo.heads())
    common = set(common or [nullid])
    oldinclude = sorted(filter(bool, kwargs.get(r'oldincludepats', [])))
    oldexclude = sorted(filter(bool, kwargs.get(r'oldexcludepats', [])))
    known = {bin(n) for n in kwargs.get(r'known', [])}
    if known and (oldinclude != include or oldexclude != exclude):
        # Steps:
        # 1. Send kill for "$known & ::common"
        #
        # 2. Send changegroup for ::common
        #
        # 3. Proceed.
        #
        # In the future, we can send kills for only the specific
        # nodes we know should go away or change shape, and then
        # send a data stream that tells the client something like this:
        #
        # a) apply this changegroup
        # b) apply nodes XXX, YYY, ZZZ that you already have
        # c) goto a
        #
        # until they've built up the full new state.
        # Convert to revnums and intersect with "common". The client should
        # have made it a subset of "common" already, but let's be safe.
        known = set(repo.revs("%ln & ::%ln", known, common))
        # TODO: we could send only roots() of this set, and the
        # list of nodes in common, and the client could work out
        # what to strip, instead of us explicitly sending every
        # single node.
        deadrevs = known

        def genkills():
            for r in deadrevs:
                yield _KILLNODESIGNAL
                yield repo.changelog.node(r)
            yield _DONESIGNAL

        bundler.newpart(_CHANGESPECPART, data=genkills())
        newvisit, newfull, newellipsis = _computeellipsis(
            repo, set(), common, known, newmatch)
        if newvisit:
            cg = _packellipsischangegroup(repo, common, newmatch, newfull,
                                          newellipsis, newvisit, depth, source,
                                          version)
            part = bundler.newpart('changegroup', data=cg)
            part.addparam('version', version)
            if 'treemanifest' in repo.requirements:
                part.addparam('treemanifest', '1')

    visitnodes, relevant_nodes, ellipsisroots = _computeellipsis(repo,
                                                                 common,
                                                                 heads,
                                                                 set(),
                                                                 newmatch,
                                                                 depth=depth)

    repo.ui.debug('Found %d relevant revs\n' % len(relevant_nodes))
    if visitnodes:
        cg = _packellipsischangegroup(repo, common, newmatch, relevant_nodes,
                                      ellipsisroots, visitnodes, depth, source,
                                      version)
        part = bundler.newpart('changegroup', data=cg)
        part.addparam('version', version)
        if 'treemanifest' in repo.requirements:
            part.addparam('treemanifest', '1')
예제 #10
0
def batchunwrap(wrappers):
    for w in wrappers:
        result = None
        try:
            result = extensions.unwrapfunction(dummy, 'getstack', w)
            msg = str(dummy.getstack())
        except (ValueError, IndexError) as e:
            msg = e.__class__.__name__
        print('unwrap %s: %s: %s' % (getid(w), getid(result), msg))


batchwrap(wrappers + [wrappers[0]])
batchunwrap([(wrappers[i] if i is not None and i >= 0 else None)
             for i in [3, None, 0, 4, 0, 2, 1, None]])

wrap0 = extensions.wrappedfunction(dummy, 'getstack', wrappers[0])
wrap1 = extensions.wrappedfunction(dummy, 'getstack', wrappers[1])

# Use them in a different order from how they were created to check that
# the wrapping happens in __enter__, not in __init__
print('context manager', dummy.getstack())
with wrap1:
    print('context manager', dummy.getstack())
    with wrap0:
        print('context manager', dummy.getstack())
        # Bad programmer forgets to unwrap the function, but the context
        # managers still unwrap their wrappings.
        extensions.wrapfunction(dummy, 'getstack', wrappers[2])
        print('context manager', dummy.getstack())
    print('context manager', dummy.getstack())
print('context manager', dummy.getstack())