[Updated] D12097: branchmap: skip obsolete revisions while computing heads

av6 (Anton Shestakov) phabricator at mercurial-scm.org
Thu Feb 3 11:58:53 UTC 2022


av6 edited the summary of this revision.
av6 updated this revision to Diff 32038.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D12097?vs=31993&id=32038

BRANCH
  default

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D12097/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D12097

AFFECTED FILES
  mercurial/branchmap.py
  mercurial/destutil.py
  mercurial/scmutil.py
  tests/test-branch-change.t
  tests/test-obsmarker-template.t
  tests/test-obsolete.t
  tests/test-rebase-obsolete.t
  tests/test-rebase-obsolete2.t
  tests/test-rebase-obsolete4.t
  tests/test-static-http.t

CHANGE DETAILS

diff --git a/tests/test-static-http.t b/tests/test-static-http.t
--- a/tests/test-static-http.t
+++ b/tests/test-static-http.t
@@ -255,6 +255,7 @@
   /remote-with-names/.hg/store/data/%7E2ehgtags.i (no-py37 !)
   /remote-with-names/.hg/store/data/foo.i
   /remote-with-names/.hg/store/data/~2ehgtags.i (py37 !)
+  /remote-with-names/.hg/store/obsstore
   /remote/.hg/bookmarks
   /remote/.hg/bookmarks.current
   /remote/.hg/cache/branch2-base
@@ -274,6 +275,7 @@
   /remote/.hg/store/data/quux.i
   /remote/.hg/store/data/~2edotfile%20with%20spaces.i (py37 !)
   /remote/.hg/store/data/~2ehgtags.i (py37 !)
+  /remote/.hg/store/obsstore
   /remotempty/.hg/bookmarks
   /remotempty/.hg/bookmarks.current
   /remotempty/.hg/dirstate
diff --git a/tests/test-rebase-obsolete4.t b/tests/test-rebase-obsolete4.t
--- a/tests/test-rebase-obsolete4.t
+++ b/tests/test-rebase-obsolete4.t
@@ -169,7 +169,7 @@
    D
   branch: default
   commit: 1 modified, 1 added, 1 unknown, 1 unresolved
-  update: 1 new changesets, 2 branch heads (merge)
+  update: (current)
   phases: 3 draft
   rebase: 0 rebased, 2 remaining (rebase --continue)
 
diff --git a/tests/test-rebase-obsolete2.t b/tests/test-rebase-obsolete2.t
--- a/tests/test-rebase-obsolete2.t
+++ b/tests/test-rebase-obsolete2.t
@@ -47,6 +47,7 @@
   $ hg add C
   $ hg commit -m C
   1 new orphan changesets
+  created new head
   $ hg log -G
   @  4:212cb178bcbb C
   |
@@ -73,6 +74,7 @@
   $ hg add D
   $ hg commit -m D
   1 new orphan changesets
+  created new head
   $ hg --hidden strip -r 'desc(B1)'
   saved backup bundle to $TESTTMP/obsskip/.hg/strip-backup/86f6414ccda7-b1c452ee-backup.hg
   1 new orphan changesets
@@ -194,6 +196,7 @@
   $ hg add foo
   $ hg commit -m "bar foo"
   1 new orphan changesets
+  created new head
   $ hg log -G
   @  14:73568ab6879d bar foo
   |
diff --git a/tests/test-rebase-obsolete.t b/tests/test-rebase-obsolete.t
--- a/tests/test-rebase-obsolete.t
+++ b/tests/test-rebase-obsolete.t
@@ -650,6 +650,7 @@
   $ hg add J
   $ hg commit -m J
   1 new orphan changesets
+  created new head
   $ hg debugobsolete `hg log --rev . -T '{node}'`
   1 new obsolescence markers
   obsoleted 1 changesets
diff --git a/tests/test-obsolete.t b/tests/test-obsolete.t
--- a/tests/test-obsolete.t
+++ b/tests/test-obsolete.t
@@ -169,10 +169,6 @@
   5:5601fb93a350 (draft) [tip ] add new_3_c
   $ hg heads --hidden
   5:5601fb93a350 (draft) [tip ] add new_3_c
-  4:ca819180edb9 (draft *obsolete*) [ ] add new_2_c [rewritten as 5:5601fb93a350]
-  3:cdbce2fbb163 (draft *obsolete*) [ ] add new_c [rewritten as 4:ca819180edb9]
-  2:245bde4270cd (draft *obsolete*) [ ] add original_c [rewritten as 3:cdbce2fbb163]
-
 
 check that summary does not report them
 
