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