def revset_automationrelevant(repo, subset, x): """``automationrelevant(set)`` Changesets relevant to scheduling in automation. Given a revset that evaluates to a single revision, will return that revision and any ancestors that are part of the same push unioned with non-public ancestors. """ s = revset.getset(repo, revset.fullreposet(repo), x) if len(s) > 1: raise error.Abort(b'can only evaluate single changeset') ctx = repo[s.first()] revs = {ctx.rev()} # The pushlog is used to get revisions part of the same push as # the requested revision. pushlog = getattr(repo, 'pushlog', None) if pushlog: push = repo.pushlog.pushfromchangeset(ctx) for n in push.nodes: pctx = repo[n] if pctx.rev() <= ctx.rev(): revs.add(pctx.rev()) # Union with non-public ancestors if configured. By default, we only # consider changesets from the push. However, on special repositories # (namely Try), we want changesets from previous pushes to come into # play too. if repo.ui.configbool(b'hgmo', b'automationrelevantdraftancestors', False): for rev in repo.revs(b'::%d & not public()', ctx.rev()): revs.add(rev) return subset & revset.baseset(revs)
def revset_automationrelevant(repo, subset, x): """``automationrelevant(set)`` Changesets relevant to scheduling in automation. Given a revset that evaluates to a single revision, will return that revision and any ancestors that are part of the same push unioned with non-public ancestors. """ s = revset.getset(repo, revset.fullreposet(repo), x) if len(s) > 1: raise util.Abort('can only evaluate single changeset') ctx = repo[s.first()] revs = set([ctx.rev()]) # The pushlog is used to get revisions part of the same push as # the requested revision. pushlog = getattr(repo, 'pushlog', None) if pushlog: pushinfo = repo.pushlog.pushfromchangeset(ctx) for n in pushinfo[3]: pctx = repo[n] if pctx.rev() <= ctx.rev(): revs.add(pctx.rev()) # Union with non-public ancestors. for rev in repo.revs('::%d & not public()', ctx.rev()): revs.add(rev) return subset & revset.baseset(revs)
def revsettransplanted(repo, subset, x): """Transplanted changesets in set, or all transplanted changesets.""" if x: s = revset.getset(repo, subset, x) else: s = subset return smartset.baseset( [r for r in s if repo[r].extra().get(b'transplant_source')])
def revsettransplanted(repo, subset, x): """``transplanted([set])`` Transplanted changesets in set, or all transplanted changesets. """ if x: s = revset.getset(repo, subset, x) else: s = subset return [r for r in s if repo[r].extra().get('transplant_source')]
def _destrestack(repo, subset, x): """restack destination for given single source revision""" unfi = repo.unfiltered() obsoleted = unfi.revs('obsolete()') getparents = unfi.changelog.parentrevs getphase = unfi._phasecache.phase nodemap = unfi.changelog.nodemap src = revset.getset(repo, subset, x).first() # Empty src or already obsoleted - Do not return a destination if not src or src in obsoleted: return smartset.baseset() # Find the obsoleted "base" by checking source's parent recursively base = src while base not in obsoleted: base = getparents(base)[0] # When encountering a public revision which cannot be obsoleted, stop # the search early and return no destination. Do the same for nullrev. if getphase(repo, base) == phases.public or base == nullrev: return smartset.baseset() # Find successors for given base # NOTE: Ideally we can use obsutil.successorssets to detect divergence # case. However it does not support cycles (unamend) well. So we use # allsuccessors and pick non-obsoleted successors manually as a workaround. basenode = repo[base].node() succnodes = [n for n in obsutil.allsuccessors(repo.obsstore, [basenode]) if (n != basenode and n in nodemap and nodemap[n] not in obsoleted)] # In case of a split, only keep its heads succrevs = list(unfi.revs('heads(%ln)', succnodes)) if len(succrevs) == 0: # Prune - Find the first non-obsoleted ancestor while base in obsoleted: base = getparents(base)[0] if base == nullrev: # Root node is pruned. The new base (destination) is the # virtual nullrev. return smartset.baseset([nullrev]) return smartset.baseset([base]) elif len(succrevs) == 1: # Unique visible successor case - A valid destination return smartset.baseset([succrevs[0]]) else: # Multiple visible successors - Choose the one with a greater revision # number. This is to be compatible with restack old behavior. We might # want to revisit it when we introduce the divergence concept to users. return smartset.baseset([max(succrevs)])
def revsettransplanted(repo, subset, x): """``transplanted(set)`` Transplanted changesets in set. """ if x: s = revset.getset(repo, subset, x) else: s = subset cs = set() for r in xrange(0, len(repo)): if repo[r].extra().get('transplant_source'): cs.add(r) return [r for r in s if r in cs]
def revset_pushrev(repo, subset, x): """Changesets that were part of the same push as the specified changeset(s).""" l = revset.getset(repo, subset, x) # This isn't the most optimal implementation, especially if the input # set is large. But it gets the job done. revs = set() for rev in l: push = repo.pushlog.pushfromchangeset(repo[rev]) if push: for node in push.nodes: revs.add(repo[node].rev()) return subset.filter(revs.__contains__)
def branch(repo, subset, x): # type: (gitrepository, abstractsmartset, Tuple) -> abstractsmartset """ All changesets belonging to the given branch or the branches of the given changesets. Pattern matching is supported for `string`. See :hg:`help revisions.patterns`. """ def getbranchrevs(r): return set(branches_with(repo._repo, r)) # FIXME: look into sorting by branch name, to keep results stable branchrevs = set() revspec = False try: b = revset.getstring(x, '') except error.ParseError: # not a string, but another revspec, e.g. tip() revspec = True else: kind, pattern, matcher = stringutil.stringmatcher(b) branchmap = repo.branchmap() if kind == 'literal': # note: falls through to the revspec case if no branch with # this name exists and pattern kind is not specified explicitly if pattern in branchmap: branchrevs.add(branchmap[b][0]) elif b.startswith('literal:'): raise error.RepoLookupError( _("branch '%s' does not exist") % pattern) else: revspec = True else: branchrevs.update(r[0] for b, r in branchmap.items() if matcher(b)) if revspec: # get all the branches in x s = revset.getset(repo, gitfullreposet(repo), x) for r in s: branchrevs.update(getbranchrevs(r)) if not branchrevs: # FIXME: return empty set or subset? raise NotImplementedError brs = list(branchrevs) s = gitfullreposet(repo, heads=brs) return subset & s
def _calculateset(repo, subset, x, f): """f is a function that converts input nodes to output nodes repo, subset, x are typical revsetpredicate parameters. This function takes care of converting between revs/nodes, and filtering. """ revs = revset.getset(repo, revset.fullreposet(repo), x) cl = repo.unfiltered().changelog torev = cl.rev tonode = cl.node nodemap = cl.nodemap resultrevs = set(torev(n) for n in f(tonode(r) for r in revs) if n in nodemap) s = smartset.baseset(resultrevs - set(revs) - repo.changelog.filteredrevs) s.sort() return subset & s