[PATCH 1 of 3] localrepo: store closed state of each head in the branch cache

Matt Mackall mpm at selenic.com
Tue Feb 7 19:09:25 UTC 2012


On Tue, 2012-02-07 at 22:48 +0800, Steven Brown wrote:
> # HG changeset patch
> # User Steven Brown <StevenGBrown at gmail.com>
> # Date 1328623028 -28800
> # Node ID 0a5cacc501b4c680832c5ab9c775321611a6015c
> # Parent  8af9e08a094ff43f828085e1102b320995e0c1b2
> localrepo: store closed state of each head in the branch cache
> 
> This will reduce the time taken to query the branches in a branchy repo.
> Profiling shows that although the closed state of a single revision can be
> determined very quickly, this is repeated many times and so it accounts for most
> of the time taken.
> 
> The heads for each branch are still cached at .hg/cache/branchheads. The closed
> state of each head is now stored in the first column. When switching to this
> cset from an earlier version of Mercurial, or switching back again, the cache
> will be rebuilt.

What happens on a repo shared on NFS by users with two different
versions? Our usual solution here is to move to a new cache name when we
tweak the format.

> diff --git a/mercurial/discovery.py b/mercurial/discovery.py
> --- a/mercurial/discovery.py
> +++ b/mercurial/discovery.py
> @@ -183,12 +183,12 @@
>          # 4. Update newmap with outgoing changes.
>          # This will possibly add new heads and remove existing ones.
>          ctxgen = (repo[n] for n in outgoing.missing)
> -        repo._updatebranchcache(newmap, ctxgen)
> +        repo.updatebranchheads(newmap, ctxgen)

Why are we changing the name of everything? Why is this no longer marked
as an internal method?

>      else:
>          # 1-4b. old servers: Check for new topological heads.
>          # Construct {old,new}map with branch = None (topological branch).
> -        # (code based on _updatebranchcache)
> +        # (code based on _updatebranchheads)

Doesn't match.

>          oldheads = set(h for h in remoteheads if h in cl.nodemap)
>          newheads = oldheads.union(outgoing.missing)
>          if len(newheads) > 1:
> diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
> --- a/mercurial/localrepo.py
> +++ b/mercurial/localrepo.py
> @@ -500,10 +500,14 @@
>          # this private cache holds all heads (not just tips)
>          self._branchcache = partial
>  
> +    def updatebranchheads(self, partialheads, ctxgen):
> +        '''update partialheads with the branch heads provided by ctxgen'''
> +        self._updatebranchheads(partialheads, ctxgen)
> +

This seems redundant?

