comparison mercurial/branchmap.py @ 41565:bf7fb97aecf1

branchmap: make branchcache responsible for reading Encapsulate reading in a classmethod, to make it clear what kind of object is being handled. This is part of a stack of refactoring changes to help performance improvements down the line. Differential Revision: https://phab.mercurial-scm.org/D5635
author Martijn Pieters <mj@octobus.net>
date Mon, 21 Jan 2019 16:04:48 +0000
parents 3461814417f3
children eb7ce452e0fb
comparison
equal deleted inserted replaced
41564:a5493a251ad3 41565:bf7fb97aecf1
28 28
29 calcsize = struct.calcsize 29 calcsize = struct.calcsize
30 pack_into = struct.pack_into 30 pack_into = struct.pack_into
31 unpack_from = struct.unpack_from 31 unpack_from = struct.unpack_from
32 32
33 def _filename(repo):
34 """name of a branchcache file for a given repo or repoview"""
35 filename = "branch2"
36 if repo.filtername:
37 filename = '%s-%s' % (filename, repo.filtername)
38 return filename
39
40 def read(repo):
41 f = None
42 try:
43 f = repo.cachevfs(_filename(repo))
44 lineiter = iter(f)
45 cachekey = next(lineiter).rstrip('\n').split(" ", 2)
46 last, lrev = cachekey[:2]
47 last, lrev = bin(last), int(lrev)
48 filteredhash = None
49 if len(cachekey) > 2:
50 filteredhash = bin(cachekey[2])
51 bcache = branchcache(tipnode=last, tiprev=lrev,
52 filteredhash=filteredhash)
53 if not bcache.validfor(repo):
54 # invalidate the cache
55 raise ValueError(r'tip differs')
56 cl = repo.changelog
57 for l in lineiter:
58 l = l.rstrip('\n')
59 if not l:
60 continue
61 node, state, label = l.split(" ", 2)
62 if state not in 'oc':
63 raise ValueError(r'invalid branch state')
64 label = encoding.tolocal(label.strip())
65 node = bin(node)
66 if not cl.hasnode(node):
67 raise ValueError(
68 r'node %s does not exist' % pycompat.sysstr(hex(node)))
69 bcache.setdefault(label, []).append(node)
70 if state == 'c':
71 bcache._closednodes.add(node)
72
73 except (IOError, OSError):
74 return None
75
76 except Exception as inst:
77 if repo.ui.debugflag:
78 msg = 'invalid branchheads cache'
79 if repo.filtername is not None:
80 msg += ' (%s)' % repo.filtername
81 msg += ': %s\n'
82 repo.ui.debug(msg % pycompat.bytestr(inst))
83 bcache = None
84
85 finally:
86 if f:
87 f.close()
88
89 return bcache
90 33
91 ### Nearest subset relation 34 ### Nearest subset relation
92 # Nearest subset of filter X is a filter Y so that: 35 # Nearest subset of filter X is a filter Y so that:
93 # * Y is included in X, 36 # * Y is included in X,
94 # * X - Y is as small as possible. 37 # * X - Y is as small as possible.
105 filtername = repo.filtername 48 filtername = repo.filtername
106 bcache = repo._branchcaches.get(filtername) 49 bcache = repo._branchcaches.get(filtername)
107 50
108 revs = [] 51 revs = []
109 if bcache is None or not bcache.validfor(repo): 52 if bcache is None or not bcache.validfor(repo):
110 bcache = read(repo) 53 bcache = branchcache.fromfile(repo)
111 if bcache is None: 54 if bcache is None:
112 subsetname = subsettable.get(filtername) 55 subsetname = subsettable.get(filtername)
113 if subsetname is None: 56 if subsetname is None:
114 bcache = branchcache() 57 bcache = branchcache()
115 else: 58 else:
179 122
180 The open/closed state is represented by a single letter 'o' or 'c'. 123 The open/closed state is represented by a single letter 'o' or 'c'.
181 This field can be used to avoid changelog reads when determining if a 124 This field can be used to avoid changelog reads when determining if a
182 branch head closes a branch or not. 125 branch head closes a branch or not.
183 """ 126 """
127 @classmethod
128 def fromfile(cls, repo):
129 f = None
130 try:
131 f = repo.cachevfs(cls._filename(repo))
132 lineiter = iter(f)
133 cachekey = next(lineiter).rstrip('\n').split(" ", 2)
134 last, lrev = cachekey[:2]
135 last, lrev = bin(last), int(lrev)
136 filteredhash = None
137 if len(cachekey) > 2:
138 filteredhash = bin(cachekey[2])
139 bcache = cls(tipnode=last, tiprev=lrev, filteredhash=filteredhash)
140 if not bcache.validfor(repo):
141 # invalidate the cache
142 raise ValueError(r'tip differs')
143 cl = repo.changelog
144 for line in lineiter:
145 line = line.rstrip('\n')
146 if not line:
147 continue
148 node, state, label = line.split(" ", 2)
149 if state not in 'oc':
150 raise ValueError(r'invalid branch state')
151 label = encoding.tolocal(label.strip())
152 node = bin(node)
153 if not cl.hasnode(node):
154 raise ValueError(
155 r'node %s does not exist' % pycompat.sysstr(hex(node)))
156 bcache.setdefault(label, []).append(node)
157 if state == 'c':
158 bcache._closednodes.add(node)
159
160 except (IOError, OSError):
161 return None
162
163 except Exception as inst:
164 if repo.ui.debugflag:
165 msg = 'invalid branchheads cache'
166 if repo.filtername is not None:
167 msg += ' (%s)' % repo.filtername
168 msg += ': %s\n'
169 repo.ui.debug(msg % pycompat.bytestr(inst))
170 bcache = None
171
172 finally:
173 if f:
174 f.close()
175
176 return bcache
177
178 @staticmethod
179 def _filename(repo):
180 """name of a branchcache file for a given repo or repoview"""
181 filename = "branch2"
182 if repo.filtername:
183 filename = '%s-%s' % (filename, repo.filtername)
184 return filename
184 185
185 def __init__(self, entries=(), tipnode=nullid, tiprev=nullrev, 186 def __init__(self, entries=(), tipnode=nullid, tiprev=nullrev,
186 filteredhash=None, closednodes=None): 187 filteredhash=None, closednodes=None):
187 super(branchcache, self).__init__(entries) 188 super(branchcache, self).__init__(entries)
188 self.tipnode = tipnode 189 self.tipnode = tipnode
244 return branchcache(self, self.tipnode, self.tiprev, self.filteredhash, 245 return branchcache(self, self.tipnode, self.tiprev, self.filteredhash,
245 self._closednodes) 246 self._closednodes)
246 247
247 def write(self, repo): 248 def write(self, repo):
248 try: 249 try:
249 f = repo.cachevfs(_filename(repo), "w", atomictemp=True) 250 f = repo.cachevfs(self._filename(repo), "w", atomictemp=True)
250 cachekey = [hex(self.tipnode), '%d' % self.tiprev] 251 cachekey = [hex(self.tipnode), '%d' % self.tiprev]
251 if self.filteredhash is not None: 252 if self.filteredhash is not None:
252 cachekey.append(hex(self.filteredhash)) 253 cachekey.append(hex(self.filteredhash))
253 f.write(" ".join(cachekey) + '\n') 254 f.write(" ".join(cachekey) + '\n')
254 nodecount = 0 255 nodecount = 0