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 |