[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