Branch heads should not include "heads" that are ancestors of other heads.
authorBrendan Cully <brendan@kublai.com>
Mon, 29 Jun 2009 00:54:23 -0700
changeset 8954 e67e5b60e55f
parent 8953 e1d119f450f0
child 8955 0405f7ccb274
Branch heads should not include "heads" that are ancestors of other heads. For example, given 1 (branch a) -> 2 (branch b) -> 3 (branch a) I expect "hg heads a" to show only 3. Discovered by running hg heads HEAD on the mutt repo, where older clients committed default on top of HEAD.
mercurial/localrepo.py
tests/test-newbranch
--- a/mercurial/localrepo.py	Sun Jun 28 19:31:16 2009 +0900
+++ b/mercurial/localrepo.py	Mon Jun 29 00:54:23 2009 -0700
@@ -454,15 +454,30 @@
             pass
 
     def _updatebranchcache(self, partial, start, end):
+        # collect new branch entries
+        newbranches = {}
         for r in xrange(start, end):
             c = self[r]
-            b = c.branch()
-            bheads = partial.setdefault(b, [])
-            bheads.append(c.node())
-            for p in c.parents():
-                pn = p.node()
-                if pn in bheads:
-                    bheads.remove(pn)
+            newbranches.setdefault(c.branch(), []).append(c.node())
+        # if older branchheads are reachable from new ones, they aren't
+        # really branchheads. Note checking parents is insufficient:
+        # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
+        for branch, newnodes in newbranches.iteritems():
+            bheads = partial.setdefault(branch, [])
+            bheads.extend(newnodes)
+            if len(bheads) < 2:
+                continue
+            newbheads = []
+            # starting from tip means fewer passes over reachable
+            while newnodes:
+                latest = newnodes.pop()
+                if latest not in bheads:
+                    continue
+                reachable = self.changelog.reachable(latest, bheads[0])
+                bheads = [b for b in bheads if b not in reachable]
+                newbheads.insert(0, latest)
+            bheads.extend(newbheads)
+            partial[branch] = bheads
 
     def lookup(self, key):
         if isinstance(key, int):
--- a/tests/test-newbranch	Sun Jun 28 19:31:16 2009 +0900
+++ b/tests/test-newbranch	Mon Jun 29 00:54:23 2009 -0700
@@ -19,6 +19,9 @@
 hg branch -f default
 hg ci -m "clear branch name" -d "1000000 0"
 
+echo % there should be only one default branch head
+hg heads .
+
 hg co foo
 hg branch
 echo bleah > a