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