##// END OF EJS Templates
branchmap: read and write key part related to filtered revision...
Pierre-Yves David -
r18184:8d48af68 default
parent child Browse files
Show More
@@ -1,200 +1,208 b''
1 # branchmap.py - logic to computes, maintain and stores branchmap for local repo
1 # branchmap.py - logic to computes, maintain and stores branchmap for local repo
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import bin, hex, nullid, nullrev
8 from node import bin, hex, nullid, nullrev
9 import encoding
9 import encoding
10 import util
10 import util
11
11
12 def read(repo):
12 def read(repo):
13 try:
13 try:
14 f = repo.opener("cache/branchheads")
14 f = repo.opener("cache/branchheads")
15 lines = f.read().split('\n')
15 lines = f.read().split('\n')
16 f.close()
16 f.close()
17 except (IOError, OSError):
17 except (IOError, OSError):
18 return branchcache()
18 return branchcache()
19
19
20 try:
20 try:
21 last, lrev = lines.pop(0).split(" ", 1)
21 cachekey = lines.pop(0).split(" ", 2)
22 last, lrev = cachekey[:2]
22 last, lrev = bin(last), int(lrev)
23 last, lrev = bin(last), int(lrev)
23 partial = branchcache(tipnode=last, tiprev=lrev)
24 filteredhash = None
25 if len(cachekey) > 2:
26 filteredhash = bin(cachekey[2])
27 partial = branchcache(tipnode=last, tiprev=lrev,
28 filteredhash=filteredhash)
24 if not partial.validfor(repo):
29 if not partial.validfor(repo):
25 # invalidate the cache
30 # invalidate the cache
26 raise ValueError('tip differs')
31 raise ValueError('tip differs')
27 for l in lines:
32 for l in lines:
28 if not l:
33 if not l:
29 continue
34 continue
30 node, label = l.split(" ", 1)
35 node, label = l.split(" ", 1)
31 label = encoding.tolocal(label.strip())
36 label = encoding.tolocal(label.strip())
32 if not node in repo:
37 if not node in repo:
33 raise ValueError('node %s does not exist' % node)
38 raise ValueError('node %s does not exist' % node)
34 partial.setdefault(label, []).append(bin(node))
39 partial.setdefault(label, []).append(bin(node))
35 except KeyboardInterrupt:
40 except KeyboardInterrupt:
36 raise
41 raise
37 except Exception, inst:
42 except Exception, inst:
38 if repo.ui.debugflag:
43 if repo.ui.debugflag:
39 repo.ui.warn(('invalid branchheads cache: %s\n') % inst)
44 repo.ui.warn(('invalid branchheads cache: %s\n') % inst)
40 partial = branchcache()
45 partial = branchcache()
41 return partial
46 return partial
42
47
43
48
44
49
45 def updatecache(repo):
50 def updatecache(repo):
46 repo = repo.unfiltered() # Until we get a smarter cache management
51 repo = repo.unfiltered() # Until we get a smarter cache management
47 cl = repo.changelog
52 cl = repo.changelog
48 partial = repo._branchcache
53 partial = repo._branchcache
49
54
50 if partial is None or not partial.validfor(repo):
55 if partial is None or not partial.validfor(repo):
51 partial = read(repo)
56 partial = read(repo)
52
57
53 catip = repo._cacheabletip()
58 catip = repo._cacheabletip()
54 # if partial.tiprev == catip: cache is already up to date
59 # if partial.tiprev == catip: cache is already up to date
55 # if partial.tiprev > catip: we have uncachable element in `partial` can't
60 # if partial.tiprev > catip: we have uncachable element in `partial` can't
56 # write on disk
61 # write on disk
57 if partial.tiprev < catip:
62 if partial.tiprev < catip:
58 ctxgen = (repo[r] for r in cl.revs(partial.tiprev + 1, catip))
63 ctxgen = (repo[r] for r in cl.revs(partial.tiprev + 1, catip))
59 partial.update(repo, ctxgen)
64 partial.update(repo, ctxgen)
60 partial.write(repo)
65 partial.write(repo)
61 # If cacheable tip were lower than actual tip, we need to update the
66 # If cacheable tip were lower than actual tip, we need to update the
62 # cache up to tip. This update (from cacheable to actual tip) is not
67 # cache up to tip. This update (from cacheable to actual tip) is not
63 # written to disk since it's not cacheable.
68 # written to disk since it's not cacheable.
64 tiprev = cl.rev(cl.tip())
69 tiprev = cl.rev(cl.tip())
65 if partial.tiprev < tiprev:
70 if partial.tiprev < tiprev:
66 ctxgen = (repo[r] for r in cl.revs(partial.tiprev + 1, tiprev))
71 ctxgen = (repo[r] for r in cl.revs(partial.tiprev + 1, tiprev))
67 partial.update(repo, ctxgen)
72 partial.update(repo, ctxgen)
68 repo._branchcache = partial
73 repo._branchcache = partial
69
74
70 class branchcache(dict):
75 class branchcache(dict):
71 """A dict like object that hold branches heads cache"""
76 """A dict like object that hold branches heads cache"""
72
77
73 def __init__(self, entries=(), tipnode=nullid, tiprev=nullrev,
78 def __init__(self, entries=(), tipnode=nullid, tiprev=nullrev,
74 filteredhash=None):
79 filteredhash=None):
75 super(branchcache, self).__init__(entries)
80 super(branchcache, self).__init__(entries)
76 self.tipnode = tipnode
81 self.tipnode = tipnode
77 self.tiprev = tiprev
82 self.tiprev = tiprev
78 self.filteredhash = filteredhash
83 self.filteredhash = filteredhash
79
84
80 def _hashfiltered(self, repo):
85 def _hashfiltered(self, repo):
81 """build hash of revision filtered in the current cache
86 """build hash of revision filtered in the current cache
82
87
83 Tracking tipnode and tiprev is not enough to ensure validaty of the
88 Tracking tipnode and tiprev is not enough to ensure validaty of the
84 cache as they do not help to distinct cache that ignored various
89 cache as they do not help to distinct cache that ignored various
85 revision bellow tiprev.
90 revision bellow tiprev.
86
91
87 To detect such difference, we build a cache of all ignored revisions.
92 To detect such difference, we build a cache of all ignored revisions.
88 """
93 """
89 cl = repo.changelog
94 cl = repo.changelog
90 if not cl.filteredrevs:
95 if not cl.filteredrevs:
91 return None
96 return None
92 key = None
97 key = None
93 revs = sorted(r for r in cl.filteredrevs if r <= self.tiprev)
98 revs = sorted(r for r in cl.filteredrevs if r <= self.tiprev)
94 if revs:
99 if revs:
95 s = util.sha1()
100 s = util.sha1()
96 for rev in revs:
101 for rev in revs:
97 s.update('%s;' % rev)
102 s.update('%s;' % rev)
98 key = s.digest()
103 key = s.digest()
99 return key
104 return key
100
105
101 def validfor(self, repo):
106 def validfor(self, repo):
102 """Is the cache content valide regarding a repo
107 """Is the cache content valide regarding a repo
103
108
104 - False when cached tipnode are unknown or if we detect a strip.
109 - False when cached tipnode are unknown or if we detect a strip.
105 - True when cache is up to date or a subset of current repo."""
110 - True when cache is up to date or a subset of current repo."""
106 try:
111 try:
107 return ((self.tipnode == repo.changelog.node(self.tiprev))
112 return ((self.tipnode == repo.changelog.node(self.tiprev))
108 and (self.filteredhash == self._hashfiltered(repo)))
113 and (self.filteredhash == self._hashfiltered(repo)))
109 except IndexError:
114 except IndexError:
110 return False
115 return False
111
116
112
117
113 def write(self, repo):
118 def write(self, repo):
114 try:
119 try:
115 f = repo.opener("cache/branchheads", "w", atomictemp=True)
120 f = repo.opener("cache/branchheads", "w", atomictemp=True)
116 f.write("%s %s\n" % (hex(self.tipnode), self.tiprev))
121 cachekey = [hex(self.tipnode), str(self.tiprev)]
122 if self.filteredhash is not None:
123 cachekey.append(hex(self.filteredhash))
124 f.write(" ".join(cachekey) + '\n')
117 for label, nodes in self.iteritems():
125 for label, nodes in self.iteritems():
118 for node in nodes:
126 for node in nodes:
119 f.write("%s %s\n" % (hex(node), encoding.fromlocal(label)))
127 f.write("%s %s\n" % (hex(node), encoding.fromlocal(label)))
120 f.close()
128 f.close()
121 except (IOError, OSError):
129 except (IOError, OSError):
122 pass
130 pass
123
131
124 def update(self, repo, ctxgen):
132 def update(self, repo, ctxgen):
125 """Given a branchhead cache, self, that may have extra nodes or be
133 """Given a branchhead cache, self, that may have extra nodes or be
126 missing heads, and a generator of nodes that are at least a superset of
134 missing heads, and a generator of nodes that are at least a superset of
127 heads missing, this function updates self to be correct.
135 heads missing, this function updates self to be correct.
128 """
136 """
129 cl = repo.changelog
137 cl = repo.changelog
130 # collect new branch entries
138 # collect new branch entries
131 newbranches = {}
139 newbranches = {}
132 for c in ctxgen:
140 for c in ctxgen:
133 newbranches.setdefault(c.branch(), []).append(c.node())
141 newbranches.setdefault(c.branch(), []).append(c.node())
134 # if older branchheads are reachable from new ones, they aren't
142 # if older branchheads are reachable from new ones, they aren't
135 # really branchheads. Note checking parents is insufficient:
143 # really branchheads. Note checking parents is insufficient:
136 # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
144 # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
137 for branch, newnodes in newbranches.iteritems():
145 for branch, newnodes in newbranches.iteritems():
138 bheads = self.setdefault(branch, [])
146 bheads = self.setdefault(branch, [])
139 # Remove candidate heads that no longer are in the repo (e.g., as
147 # Remove candidate heads that no longer are in the repo (e.g., as
140 # the result of a strip that just happened). Avoid using 'node in
148 # the result of a strip that just happened). Avoid using 'node in
141 # self' here because that dives down into branchcache code somewhat
149 # self' here because that dives down into branchcache code somewhat
142 # recursively.
150 # recursively.
143 bheadrevs = [cl.rev(node) for node in bheads
151 bheadrevs = [cl.rev(node) for node in bheads
144 if cl.hasnode(node)]
152 if cl.hasnode(node)]
145 newheadrevs = [cl.rev(node) for node in newnodes
153 newheadrevs = [cl.rev(node) for node in newnodes
146 if cl.hasnode(node)]
154 if cl.hasnode(node)]
147 ctxisnew = bheadrevs and min(newheadrevs) > max(bheadrevs)
155 ctxisnew = bheadrevs and min(newheadrevs) > max(bheadrevs)
148 # Remove duplicates - nodes that are in newheadrevs and are already
156 # Remove duplicates - nodes that are in newheadrevs and are already
149 # in bheadrevs. This can happen if you strip a node whose parent
157 # in bheadrevs. This can happen if you strip a node whose parent
150 # was already a head (because they're on different branches).
158 # was already a head (because they're on different branches).
151 bheadrevs = sorted(set(bheadrevs).union(newheadrevs))
159 bheadrevs = sorted(set(bheadrevs).union(newheadrevs))
152
160
153 # Starting from tip means fewer passes over reachable. If we know
161 # Starting from tip means fewer passes over reachable. If we know
154 # the new candidates are not ancestors of existing heads, we don't
162 # the new candidates are not ancestors of existing heads, we don't
155 # have to examine ancestors of existing heads
163 # have to examine ancestors of existing heads
156 if ctxisnew:
164 if ctxisnew:
157 iterrevs = sorted(newheadrevs)
165 iterrevs = sorted(newheadrevs)
158 else:
166 else:
159 iterrevs = list(bheadrevs)
167 iterrevs = list(bheadrevs)
160
168
161 # This loop prunes out two kinds of heads - heads that are
169 # This loop prunes out two kinds of heads - heads that are
162 # superseded by a head in newheadrevs, and newheadrevs that are not
170 # superseded by a head in newheadrevs, and newheadrevs that are not
163 # heads because an existing head is their descendant.
171 # heads because an existing head is their descendant.
164 while iterrevs:
172 while iterrevs:
165 latest = iterrevs.pop()
173 latest = iterrevs.pop()
166 if latest not in bheadrevs:
174 if latest not in bheadrevs:
167 continue
175 continue
168 ancestors = set(cl.ancestors([latest],
176 ancestors = set(cl.ancestors([latest],
169 bheadrevs[0]))
177 bheadrevs[0]))
170 if ancestors:
178 if ancestors:
171 bheadrevs = [b for b in bheadrevs if b not in ancestors]
179 bheadrevs = [b for b in bheadrevs if b not in ancestors]
172 self[branch] = [cl.node(rev) for rev in bheadrevs]
180 self[branch] = [cl.node(rev) for rev in bheadrevs]
173 tiprev = max(bheadrevs)
181 tiprev = max(bheadrevs)
174 if tiprev > self.tiprev:
182 if tiprev > self.tiprev:
175 self.tipnode = cl.node(tiprev)
183 self.tipnode = cl.node(tiprev)
176 self.tiprev = tiprev
184 self.tiprev = tiprev
177
185
178 # There may be branches that cease to exist when the last commit in the
186 # There may be branches that cease to exist when the last commit in the
179 # branch was stripped. This code filters them out. Note that the
187 # branch was stripped. This code filters them out. Note that the
180 # branch that ceased to exist may not be in newbranches because
188 # branch that ceased to exist may not be in newbranches because
181 # newbranches is the set of candidate heads, which when you strip the
189 # newbranches is the set of candidate heads, which when you strip the
182 # last commit in a branch will be the parent branch.
190 # last commit in a branch will be the parent branch.
183 droppednodes = []
191 droppednodes = []
184 for branch in self.keys():
192 for branch in self.keys():
185 nodes = [head for head in self[branch]
193 nodes = [head for head in self[branch]
186 if cl.hasnode(head)]
194 if cl.hasnode(head)]
187 if not nodes:
195 if not nodes:
188 droppednodes.extend(nodes)
196 droppednodes.extend(nodes)
189 del self[branch]
197 del self[branch]
190 if ((not self.validfor(repo)) or (self.tipnode in droppednodes)):
198 if ((not self.validfor(repo)) or (self.tipnode in droppednodes)):
191
199
192 # cache key are not valid anymore
200 # cache key are not valid anymore
193 self.tipnode = nullid
201 self.tipnode = nullid
194 self.tiprev = nullrev
202 self.tiprev = nullrev
195 for heads in self.values():
203 for heads in self.values():
196 tiprev = max(cl.rev(node) for node in heads)
204 tiprev = max(cl.rev(node) for node in heads)
197 if tiprev > self.tiprev:
205 if tiprev > self.tiprev:
198 self.tipnode = cl.node(tiprev)
206 self.tipnode = cl.node(tiprev)
199 self.tiprev = tiprev
207 self.tiprev = tiprev
200 self.filteredhash = self._hashfiltered(repo)
208 self.filteredhash = self._hashfiltered(repo)
General Comments 0
You need to be logged in to leave comments. Login now