branchmap: cache open/closed branch head information
This lets us determine the open/closed state of a branch without
reading from the changelog (which can be costly over NFS and/or with
many branches).
--- a/mercurial/branchmap.py Mon Nov 11 21:16:54 2013 +1100
+++ b/mercurial/branchmap.py Mon Sep 16 01:08:29 2013 -0700
@@ -11,7 +11,7 @@
def _filename(repo):
"""name of a branchcache file for a given repo or repoview"""
- filename = "cache/branchheads"
+ filename = "cache/branch2"
if repo.filtername:
filename = '%s-%s' % (filename, repo.filtername)
return filename
@@ -39,11 +39,16 @@
for l in lines:
if not l:
continue
- node, label = l.split(" ", 1)
+ node, state, label = l.split(" ", 2)
+ if state not in 'oc':
+ raise ValueError('invalid branch state')
label = encoding.tolocal(label.strip())
if not node in repo:
raise ValueError('node %s does not exist' % node)
- partial.setdefault(label, []).append(bin(node))
+ node = bin(node)
+ partial.setdefault(label, []).append(node)
+ if state == 'c':
+ partial._closednodes.add(node)
except KeyboardInterrupt:
raise
except Exception, inst:
@@ -102,21 +107,32 @@
The cache is serialized on disk in the following format:
<tip hex node> <tip rev number> [optional filtered repo hex hash]
- <branch head hex node> <branch name>
- <branch head hex node> <branch name>
+ <branch head hex node> <open/closed state> <branch name>
+ <branch head hex node> <open/closed state> <branch name>
...
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.
+
+ 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
+ branch head closes a branch or not.
"""
def __init__(self, entries=(), tipnode=nullid, tiprev=nullrev,
- filteredhash=None):
+ filteredhash=None, closednodes=None):
super(branchcache, self).__init__(entries)
self.tipnode = tipnode
self.tiprev = tiprev
self.filteredhash = filteredhash
+ # closednodes is a set of nodes that close their branch. If the branch
+ # cache has been updated, it may contain nodes that are no longer
+ # heads.
+ if closednodes is None:
+ self._closednodes = set()
+ else:
+ self._closednodes = closednodes
def _hashfiltered(self, repo):
"""build hash of revision filtered in the current cache
@@ -152,7 +168,8 @@
def copy(self):
"""return an deep copy of the branchcache object"""
- return branchcache(self, self.tipnode, self.tiprev, self.filteredhash)
+ return branchcache(self, self.tipnode, self.tiprev, self.filteredhash,
+ self._closednodes)
def write(self, repo):
try:
@@ -163,7 +180,12 @@
f.write(" ".join(cachekey) + '\n')
for label, nodes in sorted(self.iteritems()):
for node in nodes:
- f.write("%s %s\n" % (hex(node), encoding.fromlocal(label)))
+ if node in self._closednodes:
+ state = 'c'
+ else:
+ state = 'o'
+ f.write("%s %s %s\n" % (hex(node), state,
+ encoding.fromlocal(label)))
f.close()
except (IOError, OSError, util.Abort):
# Abort may be raise by read only opener
@@ -177,9 +199,13 @@
cl = repo.changelog
# collect new branch entries
newbranches = {}
- getbranch = cl.branch
+ getbranchinfo = cl.branchinfo
for r in revgen:
- newbranches.setdefault(getbranch(r), []).append(cl.node(r))
+ branch, closesbranch = getbranchinfo(r)
+ node = cl.node(r)
+ newbranches.setdefault(branch, []).append(node)
+ if closesbranch:
+ self._closednodes.add(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)
--- a/mercurial/changelog.py Mon Nov 11 21:16:54 2013 +1100
+++ b/mercurial/changelog.py Mon Sep 16 01:08:29 2013 -0700
@@ -342,9 +342,10 @@
text = "\n".join(l)
return self.addrevision(text, transaction, len(self), p1, p2)
- def branch(self, rev):
- """return the branch of a revision
+ def branchinfo(self, rev):
+ """return the branch name and open/close state of a revision
This function exists because creating a changectx object
just to access this is costly."""
- return encoding.tolocal(self.read(rev)[5].get("branch"))
+ extra = self.read(rev)[5]
+ return encoding.tolocal(extra.get("branch")), 'close' in extra
--- a/mercurial/hg.py Mon Nov 11 21:16:54 2013 +1100
+++ b/mercurial/hg.py Mon Sep 16 01:08:29 2013 -0700
@@ -338,8 +338,8 @@
# Recomputing branch cache might be slow on big repos,
# so just copy it
dstcachedir = os.path.join(destpath, 'cache')
- srcbranchcache = srcrepo.sjoin('cache/branchheads')
- dstbranchcache = os.path.join(dstcachedir, 'branchheads')
+ srcbranchcache = srcrepo.sjoin('cache/branch2')
+ dstbranchcache = os.path.join(dstcachedir, 'branch2')
if os.path.exists(srcbranchcache):
if not os.path.exists(dstcachedir):
os.mkdir(dstcachedir)
--- a/tests/test-fncache.t Mon Nov 11 21:16:54 2013 +1100
+++ b/tests/test-fncache.t Mon Sep 16 01:08:29 2013 -0700
@@ -70,7 +70,7 @@
.hg/00changelog.i
.hg/00manifest.i
.hg/cache
- .hg/cache/branchheads-served
+ .hg/cache/branch2-served
.hg/data
.hg/data/tst.d.hg
.hg/data/tst.d.hg/foo.i
@@ -98,7 +98,7 @@
.hg
.hg/00changelog.i
.hg/cache
- .hg/cache/branchheads-served
+ .hg/cache/branch2-served
.hg/dirstate
.hg/last-message.txt
.hg/requires
--- a/tests/test-hardlinks.t Mon Nov 11 21:16:54 2013 +1100
+++ b/tests/test-hardlinks.t Mon Sep 16 01:08:29 2013 -0700
@@ -196,7 +196,7 @@
$ nlinksdir r4
2 r4/.hg/00changelog.i
2 r4/.hg/branch
- 2 r4/.hg/cache/branchheads-served
+ 2 r4/.hg/cache/branch2-served
2 r4/.hg/dirstate
2 r4/.hg/hgrc
2 r4/.hg/last-message.txt
@@ -226,7 +226,7 @@
$ nlinksdir r4
2 r4/.hg/00changelog.i
1 r4/.hg/branch
- 2 r4/.hg/cache/branchheads-served
+ 2 r4/.hg/cache/branch2-served
1 r4/.hg/dirstate
2 r4/.hg/hgrc
2 r4/.hg/last-message.txt
--- a/tests/test-inherit-mode.t Mon Nov 11 21:16:54 2013 +1100
+++ b/tests/test-inherit-mode.t Mon Sep 16 01:08:29 2013 -0700
@@ -66,7 +66,7 @@
00700 ./.hg/
00600 ./.hg/00changelog.i
00770 ./.hg/cache/
- 00660 ./.hg/cache/branchheads-served
+ 00660 ./.hg/cache/branch2-served
00660 ./.hg/dirstate
00660 ./.hg/last-message.txt
00600 ./.hg/requires
@@ -111,7 +111,7 @@
00770 ../push/.hg/
00660 ../push/.hg/00changelog.i
00770 ../push/.hg/cache/
- 00660 ../push/.hg/cache/branchheads-base
+ 00660 ../push/.hg/cache/branch2-base
00660 ../push/.hg/requires
00770 ../push/.hg/store/
00660 ../push/.hg/store/00changelog.i
--- a/tests/test-newbranch.t Mon Nov 11 21:16:54 2013 +1100
+++ b/tests/test-newbranch.t Mon Sep 16 01:08:29 2013 -0700
@@ -1,13 +1,13 @@
- $ branchcache=.hg/cache/branchheads
+ $ branchcache=.hg/cache/branch2
$ listbranchcaches() {
- > for f in .hg/cache/branchheads*;
+ > for f in .hg/cache/branch2*;
> do echo === $f ===;
> cat $f;
> done;
> }
$ purgebranchcaches() {
- > rm .hg/cache/branchheads*
+ > rm .hg/cache/branch2*
> }
$ hg init t
@@ -158,13 +158,13 @@
4:adf1a74a7f7b
$ listbranchcaches
- === .hg/cache/branchheads ===
+ === .hg/cache/branch2 ===
corrupted
- === .hg/cache/branchheads-served ===
+ === .hg/cache/branch2-served ===
adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 4
- c21617b13b220988e7a2e26290fbe4325ffa7139 bar
- 1c28f494dae69a2f8fc815059d257eccf3fcfe75 default
- adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 foo
+ c21617b13b220988e7a2e26290fbe4325ffa7139 o bar
+ 1c28f494dae69a2f8fc815059d257eccf3fcfe75 o default
+ adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 o foo
Push should update the branch cache:
@@ -175,20 +175,20 @@
$ hg push -qr 0 ../target
$ (cd ../target/; listbranchcaches)
- === .hg/cache/branchheads-base ===
+ === .hg/cache/branch2-base ===
db01e8ea3388fd3c7c94e1436ea2bd6a53d581c5 0
- db01e8ea3388fd3c7c94e1436ea2bd6a53d581c5 default
+ db01e8ea3388fd3c7c94e1436ea2bd6a53d581c5 o default
Pushing everything:
$ hg push -qf ../target
$ (cd ../target/; listbranchcaches)
- === .hg/cache/branchheads-base ===
+ === .hg/cache/branch2-base ===
adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 4
- c21617b13b220988e7a2e26290fbe4325ffa7139 bar
- 1c28f494dae69a2f8fc815059d257eccf3fcfe75 default
- adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 foo
+ c21617b13b220988e7a2e26290fbe4325ffa7139 o bar
+ 1c28f494dae69a2f8fc815059d257eccf3fcfe75 o default
+ adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 o foo
Update with no arguments: tipmost revision of the current branch:
--- a/tests/test-phases.t Mon Nov 11 21:16:54 2013 +1100
+++ b/tests/test-phases.t Mon Sep 16 01:08:29 2013 -0700
@@ -175,28 +175,28 @@
check that branch cache with "served" filter are properly computed and stored
- $ ls ../push-dest/.hg/cache/branchheads*
- ../push-dest/.hg/cache/branchheads-served
- $ cat ../push-dest/.hg/cache/branchheads-served
+ $ ls ../push-dest/.hg/cache/branch2*
+ ../push-dest/.hg/cache/branch2-served
+ $ cat ../push-dest/.hg/cache/branch2-served
6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
- b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e default
- 6d6770faffce199f1fddd1cf87f6f026138cf061 default
+ b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
+ 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
$ hg heads -R ../push-dest --template '{rev}:{node} {phase}\n' #update visible cache too
6:6d6770faffce199f1fddd1cf87f6f026138cf061 draft
5:2713879da13d6eea1ff22b442a5a87cb31a7ce6a secret
3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e draft
- $ ls ../push-dest/.hg/cache/branchheads*
- ../push-dest/.hg/cache/branchheads-served
- ../push-dest/.hg/cache/branchheads-visible
- $ cat ../push-dest/.hg/cache/branchheads-served
+ $ ls ../push-dest/.hg/cache/branch2*
+ ../push-dest/.hg/cache/branch2-served
+ ../push-dest/.hg/cache/branch2-visible
+ $ cat ../push-dest/.hg/cache/branch2-served
6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
- b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e default
- 6d6770faffce199f1fddd1cf87f6f026138cf061 default
- $ cat ../push-dest/.hg/cache/branchheads-visible
+ b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
+ 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
+ $ cat ../push-dest/.hg/cache/branch2-visible
6d6770faffce199f1fddd1cf87f6f026138cf061 6
- b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e default
- 2713879da13d6eea1ff22b442a5a87cb31a7ce6a default
- 6d6770faffce199f1fddd1cf87f6f026138cf061 default
+ b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
+ 2713879da13d6eea1ff22b442a5a87cb31a7ce6a o default
+ 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
Restore condition prior extra insertion.
--- a/tests/test-rebase-collapse.t Mon Nov 11 21:16:54 2013 +1100
+++ b/tests/test-rebase-collapse.t Mon Sep 16 01:08:29 2013 -0700
@@ -274,18 +274,18 @@
7:c65502d4178782309ce0574c5ae6ee9485a9bafa default
6:c772a8b2dc17629cec88a19d09c926c4814b12c7 default
- $ cat $TESTTMP/b2/.hg/cache/branchheads-served
+ $ cat $TESTTMP/b2/.hg/cache/branch2-served
c65502d4178782309ce0574c5ae6ee9485a9bafa 7
- c772a8b2dc17629cec88a19d09c926c4814b12c7 default
- c65502d4178782309ce0574c5ae6ee9485a9bafa default
+ c772a8b2dc17629cec88a19d09c926c4814b12c7 o default
+ c65502d4178782309ce0574c5ae6ee9485a9bafa o default
$ hg strip 4
saved backup bundle to $TESTTMP/b2/.hg/strip-backup/8a5212ebc852-backup.hg (glob)
- $ cat $TESTTMP/b2/.hg/cache/branchheads-served
+ $ cat $TESTTMP/b2/.hg/cache/branch2-served
c65502d4178782309ce0574c5ae6ee9485a9bafa 4
- 2870ad076e541e714f3c2bc32826b5c6a6e5b040 default
- c65502d4178782309ce0574c5ae6ee9485a9bafa default
+ 2870ad076e541e714f3c2bc32826b5c6a6e5b040 o default
+ c65502d4178782309ce0574c5ae6ee9485a9bafa o default
$ hg heads --template="{rev}:{node} {branch}\n"
4:c65502d4178782309ce0574c5ae6ee9485a9bafa default