##// END OF EJS Templates
branchmap: fix blank line position...
Pierre-Yves David -
r19837:c38eaeb3 default
parent child Browse files
Show More
@@ -1,223 +1,223 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, repoview
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 revs = []
67 67 if partial is None or not partial.validfor(repo):
68 68 partial = read(repo)
69 69 if partial is None:
70 70 subsetname = repoview.subsettable.get(filtername)
71 71 if subsetname is None:
72 72 partial = branchcache()
73 73 else:
74 74 subset = repo.filtered(subsetname)
75 75 partial = subset.branchmap().copy()
76 76 extrarevs = subset.changelog.filteredrevs - cl.filteredrevs
77 77 revs.extend(r for r in extrarevs if r <= partial.tiprev)
78 78 revs.extend(cl.revs(start=partial.tiprev + 1))
79 79 if revs:
80 80 partial.update(repo, revs)
81 81 partial.write(repo)
82 82 assert partial.validfor(repo), filtername
83 83 repo._branchcaches[repo.filtername] = partial
84 84
85 85 class branchcache(dict):
86 86 """A dict like object that hold branches heads cache"""
87 87
88 88 def __init__(self, entries=(), tipnode=nullid, tiprev=nullrev,
89 89 filteredhash=None):
90 90 super(branchcache, self).__init__(entries)
91 91 self.tipnode = tipnode
92 92 self.tiprev = tiprev
93 93 self.filteredhash = filteredhash
94 94
95 95 def _hashfiltered(self, repo):
96 96 """build hash of revision filtered in the current cache
97 97
98 98 Tracking tipnode and tiprev is not enough to ensure validity of the
99 99 cache as they do not help to distinct cache that ignored various
100 100 revision bellow tiprev.
101 101
102 102 To detect such difference, we build a cache of all ignored revisions.
103 103 """
104 104 cl = repo.changelog
105 105 if not cl.filteredrevs:
106 106 return None
107 107 key = None
108 108 revs = sorted(r for r in cl.filteredrevs if r <= self.tiprev)
109 109 if revs:
110 110 s = util.sha1()
111 111 for rev in revs:
112 112 s.update('%s;' % rev)
113 113 key = s.digest()
114 114 return key
115 115
116 116 def validfor(self, repo):
117 117 """Is the cache content valid regarding a repo
118 118
119 119 - False when cached tipnode is unknown or if we detect a strip.
120 120 - True when cache is up to date or a subset of current repo."""
121 121 try:
122 122 return ((self.tipnode == repo.changelog.node(self.tiprev))
123 123 and (self.filteredhash == self._hashfiltered(repo)))
124 124 except IndexError:
125 125 return False
126 126
127 127 def copy(self):
128 128 """return an deep copy of the branchcache object"""
129 129 return branchcache(self, self.tipnode, self.tiprev, self.filteredhash)
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 sorted(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 142 except (IOError, OSError, util.Abort):
143 143 # Abort may be raise by read only opener
144 144 pass
145 145
146 146 def update(self, repo, revgen):
147 147 """Given a branchhead cache, self, that may have extra nodes or be
148 148 missing heads, and a generator of nodes that are at least a superset of
149 149 heads missing, this function updates self to be correct.
150 150 """
151 151 cl = repo.changelog
152 152 # collect new branch entries
153 153 newbranches = {}
154 154 getbranch = cl.branch
155 155 for r in revgen:
156 156 newbranches.setdefault(getbranch(r), []).append(cl.node(r))
157 157 # if older branchheads are reachable from new ones, they aren't
158 158 # really branchheads. Note checking parents is insufficient:
159 159 # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
160 160 for branch, newnodes in newbranches.iteritems():
161 161 bheads = self.setdefault(branch, [])
162 162 # Remove candidate heads that no longer are in the repo (e.g., as
163 163 # the result of a strip that just happened). Avoid using 'node in
164 164 # self' here because that dives down into branchcache code somewhat
165 165 # recursively.
166 166 bheadrevs = [cl.rev(node) for node in bheads
167 167 if cl.hasnode(node)]
168 168 newheadrevs = [cl.rev(node) for node in newnodes
169 169 if cl.hasnode(node)]
170 170 ctxisnew = bheadrevs and min(newheadrevs) > max(bheadrevs)
171 171 # Remove duplicates - nodes that are in newheadrevs and are already
172 172 # in bheadrevs. This can happen if you strip a node whose parent
173 173 # was already a head (because they're on different branches).
174 174 bheadrevs = sorted(set(bheadrevs).union(newheadrevs))
175 175
176 176 # Starting from tip means fewer passes over reachable. If we know
177 177 # the new candidates are not ancestors of existing heads, we don't
178 178 # have to examine ancestors of existing heads
179 179 if ctxisnew:
180 180 iterrevs = sorted(newheadrevs)
181 181 else:
182 182 iterrevs = list(bheadrevs)
183 183
184 184 # This loop prunes out two kinds of heads - heads that are
185 185 # superseded by a head in newheadrevs, and newheadrevs that are not
186 186 # heads because an existing head is their descendant.
187 187 while iterrevs:
188 188 latest = iterrevs.pop()
189 189 if latest not in bheadrevs:
190 190 continue
191 191 ancestors = set(cl.ancestors([latest],
192 192 bheadrevs[0]))
193 193 if ancestors:
194 194 bheadrevs = [b for b in bheadrevs if b not in ancestors]
195 195 self[branch] = [cl.node(rev) for rev in bheadrevs]
196 196 tiprev = max(bheadrevs)
197 197 if tiprev > self.tiprev:
198 198 self.tipnode = cl.node(tiprev)
199 199 self.tiprev = tiprev
200 200
201 201 # There may be branches that cease to exist when the last commit in the
202 202 # branch was stripped. This code filters them out. Note that the
203 203 # branch that ceased to exist may not be in newbranches because
204 204 # newbranches is the set of candidate heads, which when you strip the
205 205 # last commit in a branch will be the parent branch.
206 206 droppednodes = []
207 207 for branch in self.keys():
208 208 nodes = [head for head in self[branch]
209 209 if cl.hasnode(head)]
210 210 if not nodes:
211 211 droppednodes.extend(nodes)
212 212 del self[branch]
213
213 214 if ((not self.validfor(repo)) or (self.tipnode in droppednodes)):
214
215 215 # cache key are not valid anymore
216 216 self.tipnode = nullid
217 217 self.tiprev = nullrev
218 218 for heads in self.values():
219 219 tiprev = max(cl.rev(node) for node in heads)
220 220 if tiprev > self.tiprev:
221 221 self.tipnode = cl.node(tiprev)
222 222 self.tiprev = tiprev
223 223 self.filteredhash = self._hashfiltered(repo)
General Comments 0
You need to be logged in to leave comments. Login now