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