# HG changeset patch # User Pierre-Yves David # Date 2017-06-26 23:11:56 # Node ID d09ae850296d503137043ebffb8c96ec5f9ab878 # Parent 4f49810a10118a0ace063277a859c441f092ce79 obsutil: move 'exclusivemarkers' to the new modules We have a new 'obsutil' module now. We move the high level utility there to bring 'obsolete.py' back to a more reasonable size. diff --git a/mercurial/obsolete.py b/mercurial/obsolete.py --- a/mercurial/obsolete.py +++ b/mercurial/obsolete.py @@ -771,131 +771,6 @@ def makestore(ui, repo): % len(list(store))) return store -def _filterprunes(markers): - """return a set with no prune markers""" - return set(m for m in markers if m[1]) - -def exclusivemarkers(repo, nodes): - """set of markers relevant to "nodes" but no other locally-known nodes - - This function compute the set of markers "exclusive" to a locally-known - node. This means we walk the markers starting from until we reach a - locally-known precursors outside of . Element of with - locally-known successors outside of are ignored (since their - precursors markers are also relevant to these successors). - - For example: - - # (A0 rewritten as A1) - # - # A0 <-1- A1 # Marker "1" is exclusive to A1 - - or - - # (A0 rewritten as AX; AX rewritten as A1; AX is unkown locally) - # - # <-1- A0 <-2- AX <-3- A1 # Marker "2,3" are exclusive to A1 - - or - - # (A0 has unknown precursors, A0 rewritten as A1 and A2 (divergence)) - # - # <-2- A1 # Marker "2" is exclusive to A0,A1 - # / - # <-1- A0 - # \ - # <-3- A2 # Marker "3" is exclusive to A0,A2 - # - # in addition: - # - # Markers "2,3" are exclusive to A1,A2 - # Markers "1,2,3" are exclusive to A0,A1,A2 - - See test/test-obsolete-bundle-strip.t for more examples. - - An example usage is strip. When stripping a changeset, we also want to - strip the markers exclusive to this changeset. Otherwise we would have - "dangling"" obsolescence markers from its precursors: Obsolescence markers - marking a node as obsolete without any successors available locally. - - As for relevant markers, the prune markers for children will be followed. - Of course, they will only be followed if the pruned children is - locally-known. Since the prune markers are relevant to the pruned node. - However, while prune markers are considered relevant to the parent of the - pruned changesets, prune markers for locally-known changeset (with no - successors) are considered exclusive to the pruned nodes. This allows - to strip the prune markers (with the rest of the exclusive chain) alongside - the pruned changesets. - """ - # running on a filtered repository would be dangerous as markers could be - # reported as exclusive when they are relevant for other filtered nodes. - unfi = repo.unfiltered() - - # shortcut to various useful item - nm = unfi.changelog.nodemap - precursorsmarkers = unfi.obsstore.precursors - successormarkers = unfi.obsstore.successors - childrenmarkers = unfi.obsstore.children - - # exclusive markers (return of the function) - exclmarkers = set() - # we need fast membership testing - nodes = set(nodes) - # looking for head in the obshistory - # - # XXX we are ignoring all issues in regard with cycle for now. - stack = [n for n in nodes if not _filterprunes(successormarkers.get(n, ()))] - stack.sort() - # nodes already stacked - seennodes = set(stack) - while stack: - current = stack.pop() - # fetch precursors markers - markers = list(precursorsmarkers.get(current, ())) - # extend the list with prune markers - for mark in successormarkers.get(current, ()): - if not mark[1]: - markers.append(mark) - # and markers from children (looking for prune) - for mark in childrenmarkers.get(current, ()): - if not mark[1]: - markers.append(mark) - # traverse the markers - for mark in markers: - if mark in exclmarkers: - # markers already selected - continue - - # If the markers is about the current node, select it - # - # (this delay the addition of markers from children) - if mark[1] or mark[0] == current: - exclmarkers.add(mark) - - # should we keep traversing through the precursors? - prec = mark[0] - - # nodes in the stack or already processed - if prec in seennodes: - continue - - # is this a locally known node ? - known = prec in nm - # if locally-known and not in the set the traversal - # stop here. - if known and prec not in nodes: - continue - - # do not keep going if there are unselected markers pointing to this - # nodes. If we end up traversing these unselected markers later the - # node will be taken care of at that point. - precmarkers = _filterprunes(successormarkers.get(prec)) - if precmarkers.issubset(exclmarkers): - seennodes.add(prec) - stack.append(prec) - - return exclmarkers - def commonversion(versions): """Return the newest version listed in both versions and our local formats. @@ -971,7 +846,7 @@ def getmarkers(repo, nodes=None, exclusi if nodes is None: rawmarkers = repo.obsstore elif exclusive: - rawmarkers = exclusivemarkers(repo, nodes) + rawmarkers = obsutil.exclusivemarkers(repo, nodes) else: rawmarkers = repo.obsstore.relevantmarkers(nodes) @@ -1063,6 +938,11 @@ def foreground(repo, nodes): foreground = set(repo.set('%ln::', known)) return set(c.node() for c in foreground) +def exclusivemarkers(repo, nodes): + movemsg = 'obsolete.exclusivemarkers moved to obsutil.exclusivemarkers' + repo.ui.deprecwarn(movemsg, '4.3') + return obsutil.exclusivemarkers(repo, nodes) + def successorssets(repo, initialnode, cache=None): movemsg = 'obsolete.successorssets moved to obsutil.successorssets' repo.ui.deprecwarn(movemsg, '4.3') diff --git a/mercurial/obsutil.py b/mercurial/obsutil.py --- a/mercurial/obsutil.py +++ b/mercurial/obsutil.py @@ -35,6 +35,131 @@ def closestpredecessors(repo, nodeid): else: stack.append(precnodeid) +def _filterprunes(markers): + """return a set with no prune markers""" + return set(m for m in markers if m[1]) + +def exclusivemarkers(repo, nodes): + """set of markers relevant to "nodes" but no other locally-known nodes + + This function compute the set of markers "exclusive" to a locally-known + node. This means we walk the markers starting from until we reach a + locally-known precursors outside of . Element of with + locally-known successors outside of are ignored (since their + precursors markers are also relevant to these successors). + + For example: + + # (A0 rewritten as A1) + # + # A0 <-1- A1 # Marker "1" is exclusive to A1 + + or + + # (A0 rewritten as AX; AX rewritten as A1; AX is unkown locally) + # + # <-1- A0 <-2- AX <-3- A1 # Marker "2,3" are exclusive to A1 + + or + + # (A0 has unknown precursors, A0 rewritten as A1 and A2 (divergence)) + # + # <-2- A1 # Marker "2" is exclusive to A0,A1 + # / + # <-1- A0 + # \ + # <-3- A2 # Marker "3" is exclusive to A0,A2 + # + # in addition: + # + # Markers "2,3" are exclusive to A1,A2 + # Markers "1,2,3" are exclusive to A0,A1,A2 + + See test/test-obsolete-bundle-strip.t for more examples. + + An example usage is strip. When stripping a changeset, we also want to + strip the markers exclusive to this changeset. Otherwise we would have + "dangling"" obsolescence markers from its precursors: Obsolescence markers + marking a node as obsolete without any successors available locally. + + As for relevant markers, the prune markers for children will be followed. + Of course, they will only be followed if the pruned children is + locally-known. Since the prune markers are relevant to the pruned node. + However, while prune markers are considered relevant to the parent of the + pruned changesets, prune markers for locally-known changeset (with no + successors) are considered exclusive to the pruned nodes. This allows + to strip the prune markers (with the rest of the exclusive chain) alongside + the pruned changesets. + """ + # running on a filtered repository would be dangerous as markers could be + # reported as exclusive when they are relevant for other filtered nodes. + unfi = repo.unfiltered() + + # shortcut to various useful item + nm = unfi.changelog.nodemap + precursorsmarkers = unfi.obsstore.precursors + successormarkers = unfi.obsstore.successors + childrenmarkers = unfi.obsstore.children + + # exclusive markers (return of the function) + exclmarkers = set() + # we need fast membership testing + nodes = set(nodes) + # looking for head in the obshistory + # + # XXX we are ignoring all issues in regard with cycle for now. + stack = [n for n in nodes if not _filterprunes(successormarkers.get(n, ()))] + stack.sort() + # nodes already stacked + seennodes = set(stack) + while stack: + current = stack.pop() + # fetch precursors markers + markers = list(precursorsmarkers.get(current, ())) + # extend the list with prune markers + for mark in successormarkers.get(current, ()): + if not mark[1]: + markers.append(mark) + # and markers from children (looking for prune) + for mark in childrenmarkers.get(current, ()): + if not mark[1]: + markers.append(mark) + # traverse the markers + for mark in markers: + if mark in exclmarkers: + # markers already selected + continue + + # If the markers is about the current node, select it + # + # (this delay the addition of markers from children) + if mark[1] or mark[0] == current: + exclmarkers.add(mark) + + # should we keep traversing through the precursors? + prec = mark[0] + + # nodes in the stack or already processed + if prec in seennodes: + continue + + # is this a locally known node ? + known = prec in nm + # if locally-known and not in the set the traversal + # stop here. + if known and prec not in nodes: + continue + + # do not keep going if there are unselected markers pointing to this + # nodes. If we end up traversing these unselected markers later the + # node will be taken care of at that point. + precmarkers = _filterprunes(successormarkers.get(prec)) + if precmarkers.issubset(exclmarkers): + seennodes.add(prec) + stack.append(prec) + + return exclmarkers + def successorssets(repo, initialnode, cache=None): """Return set of all latest successors of initial nodes diff --git a/mercurial/repair.py b/mercurial/repair.py --- a/mercurial/repair.py +++ b/mercurial/repair.py @@ -20,6 +20,7 @@ from . import ( error, exchange, obsolete, + obsutil, util, ) @@ -132,7 +133,7 @@ def strip(ui, repo, nodelist, backup=Tru stripobsidx = obsmarkers = () if repo.ui.configbool('devel', 'strip-obsmarkers', True): - obsmarkers = obsolete.exclusivemarkers(repo, stripbases) + obsmarkers = obsutil.exclusivemarkers(repo, stripbases) if obsmarkers: stripobsidx = [i for i, m in enumerate(repo.obsstore) if m in obsmarkers]