@@ -193,7 +189,7 @@
    add new_3_c
   branch: default
   commit: (clean)
-  update: 3 new changesets, 4 branch heads (merge)
+  update: (current)
   phases: 6 draft
   remote: 3 outgoing
 
diff --git a/tests/test-obsmarker-template.t b/tests/test-obsmarker-template.t
--- a/tests/test-obsmarker-template.t
+++ b/tests/test-obsmarker-template.t
@@ -1762,7 +1762,7 @@
   2 new obsolescence markers
   obsoleted 1 changesets
   new changesets 7a230b46bf61 (1 drafts)
-  (run 'hg heads' to see heads, 'hg merge' to merge)
+  (run 'hg heads' to see heads)
   $ hg log --hidden -G
   o  changeset:   2:7a230b46bf61
   |  tag:         tip
diff --git a/tests/test-branch-change.t b/tests/test-branch-change.t
--- a/tests/test-branch-change.t
+++ b/tests/test-branch-change.t
@@ -185,6 +185,7 @@
   changed branch on 2 changesets
   updating the branch cache
   invalid branch cache (served): tip differs
+  invalid branch cache (served.hidden): tip differs
 
   $ hg glog -r '(.^)::'
   @  9:de1404b45a69 Added e
@@ -211,7 +212,7 @@
   secret                        11:38a9b2d53f98
   foo                            7:8a4729a5e2b8
   wat                            9:de1404b45a69 (inactive)
-  default                        2:28ad74487de9 (inactive)
+  default                        1:29becc82797a (inactive)
   $ hg branch
   secret
 
diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py
--- a/mercurial/scmutil.py
+++ b/mercurial/scmutil.py
@@ -349,7 +349,7 @@
         self._newfiles.add(f)
 
 
-def filteredhash(repo, maxrev):
+def filteredhash(repo, maxrev, needobsolete=False):
     """build hash of filtered revisions in the current repoview.
 
     Multiple caches perform up-to-date validation by checking that the
@@ -358,22 +358,33 @@
     of revisions in the view may change without the repository tiprev and
     tipnode changing.
 
-    This function hashes all the revs filtered from the view and returns
-    that SHA-1 digest.
+    This function hashes all the revs filtered from the view (and, optionally,
+    all obsolete revs) up to maxrev and returns that SHA-1 digest.
     """
     cl = repo.changelog
-    if not cl.filteredrevs:
-        return None
-    key = cl._filteredrevs_hashcache.get(maxrev)
-    if not key:
-        revs = sorted(r for r in cl.filteredrevs if r <= maxrev)
+    if needobsolete:
+        obsrevs = obsolete.getrevs(repo, b'obsolete')
+        if not cl.filteredrevs and not obsrevs:
+            return None
+        # TODO: obsrevs should be a frozenset, but right now obsolete.getrevs()
+        # may return a set, which is not a hashable type.
+        key = (maxrev, hash(cl.filteredrevs), hash(frozenset(obsrevs)))
+    else:
+        if not cl.filteredrevs:
+            return None
+        key = maxrev
+        obsrevs = frozenset()
+
+    result = cl._filteredrevs_hashcache.get(key)
+    if not result:
+        revs = sorted(r for r in cl.filteredrevs | obsrevs if r <= maxrev)
         if revs:
             s = hashutil.sha1()
             for rev in revs:
                 s.update(b'%d;' % rev)
-            key = s.digest()
-            cl._filteredrevs_hashcache[maxrev] = key
-    return key
+            result = s.digest()
+            cl._filteredrevs_hashcache[key] = result
+    return result
 
 
 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
diff --git a/mercurial/destutil.py b/mercurial/destutil.py
--- a/mercurial/destutil.py
+++ b/mercurial/destutil.py
@@ -79,6 +79,9 @@
             node = repo.revs(b'max(%ln)', successors).first()
             if bookmarks.isactivewdirparent(repo):
                 movemark = repo[b'.'].node()
+        else:
+            # TODO: copy hg prune logic
+            node = repo[b'.'].node()
     return node, movemark, None
 
 