>      def branchmap(self):
>          '''returns a dictionary {branch: [branchheads]}'''
>          self.updatebranchcache()
> -        return self._branchcache
> +        return self._branchcache.heads
>  
>      def branchtags(self):
>          '''return a dict where branch names map to the tipmost head of
> @@ -519,13 +523,13 @@
>          return bt
>  
>      def _readbranchcache(self):
> -        partial = {}
> +        partial = branchcache()
>          try:
>              f = self.opener("cache/branchheads")
>              lines = f.read().split('\n')
>              f.close()
>          except (IOError, OSError):
> -            return {}, nullid, nullrev
> +            return partial, nullid, nullrev
>  
>          try:
>              last, lrev = lines.pop(0).split(" ", 1)
> @@ -536,29 +540,45 @@
>              for l in lines:
>                  if not l:
>                      continue
> -                node, label = l.split(" ", 1)
> -                label = encoding.tolocal(label.strip())
> -                partial.setdefault(label, []).append(bin(node))
> +                closed = l[:1]
> +                node = bin(l[2:42])
> +                label = encoding.tolocal(l[43:].strip())
> +                partial.heads.setdefault(label, []).append(node)
> +                openheads = partial.openheads.setdefault(label, [])
> +                if closed != 'c':
> +                    openheads.append(node)
>          except KeyboardInterrupt:
>              raise
>          except Exception, inst:
>              if self.ui.debugflag:
>                  self.ui.warn(str(inst), '\n')
> -            partial, last, lrev = {}, nullid, nullrev
> +            partial, last, lrev = branchcache(), nullid, nullrev
>          return partial, last, lrev
>  
>      def _writebranchcache(self, branches, tip, tiprev):
>          try:
>              f = self.opener("cache/branchheads", "w", atomictemp=True)
>              f.write("%s %s\n" % (hex(tip), tiprev))
> -            for label, nodes in branches.iteritems():
> +            for label, nodes in branches.heads.iteritems():
> +                openheads = branches.openheads[label]
>                  for node in nodes:
> -                    f.write("%s %s\n" % (hex(node), encoding.fromlocal(label)))
> +                    closed = '_'
> +                    if node not in openheads:
> +                        closed = 'c'
> +                    enclabel = encoding.fromlocal(label)
> +                    f.write("%s %s %s\n" % (closed, hex(node), enclabel))
>              f.close()
>          except (IOError, OSError):
>              pass
>  
>      def _updatebranchcache(self, partial, ctxgen):
> +        updated = self._updatebranchheads(partial.heads, ctxgen)
> +        for branch in updated:
> +            headsctx = [self[h] for h in partial.heads[branch]]
> +            partial.openheads[branch] = \
> +                [h.node() for h in headsctx if 'close' not in h.extra()]
> +
> +    def _updatebranchheads(self, partialheads, ctxgen):
>          # collect new branch entries
>          newbranches = {}
>          for c in ctxgen:
> @@ -567,7 +587,7 @@
>          # 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 = partialheads.setdefault(branch, [])
>              bheads.extend(newnodes)
>              if len(bheads) <= 1:
>                  continue
> @@ -582,7 +602,8 @@
>                  reachable.remove(latest)
>                  if reachable:
>                      bheads = [b for b in bheads if b not in reachable]
> -            partial[branch] = bheads
> +            partialheads[branch] = bheads
> +        return set(newbranches.keys())
>  
>      def lookup(self, key):
>          if isinstance(key, int):
> @@ -2308,3 +2329,8 @@
>  
>  def islocal(path):
>      return True
> +
> +class branchcache(object):
> +    def __init__(self):
> +        self.heads = {}
> +        self.openheads = {}
> diff --git a/tests/test-mq-caches.t b/tests/test-mq-caches.t
> --- a/tests/test-mq-caches.t
> +++ b/tests/test-mq-caches.t
> @@ -8,7 +8,8 @@
>    >     hg log -r does-not-exist 2> /dev/null
>    >     hg log -r tip --template 'tip: {rev}\n'
>    >     if [ -f $branches ]; then
> -  >       sort $branches
> +  >       head -n1 $branches
> +  >       tail -n+2 $branches | sort
>    >     else
>    >       echo No branch cache
>    >     fi
> @@ -30,7 +31,7 @@
>    $ show_branch_cache
>    tip: 0
>    d986d5caac23a7d44a46efc0ddaf5eb9665844cf 0
> -  d986d5caac23a7d44a46efc0ddaf5eb9665844cf default
> +  _ d986d5caac23a7d44a46efc0ddaf5eb9665844cf default
>  
>    $ echo > pfile
>    $ hg add pfile
> @@ -38,7 +39,7 @@
>    $ show_branch_cache
>    tip: 0
>    a7977e38ed2c2942fa6c278030badfef3d180979 0
> -  a7977e38ed2c2942fa6c278030badfef3d180979 default
> +  _ a7977e38ed2c2942fa6c278030badfef3d180979 default
>  
>  some regular revisions
>  
> @@ -57,8 +58,8 @@
>    $ show_branch_cache
>    tip: 1
>    c229711f16da3d7591f89b1b8d963b79bda22714 1
> -  c229711f16da3d7591f89b1b8d963b79bda22714 bar
> -  dc25e3827021582e979f600811852e36cbe57341 foo
> +  _ c229711f16da3d7591f89b1b8d963b79bda22714 bar
> +  _ dc25e3827021582e979f600811852e36cbe57341 foo
>  
>  add some mq patches
>  
> @@ -68,8 +69,8 @@
>    $ show_branch_cache
>    tip: 2
>    982611f6955f9c48d3365decea203217c945ef0d 2
> -  982611f6955f9c48d3365decea203217c945ef0d bar
> -  dc25e3827021582e979f600811852e36cbe57341 foo
> +  _ 982611f6955f9c48d3365decea203217c945ef0d bar
> +  _ dc25e3827021582e979f600811852e36cbe57341 foo
>  
>    $ hg qnew -d '0 0' p2
>    $ echo foo > .hg/branch
> @@ -78,8 +79,8 @@
>    $ show_branch_cache 1
>    tip: 3
>    982611f6955f9c48d3365decea203217c945ef0d 2
> -  982611f6955f9c48d3365decea203217c945ef0d bar
> -  dc25e3827021582e979f600811852e36cbe57341 foo
> +  _ 982611f6955f9c48d3365decea203217c945ef0d bar
> +  _ dc25e3827021582e979f600811852e36cbe57341 foo
>    branch foo: 3
>    branch bar: 2
>  
> @@ -89,8 +90,8 @@
>    $ show_branch_cache 1
>    tip: 3
>    c229711f16da3d7591f89b1b8d963b79bda22714 1
> -  c229711f16da3d7591f89b1b8d963b79bda22714 bar
> -  dc25e3827021582e979f600811852e36cbe57341 foo
> +  _ c229711f16da3d7591f89b1b8d963b79bda22714 bar
> +  _ dc25e3827021582e979f600811852e36cbe57341 foo
>    branch foo: 3
>    branch bar: 2
>  
> @@ -100,8 +101,8 @@
>    $ show_branch_cache 1
>    tip: 3
>    c229711f16da3d7591f89b1b8d963b79bda22714 1
> -  c229711f16da3d7591f89b1b8d963b79bda22714 bar
> -  dc25e3827021582e979f600811852e36cbe57341 foo
> +  _ c229711f16da3d7591f89b1b8d963b79bda22714 bar
> +  _ dc25e3827021582e979f600811852e36cbe57341 foo
>    branch foo: 3
>    branch bar: 2
>    $ hg log -r qbase --template 'qbase: {rev}\n'
> @@ -122,5 +123,5 @@
>    $ show_branch_cache
>    tip: 3
>    3fe2e3b237359b5c55cec6ed172ac41d3850fade 1
> -  3fe2e3b237359b5c55cec6ed172ac41d3850fade foo
> +  _ 3fe2e3b237359b5c55cec6ed172ac41d3850fade foo
>  
> diff --git a/tests/test-newbranch.t b/tests/test-newbranch.t
> --- a/tests/test-newbranch.t
> +++ b/tests/test-newbranch.t
> @@ -150,9 +150,9 @@
>  
>    $ cat $branchcache
>    adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 4
> -  1c28f494dae69a2f8fc815059d257eccf3fcfe75 default
> -  adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 foo
> -  c21617b13b220988e7a2e26290fbe4325ffa7139 bar
> +  _ 1c28f494dae69a2f8fc815059d257eccf3fcfe75 default
> +  _ adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 foo
> +  _ c21617b13b220988e7a2e26290fbe4325ffa7139 bar
>  
>  Push should update the branch cache:
>  
> @@ -164,7 +164,7 @@
>  
>    $ cat ../target/$branchcache
>    db01e8ea3388fd3c7c94e1436ea2bd6a53d581c5 0
> -  db01e8ea3388fd3c7c94e1436ea2bd6a53d581c5 default
> +  _ db01e8ea3388fd3c7c94e1436ea2bd6a53d581c5 default
>  
>  Pushing everything:
>  
> @@ -172,9 +172,37 @@
>  
>    $ cat ../target/$branchcache
>    adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 4
> -  1c28f494dae69a2f8fc815059d257eccf3fcfe75 default
> -  adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 foo
> -  c21617b13b220988e7a2e26290fbe4325ffa7139 bar
> +  _ 1c28f494dae69a2f8fc815059d257eccf3fcfe75 default
> +  _ adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 foo
> +  _ c21617b13b220988e7a2e26290fbe4325ffa7139 bar
> +
> +Closed branch in the cache:
> +
> +  $ hg -R ../target update bar
> +  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
> +  $ hg -R ../target ci -m "close" --close-branch
> +
> +  $ cat ../target/$branchcache
> +  7f3676b7004d4616a72ccf9ecccbc5c38e128882 5
> +  _ 1c28f494dae69a2f8fc815059d257eccf3fcfe75 default
> +  _ adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 foo
> +  c 7f3676b7004d4616a72ccf9ecccbc5c38e128882 bar
> +
> +Cache created with Mercurial 2.1 or earlier will be rebuilt:
> +
> +  $ echo '7f3676b7004d4616a72ccf9ecccbc5c38e128882 5' > ../target/$branchcache
> +  $ echo '1c28f494dae69a2f8fc815059d257eccf3fcfe75 default' >> ../target/$branchcache
> +  $ echo 'adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 foo' >> ../target/$branchcache
> +  $ echo '7f3676b7004d4616a72ccf9ecccbc5c38e128882 bar' >> ../target/$branchcache
> +  $ hg -R ../target branches
> +  foo                            4:adf1a74a7f7b
> +  default                        3:1c28f494dae6
> +
> +  $ cat ../target/$branchcache
> +  7f3676b7004d4616a72ccf9ecccbc5c38e128882 5
> +  _ 1c28f494dae69a2f8fc815059d257eccf3fcfe75 default
> +  _ adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 foo
> +  c 7f3676b7004d4616a72ccf9ecccbc5c38e128882 bar
>  
>  Update with no arguments: tipmost revision of the current branch:
>  
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel


-- 
Mathematics is the supreme nostalgia of our time.





More information about the Mercurial-devel mailing list