head-checking: filter out obsolete heads when checking for new heads stable
authorPierre-Yves David <pierre-yves.david@octobus.net>
Mon, 06 Apr 2020 04:01:58 +0200
branchstable
changeset 5267 ba53591d4aab
parent 5266 af9f40236037
child 5268 11c359b4071d
head-checking: filter out obsolete heads when checking for new heads This does not affect any tests right now, but this will be useful for the next changeset. This is also the start of a more aggressive removal of obsolete heads in places where it does not make sense to take them into account.
hgext3rd/topic/discovery.py
--- a/hgext3rd/topic/discovery.py	Mon Apr 06 05:05:07 2020 +0200
+++ b/hgext3rd/topic/discovery.py	Mon Apr 06 04:01:58 2020 +0200
@@ -139,6 +139,46 @@
         repo.__class__ = oldrepo
 
 
+def _get_branch_name(ctx):
+    # make it easy for extension with the branch logic there
+    return ctx.branch()
+
+
+def _filter_obsolete_heads(repo, heads):
+    """filter heads to return non-obsolete ones
+
+    Given a list of heads (on the same named branch) return a new list of heads
+    where the obsolete part have been skimmed out.
+    """
+    new_heads = []
+    old_heads = heads[:]
+    while old_heads:
+        rh = old_heads.pop()
+        ctx = repo[rh]
+        current_name = _get_branch_name(ctx)
+        # run this check early to skip the evaluation of the whole branch
+        if not ctx.obsolete():
+            new_heads.append(rh)
+            continue
+
+        # Get all revs/nodes on the branch exclusive to this head
+        # (already filtered heads are "ignored"))
+        sections_revs = repo.revs(
+            b'only(%d, (%ld+%ld))', rh, old_heads, new_heads,
+        )
+        keep_revs = []
+        for r in sections_revs:
+            ctx = repo[r]
+            if ctx.obsolete():
+                continue
+            if _get_branch_name(ctx) != current_name:
+                continue
+            keep_revs.append(r)
+        for h in repo.revs(b'heads(%ld and (::%ld))', sections_revs, keep_revs):
+            new_heads.append(h)
+    new_heads.sort()
+    return new_heads
+
 # Discovery have deficiency around phases, branch can get new heads with pure
 # phases change. This happened with a changeset was allowed to be pushed
 # because it had a topic, but it later become public and create a new branch
@@ -150,7 +190,9 @@
     for b in repo.branchmap().iterbranches():
         if b':' in b[0]:
             continue
-        data[b[0]] = len(b[1])
+        oldheads = [repo[n].rev() for n in b[1]]
+        newheads = _filter_obsolete_heads(repo, oldheads)
+        data[b[0]] = len(newheads)
     return data
 
 def handlecheckheads(orig, op, inpart):