diff --git a/mercurial/branchmap.py b/mercurial/branchmap.py
--- a/mercurial/branchmap.py
+++ b/mercurial/branchmap.py
@@ -17,6 +17,7 @@
 from . import (
     encoding,
     error,
+    obsolete,
     pycompat,
     scmutil,
     util,
@@ -177,7 +178,7 @@
 
     The first line is used to check if the cache is still valid. If the
     branch cache is for a filtered repo view, an optional third hash is
-    included that hashes the hashes of all filtered revisions.
+    included that hashes the hashes of all filtered and obsolete revisions.
 
     The open/closed state is represented by a single letter 'o' or 'c'.
     This field can be used to avoid changelog reads when determining if a
@@ -349,7 +350,8 @@
         - True when cache is up to date or a subset of current repo."""
         try:
             return (self.tipnode == repo.changelog.node(self.tiprev)) and (
-                self.filteredhash == scmutil.filteredhash(repo, self.tiprev)
+                self.filteredhash
+                == scmutil.filteredhash(repo, self.tiprev, needobsolete=True)
             )
         except IndexError:
             return False
@@ -462,6 +464,9 @@
         # use the faster unfiltered parent accessor.
         parentrevs = repo.unfiltered().changelog.parentrevs
 
+        # Faster than using ctx.obsolete()
+        obsrevs = obsolete.getrevs(repo, b'obsolete')
+
         for branch, newheadrevs in pycompat.iteritems(newbranches):
             # For every branch, compute the new branchheads.
             # A branchhead is a revision such that no descendant is on
@@ -502,6 +507,11 @@
             bheadset = {cl.rev(node) for node in bheads}
             uncertain = set()
             for newrev in sorted(newheadrevs):
+                if newrev in obsrevs:
+                    # We ignore obsolete changesets as they shouldn't be
+                    # considered heads.
+                    continue
+
                 if not bheadset:
                     bheadset.add(newrev)
                     continue
@@ -509,13 +519,22 @@
                 parents = [p for p in parentrevs(newrev) if p != nullrev]
                 samebranch = set()
                 otherbranch = set()
+                obsparents = set()
                 for p in parents:
-                    if p in bheadset or getbranchinfo(p)[0] == branch:
+                    if p in obsrevs:
+                        # We ignored this obsolete changeset earlier, but now
+                        # that it has non-ignored children, we need to make
+                        # sure their ancestors are not considered heads. To
+                        # achieve that, we will simply treat this obsolete
+                        # changeset as a parent from other branch.
+                        obsparents.add(p)
+                    elif p in bheadset or getbranchinfo(p)[0] == branch:
                         samebranch.add(p)
                     else:
                         otherbranch.add(p)
-                if otherbranch and not (len(bheadset) == len(samebranch) == 1):
+                if not (len(bheadset) == len(samebranch) == 1):
                     uncertain.update(otherbranch)
+                    uncertain.update(obsparents)
                 bheadset.difference_update(samebranch)
                 bheadset.add(newrev)
 
@@ -524,11 +543,12 @@
                     topoheads = set(cl.headrevs())
                 if bheadset - topoheads:
                     floorrev = min(bheadset)
-                    ancestors = set(cl.ancestors(newheadrevs, floorrev))
-                    bheadset -= ancestors
+                    if floorrev <= max(uncertain):
+                        ancestors = set(cl.ancestors(uncertain, floorrev))
+                        bheadset -= ancestors
             bheadrevs = sorted(bheadset)
             self[branch] = [cl.node(rev) for rev in bheadrevs]
-            tiprev = bheadrevs[-1]
+            tiprev = max(newheadrevs)
             if tiprev > ntiprev:
                 ntiprev = tiprev
 
@@ -537,15 +557,24 @@
             self.tipnode = cl.node(ntiprev)
 
         if not self.validfor(repo):
-            # cache key are not valid anymore
+            # old cache key is now invalid for the repo, but we've just updated
+            # the cache and we assume it's valid, so let's make the cache key
+            # valid as well by recomputing it from the cached data
             self.tipnode = repo.nullid
             self.tiprev = nullrev
             for heads in self.iterheads():
+                if not heads:
+                    # all revisions on a branch are obsolete
+                    continue
+                # note: tiprev is not necessarily the tip revision of repo,
+                # because the tip could be obsolete (i.e. not a head)
                 tiprev = max(cl.rev(node) for node in heads)
                 if tiprev > self.tiprev:
                     self.tipnode = cl.node(tiprev)
                     self.tiprev = tiprev
-        self.filteredhash = scmutil.filteredhash(repo, self.tiprev)
+        self.filteredhash = scmutil.filteredhash(
+            repo, self.tiprev, needobsolete=True
+        )
 
         duration = util.timer() - starttime
         repo.ui.log(



To: av6, #hg-reviewers
Cc: marmoute, mercurial-patches
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mercurial-scm.org/pipermail/mercurial-patches/attachments/20220203/0f712e24/attachment-0001.html>


More information about the Mercurial-patches mailing list