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