##// END OF EJS Templates
interfaces: convert `repository.imanifeststorage` from zope `Attribute` attrs...
Matt Harbison -
r53375:d01af74e default
parent child Browse files
Show More
@@ -1,2180 +1,2182
1 # repository.py - Interfaces and base classes for repositories and peers.
1 # repository.py - Interfaces and base classes for repositories and peers.
2 # coding: utf-8
2 # coding: utf-8
3 #
3 #
4 # Copyright 2017 Gregory Szorc <gregory.szorc@gmail.com>
4 # Copyright 2017 Gregory Szorc <gregory.szorc@gmail.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from __future__ import annotations
9 from __future__ import annotations
10
10
11 import abc
11 import abc
12 import typing
12 import typing
13
13
14 from typing import (
14 from typing import (
15 Any,
15 Any,
16 Collection,
16 Collection,
17 Protocol,
17 Protocol,
18 )
18 )
19
19
20 from ..i18n import _
20 from ..i18n import _
21 from .. import error
21 from .. import error
22 from . import util as interfaceutil
22 from . import util as interfaceutil
23
23
24 if typing.TYPE_CHECKING:
24 if typing.TYPE_CHECKING:
25 # Almost all mercurial modules are only imported in the type checking phase
25 # Almost all mercurial modules are only imported in the type checking phase
26 # to avoid circular imports
26 # to avoid circular imports
27 from ..utils import (
27 from ..utils import (
28 urlutil,
28 urlutil,
29 )
29 )
30
30
31 # TODO: make a protocol class for this
32 NodeConstants = Any
33
31 # TODO: create a Protocol class, since importing uimod here causes a cycle
34 # TODO: create a Protocol class, since importing uimod here causes a cycle
32 # that confuses pytype.
35 # that confuses pytype.
33 Ui = Any
36 Ui = Any
34
37
38 # TODO: make a protocol class for this
39 Vfs = Any
40
35 # Local repository feature string.
41 # Local repository feature string.
36
42
37 # Revlogs are being used for file storage.
43 # Revlogs are being used for file storage.
38 REPO_FEATURE_REVLOG_FILE_STORAGE = b'revlogfilestorage'
44 REPO_FEATURE_REVLOG_FILE_STORAGE = b'revlogfilestorage'
39 # The storage part of the repository is shared from an external source.
45 # The storage part of the repository is shared from an external source.
40 REPO_FEATURE_SHARED_STORAGE = b'sharedstore'
46 REPO_FEATURE_SHARED_STORAGE = b'sharedstore'
41 # LFS supported for backing file storage.
47 # LFS supported for backing file storage.
42 REPO_FEATURE_LFS = b'lfs'
48 REPO_FEATURE_LFS = b'lfs'
43 # Repository supports being stream cloned.
49 # Repository supports being stream cloned.
44 REPO_FEATURE_STREAM_CLONE = b'streamclone'
50 REPO_FEATURE_STREAM_CLONE = b'streamclone'
45 # Repository supports (at least) some sidedata to be stored
51 # Repository supports (at least) some sidedata to be stored
46 REPO_FEATURE_SIDE_DATA = b'side-data'
52 REPO_FEATURE_SIDE_DATA = b'side-data'
47 # Files storage may lack data for all ancestors.
53 # Files storage may lack data for all ancestors.
48 REPO_FEATURE_SHALLOW_FILE_STORAGE = b'shallowfilestorage'
54 REPO_FEATURE_SHALLOW_FILE_STORAGE = b'shallowfilestorage'
49
55
50 REVISION_FLAG_CENSORED = 1 << 15
56 REVISION_FLAG_CENSORED = 1 << 15
51 REVISION_FLAG_ELLIPSIS = 1 << 14
57 REVISION_FLAG_ELLIPSIS = 1 << 14
52 REVISION_FLAG_EXTSTORED = 1 << 13
58 REVISION_FLAG_EXTSTORED = 1 << 13
53 REVISION_FLAG_HASCOPIESINFO = 1 << 12
59 REVISION_FLAG_HASCOPIESINFO = 1 << 12
54
60
55 REVISION_FLAGS_KNOWN = (
61 REVISION_FLAGS_KNOWN = (
56 REVISION_FLAG_CENSORED
62 REVISION_FLAG_CENSORED
57 | REVISION_FLAG_ELLIPSIS
63 | REVISION_FLAG_ELLIPSIS
58 | REVISION_FLAG_EXTSTORED
64 | REVISION_FLAG_EXTSTORED
59 | REVISION_FLAG_HASCOPIESINFO
65 | REVISION_FLAG_HASCOPIESINFO
60 )
66 )
61
67
62 CG_DELTAMODE_STD = b'default'
68 CG_DELTAMODE_STD = b'default'
63 CG_DELTAMODE_PREV = b'previous'
69 CG_DELTAMODE_PREV = b'previous'
64 CG_DELTAMODE_FULL = b'fulltext'
70 CG_DELTAMODE_FULL = b'fulltext'
65 CG_DELTAMODE_P1 = b'p1'
71 CG_DELTAMODE_P1 = b'p1'
66
72
67
73
68 ## Cache related constants:
74 ## Cache related constants:
69 #
75 #
70 # Used to control which cache should be warmed in a repo.updatecaches(…) call.
76 # Used to control which cache should be warmed in a repo.updatecaches(…) call.
71
77
72 # Warm branchmaps of all known repoview's filter-level
78 # Warm branchmaps of all known repoview's filter-level
73 CACHE_BRANCHMAP_ALL = b"branchmap-all"
79 CACHE_BRANCHMAP_ALL = b"branchmap-all"
74 # Warm branchmaps of repoview's filter-level used by server
80 # Warm branchmaps of repoview's filter-level used by server
75 CACHE_BRANCHMAP_SERVED = b"branchmap-served"
81 CACHE_BRANCHMAP_SERVED = b"branchmap-served"
76 # Warm internal changelog cache (eg: persistent nodemap)
82 # Warm internal changelog cache (eg: persistent nodemap)
77 CACHE_CHANGELOG_CACHE = b"changelog-cache"
83 CACHE_CHANGELOG_CACHE = b"changelog-cache"
78 # check of a branchmap can use the "pure topo" mode
84 # check of a branchmap can use the "pure topo" mode
79 CACHE_BRANCHMAP_DETECT_PURE_TOPO = b"branchmap-detect-pure-topo"
85 CACHE_BRANCHMAP_DETECT_PURE_TOPO = b"branchmap-detect-pure-topo"
80 # Warm full manifest cache
86 # Warm full manifest cache
81 CACHE_FULL_MANIFEST = b"full-manifest"
87 CACHE_FULL_MANIFEST = b"full-manifest"
82 # Warm file-node-tags cache
88 # Warm file-node-tags cache
83 CACHE_FILE_NODE_TAGS = b"file-node-tags"
89 CACHE_FILE_NODE_TAGS = b"file-node-tags"
84 # Warm internal manifestlog cache (eg: persistent nodemap)
90 # Warm internal manifestlog cache (eg: persistent nodemap)
85 CACHE_MANIFESTLOG_CACHE = b"manifestlog-cache"
91 CACHE_MANIFESTLOG_CACHE = b"manifestlog-cache"
86 # Warn rev branch cache
92 # Warn rev branch cache
87 CACHE_REV_BRANCH = b"rev-branch-cache"
93 CACHE_REV_BRANCH = b"rev-branch-cache"
88 # Warm tags' cache for default repoview'
94 # Warm tags' cache for default repoview'
89 CACHE_TAGS_DEFAULT = b"tags-default"
95 CACHE_TAGS_DEFAULT = b"tags-default"
90 # Warm tags' cache for repoview's filter-level used by server
96 # Warm tags' cache for repoview's filter-level used by server
91 CACHE_TAGS_SERVED = b"tags-served"
97 CACHE_TAGS_SERVED = b"tags-served"
92
98
93 # the cache to warm by default after a simple transaction
99 # the cache to warm by default after a simple transaction
94 # (this is a mutable set to let extension update it)
100 # (this is a mutable set to let extension update it)
95 CACHES_DEFAULT = {
101 CACHES_DEFAULT = {
96 CACHE_BRANCHMAP_SERVED,
102 CACHE_BRANCHMAP_SERVED,
97 }
103 }
98
104
99 # the caches to warm when warming all of them
105 # the caches to warm when warming all of them
100 # (this is a mutable set to let extension update it)
106 # (this is a mutable set to let extension update it)
101 CACHES_ALL = {
107 CACHES_ALL = {
102 CACHE_BRANCHMAP_SERVED,
108 CACHE_BRANCHMAP_SERVED,
103 CACHE_BRANCHMAP_ALL,
109 CACHE_BRANCHMAP_ALL,
104 CACHE_BRANCHMAP_DETECT_PURE_TOPO,
110 CACHE_BRANCHMAP_DETECT_PURE_TOPO,
105 CACHE_REV_BRANCH,
111 CACHE_REV_BRANCH,
106 CACHE_CHANGELOG_CACHE,
112 CACHE_CHANGELOG_CACHE,
107 CACHE_FILE_NODE_TAGS,
113 CACHE_FILE_NODE_TAGS,
108 CACHE_FULL_MANIFEST,
114 CACHE_FULL_MANIFEST,
109 CACHE_MANIFESTLOG_CACHE,
115 CACHE_MANIFESTLOG_CACHE,
110 CACHE_TAGS_DEFAULT,
116 CACHE_TAGS_DEFAULT,
111 CACHE_TAGS_SERVED,
117 CACHE_TAGS_SERVED,
112 }
118 }
113
119
114 # the cache to warm by default on simple call
120 # the cache to warm by default on simple call
115 # (this is a mutable set to let extension update it)
121 # (this is a mutable set to let extension update it)
116 CACHES_POST_CLONE = CACHES_ALL.copy()
122 CACHES_POST_CLONE = CACHES_ALL.copy()
117 CACHES_POST_CLONE.discard(CACHE_FILE_NODE_TAGS)
123 CACHES_POST_CLONE.discard(CACHE_FILE_NODE_TAGS)
118 CACHES_POST_CLONE.discard(CACHE_REV_BRANCH)
124 CACHES_POST_CLONE.discard(CACHE_REV_BRANCH)
119
125
120
126
121 class _ipeerconnection(Protocol):
127 class _ipeerconnection(Protocol):
122 """Represents a "connection" to a repository.
128 """Represents a "connection" to a repository.
123
129
124 This is the base interface for representing a connection to a repository.
130 This is the base interface for representing a connection to a repository.
125 It holds basic properties and methods applicable to all peer types.
131 It holds basic properties and methods applicable to all peer types.
126
132
127 This is not a complete interface definition and should not be used
133 This is not a complete interface definition and should not be used
128 outside of this module.
134 outside of this module.
129 """
135 """
130
136
131 ui: Ui
137 ui: Ui
132 """ui.ui instance"""
138 """ui.ui instance"""
133
139
134 path: urlutil.path | None
140 path: urlutil.path | None
135 """a urlutil.path instance or None"""
141 """a urlutil.path instance or None"""
136
142
137 def url(self):
143 def url(self):
138 """Returns a URL string representing this peer.
144 """Returns a URL string representing this peer.
139
145
140 Currently, implementations expose the raw URL used to construct the
146 Currently, implementations expose the raw URL used to construct the
141 instance. It may contain credentials as part of the URL. The
147 instance. It may contain credentials as part of the URL. The
142 expectations of the value aren't well-defined and this could lead to
148 expectations of the value aren't well-defined and this could lead to
143 data leakage.
149 data leakage.
144
150
145 TODO audit/clean consumers and more clearly define the contents of this
151 TODO audit/clean consumers and more clearly define the contents of this
146 value.
152 value.
147 """
153 """
148
154
149 def local(self):
155 def local(self):
150 """Returns a local repository instance.
156 """Returns a local repository instance.
151
157
152 If the peer represents a local repository, returns an object that
158 If the peer represents a local repository, returns an object that
153 can be used to interface with it. Otherwise returns ``None``.
159 can be used to interface with it. Otherwise returns ``None``.
154 """
160 """
155
161
156 def canpush(self):
162 def canpush(self):
157 """Returns a boolean indicating if this peer can be pushed to."""
163 """Returns a boolean indicating if this peer can be pushed to."""
158
164
159 def close(self):
165 def close(self):
160 """Close the connection to this peer.
166 """Close the connection to this peer.
161
167
162 This is called when the peer will no longer be used. Resources
168 This is called when the peer will no longer be used. Resources
163 associated with the peer should be cleaned up.
169 associated with the peer should be cleaned up.
164 """
170 """
165
171
166
172
167 class ipeercapabilities(Protocol):
173 class ipeercapabilities(Protocol):
168 """Peer sub-interface related to capabilities."""
174 """Peer sub-interface related to capabilities."""
169
175
170 def capable(self, name):
176 def capable(self, name):
171 """Determine support for a named capability.
177 """Determine support for a named capability.
172
178
173 Returns ``False`` if capability not supported.
179 Returns ``False`` if capability not supported.
174
180
175 Returns ``True`` if boolean capability is supported. Returns a string
181 Returns ``True`` if boolean capability is supported. Returns a string
176 if capability support is non-boolean.
182 if capability support is non-boolean.
177
183
178 Capability strings may or may not map to wire protocol capabilities.
184 Capability strings may or may not map to wire protocol capabilities.
179 """
185 """
180
186
181 def requirecap(self, name, purpose):
187 def requirecap(self, name, purpose):
182 """Require a capability to be present.
188 """Require a capability to be present.
183
189
184 Raises a ``CapabilityError`` if the capability isn't present.
190 Raises a ``CapabilityError`` if the capability isn't present.
185 """
191 """
186
192
187
193
188 class ipeercommands(Protocol):
194 class ipeercommands(Protocol):
189 """Client-side interface for communicating over the wire protocol.
195 """Client-side interface for communicating over the wire protocol.
190
196
191 This interface is used as a gateway to the Mercurial wire protocol.
197 This interface is used as a gateway to the Mercurial wire protocol.
192 methods commonly call wire protocol commands of the same name.
198 methods commonly call wire protocol commands of the same name.
193 """
199 """
194
200
195 def branchmap(self):
201 def branchmap(self):
196 """Obtain heads in named branches.
202 """Obtain heads in named branches.
197
203
198 Returns a dict mapping branch name to an iterable of nodes that are
204 Returns a dict mapping branch name to an iterable of nodes that are
199 heads on that branch.
205 heads on that branch.
200 """
206 """
201
207
202 def capabilities(self):
208 def capabilities(self):
203 """Obtain capabilities of the peer.
209 """Obtain capabilities of the peer.
204
210
205 Returns a set of string capabilities.
211 Returns a set of string capabilities.
206 """
212 """
207
213
208 def get_cached_bundle_inline(self, path):
214 def get_cached_bundle_inline(self, path):
209 """Retrieve a clonebundle across the wire.
215 """Retrieve a clonebundle across the wire.
210
216
211 Returns a chunkbuffer
217 Returns a chunkbuffer
212 """
218 """
213
219
214 def clonebundles(self):
220 def clonebundles(self):
215 """Obtains the clone bundles manifest for the repo.
221 """Obtains the clone bundles manifest for the repo.
216
222
217 Returns the manifest as unparsed bytes.
223 Returns the manifest as unparsed bytes.
218 """
224 """
219
225
220 def debugwireargs(self, one, two, three=None, four=None, five=None):
226 def debugwireargs(self, one, two, three=None, four=None, five=None):
221 """Used to facilitate debugging of arguments passed over the wire."""
227 """Used to facilitate debugging of arguments passed over the wire."""
222
228
223 def getbundle(self, source, **kwargs):
229 def getbundle(self, source, **kwargs):
224 """Obtain remote repository data as a bundle.
230 """Obtain remote repository data as a bundle.
225
231
226 This command is how the bulk of repository data is transferred from
232 This command is how the bulk of repository data is transferred from
227 the peer to the local repository
233 the peer to the local repository
228
234
229 Returns a generator of bundle data.
235 Returns a generator of bundle data.
230 """
236 """
231
237
232 def heads(self):
238 def heads(self):
233 """Determine all known head revisions in the peer.
239 """Determine all known head revisions in the peer.
234
240
235 Returns an iterable of binary nodes.
241 Returns an iterable of binary nodes.
236 """
242 """
237
243
238 def known(self, nodes):
244 def known(self, nodes):
239 """Determine whether multiple nodes are known.
245 """Determine whether multiple nodes are known.
240
246
241 Accepts an iterable of nodes whose presence to check for.
247 Accepts an iterable of nodes whose presence to check for.
242
248
243 Returns an iterable of booleans indicating of the corresponding node
249 Returns an iterable of booleans indicating of the corresponding node
244 at that index is known to the peer.
250 at that index is known to the peer.
245 """
251 """
246
252
247 def listkeys(self, namespace):
253 def listkeys(self, namespace):
248 """Obtain all keys in a pushkey namespace.
254 """Obtain all keys in a pushkey namespace.
249
255
250 Returns an iterable of key names.
256 Returns an iterable of key names.
251 """
257 """
252
258
253 def lookup(self, key):
259 def lookup(self, key):
254 """Resolve a value to a known revision.
260 """Resolve a value to a known revision.
255
261
256 Returns a binary node of the resolved revision on success.
262 Returns a binary node of the resolved revision on success.
257 """
263 """
258
264
259 def pushkey(self, namespace, key, old, new):
265 def pushkey(self, namespace, key, old, new):
260 """Set a value using the ``pushkey`` protocol.
266 """Set a value using the ``pushkey`` protocol.
261
267
262 Arguments correspond to the pushkey namespace and key to operate on and
268 Arguments correspond to the pushkey namespace and key to operate on and
263 the old and new values for that key.
269 the old and new values for that key.
264
270
265 Returns a string with the peer result. The value inside varies by the
271 Returns a string with the peer result. The value inside varies by the
266 namespace.
272 namespace.
267 """
273 """
268
274
269 def stream_out(self):
275 def stream_out(self):
270 """Obtain streaming clone data.
276 """Obtain streaming clone data.
271
277
272 Successful result should be a generator of data chunks.
278 Successful result should be a generator of data chunks.
273 """
279 """
274
280
275 def unbundle(self, bundle, heads, url):
281 def unbundle(self, bundle, heads, url):
276 """Transfer repository data to the peer.
282 """Transfer repository data to the peer.
277
283
278 This is how the bulk of data during a push is transferred.
284 This is how the bulk of data during a push is transferred.
279
285
280 Returns the integer number of heads added to the peer.
286 Returns the integer number of heads added to the peer.
281 """
287 """
282
288
283
289
284 class ipeerlegacycommands(Protocol):
290 class ipeerlegacycommands(Protocol):
285 """Interface for implementing support for legacy wire protocol commands.
291 """Interface for implementing support for legacy wire protocol commands.
286
292
287 Wire protocol commands transition to legacy status when they are no longer
293 Wire protocol commands transition to legacy status when they are no longer
288 used by modern clients. To facilitate identifying which commands are
294 used by modern clients. To facilitate identifying which commands are
289 legacy, the interfaces are split.
295 legacy, the interfaces are split.
290 """
296 """
291
297
292 def between(self, pairs):
298 def between(self, pairs):
293 """Obtain nodes between pairs of nodes.
299 """Obtain nodes between pairs of nodes.
294
300
295 ``pairs`` is an iterable of node pairs.
301 ``pairs`` is an iterable of node pairs.
296
302
297 Returns an iterable of iterables of nodes corresponding to each
303 Returns an iterable of iterables of nodes corresponding to each
298 requested pair.
304 requested pair.
299 """
305 """
300
306
301 def branches(self, nodes):
307 def branches(self, nodes):
302 """Obtain ancestor changesets of specific nodes back to a branch point.
308 """Obtain ancestor changesets of specific nodes back to a branch point.
303
309
304 For each requested node, the peer finds the first ancestor node that is
310 For each requested node, the peer finds the first ancestor node that is
305 a DAG root or is a merge.
311 a DAG root or is a merge.
306
312
307 Returns an iterable of iterables with the resolved values for each node.
313 Returns an iterable of iterables with the resolved values for each node.
308 """
314 """
309
315
310 def changegroup(self, nodes, source):
316 def changegroup(self, nodes, source):
311 """Obtain a changegroup with data for descendants of specified nodes."""
317 """Obtain a changegroup with data for descendants of specified nodes."""
312
318
313 def changegroupsubset(self, bases, heads, source):
319 def changegroupsubset(self, bases, heads, source):
314 pass
320 pass
315
321
316
322
317 class ipeercommandexecutor(Protocol):
323 class ipeercommandexecutor(Protocol):
318 """Represents a mechanism to execute remote commands.
324 """Represents a mechanism to execute remote commands.
319
325
320 This is the primary interface for requesting that wire protocol commands
326 This is the primary interface for requesting that wire protocol commands
321 be executed. Instances of this interface are active in a context manager
327 be executed. Instances of this interface are active in a context manager
322 and have a well-defined lifetime. When the context manager exits, all
328 and have a well-defined lifetime. When the context manager exits, all
323 outstanding requests are waited on.
329 outstanding requests are waited on.
324 """
330 """
325
331
326 def callcommand(self, name, args):
332 def callcommand(self, name, args):
327 """Request that a named command be executed.
333 """Request that a named command be executed.
328
334
329 Receives the command name and a dictionary of command arguments.
335 Receives the command name and a dictionary of command arguments.
330
336
331 Returns a ``concurrent.futures.Future`` that will resolve to the
337 Returns a ``concurrent.futures.Future`` that will resolve to the
332 result of that command request. That exact value is left up to
338 result of that command request. That exact value is left up to
333 the implementation and possibly varies by command.
339 the implementation and possibly varies by command.
334
340
335 Not all commands can coexist with other commands in an executor
341 Not all commands can coexist with other commands in an executor
336 instance: it depends on the underlying wire protocol transport being
342 instance: it depends on the underlying wire protocol transport being
337 used and the command itself.
343 used and the command itself.
338
344
339 Implementations MAY call ``sendcommands()`` automatically if the
345 Implementations MAY call ``sendcommands()`` automatically if the
340 requested command can not coexist with other commands in this executor.
346 requested command can not coexist with other commands in this executor.
341
347
342 Implementations MAY call ``sendcommands()`` automatically when the
348 Implementations MAY call ``sendcommands()`` automatically when the
343 future's ``result()`` is called. So, consumers using multiple
349 future's ``result()`` is called. So, consumers using multiple
344 commands with an executor MUST ensure that ``result()`` is not called
350 commands with an executor MUST ensure that ``result()`` is not called
345 until all command requests have been issued.
351 until all command requests have been issued.
346 """
352 """
347
353
348 def sendcommands(self):
354 def sendcommands(self):
349 """Trigger submission of queued command requests.
355 """Trigger submission of queued command requests.
350
356
351 Not all transports submit commands as soon as they are requested to
357 Not all transports submit commands as soon as they are requested to
352 run. When called, this method forces queued command requests to be
358 run. When called, this method forces queued command requests to be
353 issued. It will no-op if all commands have already been sent.
359 issued. It will no-op if all commands have already been sent.
354
360
355 When called, no more new commands may be issued with this executor.
361 When called, no more new commands may be issued with this executor.
356 """
362 """
357
363
358 def close(self):
364 def close(self):
359 """Signal that this command request is finished.
365 """Signal that this command request is finished.
360
366
361 When called, no more new commands may be issued. All outstanding
367 When called, no more new commands may be issued. All outstanding
362 commands that have previously been issued are waited on before
368 commands that have previously been issued are waited on before
363 returning. This not only includes waiting for the futures to resolve,
369 returning. This not only includes waiting for the futures to resolve,
364 but also waiting for all response data to arrive. In other words,
370 but also waiting for all response data to arrive. In other words,
365 calling this waits for all on-wire state for issued command requests
371 calling this waits for all on-wire state for issued command requests
366 to finish.
372 to finish.
367
373
368 When used as a context manager, this method is called when exiting the
374 When used as a context manager, this method is called when exiting the
369 context manager.
375 context manager.
370
376
371 This method may call ``sendcommands()`` if there are buffered commands.
377 This method may call ``sendcommands()`` if there are buffered commands.
372 """
378 """
373
379
374
380
375 class ipeerrequests(Protocol):
381 class ipeerrequests(Protocol):
376 """Interface for executing commands on a peer."""
382 """Interface for executing commands on a peer."""
377
383
378 limitedarguments: bool
384 limitedarguments: bool
379 """True if the peer cannot receive large argument value for commands."""
385 """True if the peer cannot receive large argument value for commands."""
380
386
381 def commandexecutor(self):
387 def commandexecutor(self):
382 """A context manager that resolves to an ipeercommandexecutor.
388 """A context manager that resolves to an ipeercommandexecutor.
383
389
384 The object this resolves to can be used to issue command requests
390 The object this resolves to can be used to issue command requests
385 to the peer.
391 to the peer.
386
392
387 Callers should call its ``callcommand`` method to issue command
393 Callers should call its ``callcommand`` method to issue command
388 requests.
394 requests.
389
395
390 A new executor should be obtained for each distinct set of commands
396 A new executor should be obtained for each distinct set of commands
391 (possibly just a single command) that the consumer wants to execute
397 (possibly just a single command) that the consumer wants to execute
392 as part of a single operation or round trip. This is because some
398 as part of a single operation or round trip. This is because some
393 peers are half-duplex and/or don't support persistent connections.
399 peers are half-duplex and/or don't support persistent connections.
394 e.g. in the case of HTTP peers, commands sent to an executor represent
400 e.g. in the case of HTTP peers, commands sent to an executor represent
395 a single HTTP request. While some peers may support multiple command
401 a single HTTP request. While some peers may support multiple command
396 sends over the wire per executor, consumers need to code to the least
402 sends over the wire per executor, consumers need to code to the least
397 capable peer. So it should be assumed that command executors buffer
403 capable peer. So it should be assumed that command executors buffer
398 called commands until they are told to send them and that each
404 called commands until they are told to send them and that each
399 command executor could result in a new connection or wire-level request
405 command executor could result in a new connection or wire-level request
400 being issued.
406 being issued.
401 """
407 """
402
408
403
409
404 class peer(_ipeerconnection, ipeercapabilities, ipeerrequests, Protocol):
410 class peer(_ipeerconnection, ipeercapabilities, ipeerrequests, Protocol):
405 """Unified interface for peer repositories.
411 """Unified interface for peer repositories.
406
412
407 All peer instances must conform to this interface.
413 All peer instances must conform to this interface.
408 """
414 """
409
415
410 limitedarguments: bool = False
416 limitedarguments: bool = False
411
417
412 def __init__(self, ui, path=None, remotehidden=False):
418 def __init__(self, ui, path=None, remotehidden=False):
413 self.ui = ui
419 self.ui = ui
414 self.path = path
420 self.path = path
415
421
416 def capable(self, name):
422 def capable(self, name):
417 # TODO: this class should maybe subclass ipeercommands too, otherwise it
423 # TODO: this class should maybe subclass ipeercommands too, otherwise it
418 # is assuming whatever uses this as a mixin also has this interface.
424 # is assuming whatever uses this as a mixin also has this interface.
419 caps = self.capabilities() # pytype: disable=attribute-error
425 caps = self.capabilities() # pytype: disable=attribute-error
420 if name in caps:
426 if name in caps:
421 return True
427 return True
422
428
423 name = b'%s=' % name
429 name = b'%s=' % name
424 for cap in caps:
430 for cap in caps:
425 if cap.startswith(name):
431 if cap.startswith(name):
426 return cap[len(name) :]
432 return cap[len(name) :]
427
433
428 return False
434 return False
429
435
430 def requirecap(self, name, purpose):
436 def requirecap(self, name, purpose):
431 if self.capable(name):
437 if self.capable(name):
432 return
438 return
433
439
434 raise error.CapabilityError(
440 raise error.CapabilityError(
435 _(
441 _(
436 b'cannot %s; remote repository does not support the '
442 b'cannot %s; remote repository does not support the '
437 b'\'%s\' capability'
443 b'\'%s\' capability'
438 )
444 )
439 % (purpose, name)
445 % (purpose, name)
440 )
446 )
441
447
442
448
443 class iverifyproblem(Protocol):
449 class iverifyproblem(Protocol):
444 """Represents a problem with the integrity of the repository.
450 """Represents a problem with the integrity of the repository.
445
451
446 Instances of this interface are emitted to describe an integrity issue
452 Instances of this interface are emitted to describe an integrity issue
447 with a repository (e.g. corrupt storage, missing data, etc).
453 with a repository (e.g. corrupt storage, missing data, etc).
448
454
449 Instances are essentially messages associated with severity.
455 Instances are essentially messages associated with severity.
450 """
456 """
451
457
452 warning: bytes | None
458 warning: bytes | None
453 """Message indicating a non-fatal problem."""
459 """Message indicating a non-fatal problem."""
454
460
455 error: bytes | None
461 error: bytes | None
456 """Message indicating a fatal problem."""
462 """Message indicating a fatal problem."""
457
463
458 node: bytes | None
464 node: bytes | None
459 """Revision encountering the problem.
465 """Revision encountering the problem.
460
466
461 ``None`` means the problem doesn't apply to a single revision.
467 ``None`` means the problem doesn't apply to a single revision.
462 """
468 """
463
469
464
470
465 class irevisiondelta(Protocol):
471 class irevisiondelta(Protocol):
466 """Represents a delta between one revision and another.
472 """Represents a delta between one revision and another.
467
473
468 Instances convey enough information to allow a revision to be exchanged
474 Instances convey enough information to allow a revision to be exchanged
469 with another repository.
475 with another repository.
470
476
471 Instances represent the fulltext revision data or a delta against
477 Instances represent the fulltext revision data or a delta against
472 another revision. Therefore the ``revision`` and ``delta`` attributes
478 another revision. Therefore the ``revision`` and ``delta`` attributes
473 are mutually exclusive.
479 are mutually exclusive.
474
480
475 Typically used for changegroup generation.
481 Typically used for changegroup generation.
476 """
482 """
477
483
478 node: bytes
484 node: bytes
479 """20 byte node of this revision."""
485 """20 byte node of this revision."""
480
486
481 p1node: bytes
487 p1node: bytes
482 """20 byte node of 1st parent of this revision."""
488 """20 byte node of 1st parent of this revision."""
483
489
484 p2node: bytes
490 p2node: bytes
485 """20 byte node of 2nd parent of this revision."""
491 """20 byte node of 2nd parent of this revision."""
486
492
487 # TODO: is this really optional? revlog.revlogrevisiondelta defaults to None
493 # TODO: is this really optional? revlog.revlogrevisiondelta defaults to None
488 linknode: bytes | None
494 linknode: bytes | None
489 """20 byte node of the changelog revision this node is linked to."""
495 """20 byte node of the changelog revision this node is linked to."""
490
496
491 flags: int
497 flags: int
492 """2 bytes of integer flags that apply to this revision.
498 """2 bytes of integer flags that apply to this revision.
493
499
494 This is a bitwise composition of the ``REVISION_FLAG_*`` constants.
500 This is a bitwise composition of the ``REVISION_FLAG_*`` constants.
495 """
501 """
496
502
497 basenode: bytes
503 basenode: bytes
498 """20 byte node of the revision this data is a delta against.
504 """20 byte node of the revision this data is a delta against.
499
505
500 ``nullid`` indicates that the revision is a full revision and not
506 ``nullid`` indicates that the revision is a full revision and not
501 a delta.
507 a delta.
502 """
508 """
503
509
504 baserevisionsize: int | None
510 baserevisionsize: int | None
505 """Size of base revision this delta is against.
511 """Size of base revision this delta is against.
506
512
507 May be ``None`` if ``basenode`` is ``nullid``.
513 May be ``None`` if ``basenode`` is ``nullid``.
508 """
514 """
509
515
510 # TODO: is this really optional? (Seems possible in
516 # TODO: is this really optional? (Seems possible in
511 # storageutil.emitrevisions()).
517 # storageutil.emitrevisions()).
512 revision: bytes | None
518 revision: bytes | None
513 """Raw fulltext of revision data for this node."""
519 """Raw fulltext of revision data for this node."""
514
520
515 delta: bytes | None
521 delta: bytes | None
516 """Delta between ``basenode`` and ``node``.
522 """Delta between ``basenode`` and ``node``.
517
523
518 Stored in the bdiff delta format.
524 Stored in the bdiff delta format.
519 """
525 """
520
526
521 sidedata: bytes | None
527 sidedata: bytes | None
522 """Raw sidedata bytes for the given revision."""
528 """Raw sidedata bytes for the given revision."""
523
529
524 protocol_flags: int
530 protocol_flags: int
525 """Single byte of integer flags that can influence the protocol.
531 """Single byte of integer flags that can influence the protocol.
526
532
527 This is a bitwise composition of the ``storageutil.CG_FLAG*`` constants.
533 This is a bitwise composition of the ``storageutil.CG_FLAG*`` constants.
528 """
534 """
529
535
530
536
531 class ifilerevisionssequence(Protocol):
537 class ifilerevisionssequence(Protocol):
532 """Contains index data for all revisions of a file.
538 """Contains index data for all revisions of a file.
533
539
534 Types implementing this behave like lists of tuples. The index
540 Types implementing this behave like lists of tuples. The index
535 in the list corresponds to the revision number. The values contain
541 in the list corresponds to the revision number. The values contain
536 index metadata.
542 index metadata.
537
543
538 The *null* revision (revision number -1) is always the last item
544 The *null* revision (revision number -1) is always the last item
539 in the index.
545 in the index.
540 """
546 """
541
547
542 def __len__(self):
548 def __len__(self):
543 """The total number of revisions."""
549 """The total number of revisions."""
544
550
545 def __getitem__(self, rev):
551 def __getitem__(self, rev):
546 """Returns the object having a specific revision number.
552 """Returns the object having a specific revision number.
547
553
548 Returns an 8-tuple with the following fields:
554 Returns an 8-tuple with the following fields:
549
555
550 offset+flags
556 offset+flags
551 Contains the offset and flags for the revision. 64-bit unsigned
557 Contains the offset and flags for the revision. 64-bit unsigned
552 integer where first 6 bytes are the offset and the next 2 bytes
558 integer where first 6 bytes are the offset and the next 2 bytes
553 are flags. The offset can be 0 if it is not used by the store.
559 are flags. The offset can be 0 if it is not used by the store.
554 compressed size
560 compressed size
555 Size of the revision data in the store. It can be 0 if it isn't
561 Size of the revision data in the store. It can be 0 if it isn't
556 needed by the store.
562 needed by the store.
557 uncompressed size
563 uncompressed size
558 Fulltext size. It can be 0 if it isn't needed by the store.
564 Fulltext size. It can be 0 if it isn't needed by the store.
559 base revision
565 base revision
560 Revision number of revision the delta for storage is encoded
566 Revision number of revision the delta for storage is encoded
561 against. -1 indicates not encoded against a base revision.
567 against. -1 indicates not encoded against a base revision.
562 link revision
568 link revision
563 Revision number of changelog revision this entry is related to.
569 Revision number of changelog revision this entry is related to.
564 p1 revision
570 p1 revision
565 Revision number of 1st parent. -1 if no 1st parent.
571 Revision number of 1st parent. -1 if no 1st parent.
566 p2 revision
572 p2 revision
567 Revision number of 2nd parent. -1 if no 1st parent.
573 Revision number of 2nd parent. -1 if no 1st parent.
568 node
574 node
569 Binary node value for this revision number.
575 Binary node value for this revision number.
570
576
571 Negative values should index off the end of the sequence. ``-1``
577 Negative values should index off the end of the sequence. ``-1``
572 should return the null revision. ``-2`` should return the most
578 should return the null revision. ``-2`` should return the most
573 recent revision.
579 recent revision.
574 """
580 """
575
581
576 def __contains__(self, rev):
582 def __contains__(self, rev):
577 """Whether a revision number exists."""
583 """Whether a revision number exists."""
578
584
579 def insert(self, i, entry):
585 def insert(self, i, entry):
580 """Add an item to the index at specific revision."""
586 """Add an item to the index at specific revision."""
581
587
582
588
583 class ifileindex(Protocol):
589 class ifileindex(Protocol):
584 """Storage interface for index data of a single file.
590 """Storage interface for index data of a single file.
585
591
586 File storage data is divided into index metadata and data storage.
592 File storage data is divided into index metadata and data storage.
587 This interface defines the index portion of the interface.
593 This interface defines the index portion of the interface.
588
594
589 The index logically consists of:
595 The index logically consists of:
590
596
591 * A mapping between revision numbers and nodes.
597 * A mapping between revision numbers and nodes.
592 * DAG data (storing and querying the relationship between nodes).
598 * DAG data (storing and querying the relationship between nodes).
593 * Metadata to facilitate storage.
599 * Metadata to facilitate storage.
594 """
600 """
595
601
596 nullid: bytes
602 nullid: bytes
597 """node for the null revision for use as delta base."""
603 """node for the null revision for use as delta base."""
598
604
599 def __len__(self):
605 def __len__(self):
600 """Obtain the number of revisions stored for this file."""
606 """Obtain the number of revisions stored for this file."""
601
607
602 def __iter__(self):
608 def __iter__(self):
603 """Iterate over revision numbers for this file."""
609 """Iterate over revision numbers for this file."""
604
610
605 def hasnode(self, node):
611 def hasnode(self, node):
606 """Returns a bool indicating if a node is known to this store.
612 """Returns a bool indicating if a node is known to this store.
607
613
608 Implementations must only return True for full, binary node values:
614 Implementations must only return True for full, binary node values:
609 hex nodes, revision numbers, and partial node matches must be
615 hex nodes, revision numbers, and partial node matches must be
610 rejected.
616 rejected.
611
617
612 The null node is never present.
618 The null node is never present.
613 """
619 """
614
620
615 def revs(self, start=0, stop=None):
621 def revs(self, start=0, stop=None):
616 """Iterate over revision numbers for this file, with control."""
622 """Iterate over revision numbers for this file, with control."""
617
623
618 def parents(self, node):
624 def parents(self, node):
619 """Returns a 2-tuple of parent nodes for a revision.
625 """Returns a 2-tuple of parent nodes for a revision.
620
626
621 Values will be ``nullid`` if the parent is empty.
627 Values will be ``nullid`` if the parent is empty.
622 """
628 """
623
629
624 def parentrevs(self, rev):
630 def parentrevs(self, rev):
625 """Like parents() but operates on revision numbers."""
631 """Like parents() but operates on revision numbers."""
626
632
627 def rev(self, node):
633 def rev(self, node):
628 """Obtain the revision number given a node.
634 """Obtain the revision number given a node.
629
635
630 Raises ``error.LookupError`` if the node is not known.
636 Raises ``error.LookupError`` if the node is not known.
631 """
637 """
632
638
633 def node(self, rev):
639 def node(self, rev):
634 """Obtain the node value given a revision number.
640 """Obtain the node value given a revision number.
635
641
636 Raises ``IndexError`` if the node is not known.
642 Raises ``IndexError`` if the node is not known.
637 """
643 """
638
644
639 def lookup(self, node):
645 def lookup(self, node):
640 """Attempt to resolve a value to a node.
646 """Attempt to resolve a value to a node.
641
647
642 Value can be a binary node, hex node, revision number, or a string
648 Value can be a binary node, hex node, revision number, or a string
643 that can be converted to an integer.
649 that can be converted to an integer.
644
650
645 Raises ``error.LookupError`` if a node could not be resolved.
651 Raises ``error.LookupError`` if a node could not be resolved.
646 """
652 """
647
653
648 def linkrev(self, rev):
654 def linkrev(self, rev):
649 """Obtain the changeset revision number a revision is linked to."""
655 """Obtain the changeset revision number a revision is linked to."""
650
656
651 def iscensored(self, rev):
657 def iscensored(self, rev):
652 """Return whether a revision's content has been censored."""
658 """Return whether a revision's content has been censored."""
653
659
654 def commonancestorsheads(self, node1, node2):
660 def commonancestorsheads(self, node1, node2):
655 """Obtain an iterable of nodes containing heads of common ancestors.
661 """Obtain an iterable of nodes containing heads of common ancestors.
656
662
657 See ``ancestor.commonancestorsheads()``.
663 See ``ancestor.commonancestorsheads()``.
658 """
664 """
659
665
660 def descendants(self, revs):
666 def descendants(self, revs):
661 """Obtain descendant revision numbers for a set of revision numbers.
667 """Obtain descendant revision numbers for a set of revision numbers.
662
668
663 If ``nullrev`` is in the set, this is equivalent to ``revs()``.
669 If ``nullrev`` is in the set, this is equivalent to ``revs()``.
664 """
670 """
665
671
666 def heads(self, start=None, stop=None):
672 def heads(self, start=None, stop=None):
667 """Obtain a list of nodes that are DAG heads, with control.
673 """Obtain a list of nodes that are DAG heads, with control.
668
674
669 The set of revisions examined can be limited by specifying
675 The set of revisions examined can be limited by specifying
670 ``start`` and ``stop``. ``start`` is a node. ``stop`` is an
676 ``start`` and ``stop``. ``start`` is a node. ``stop`` is an
671 iterable of nodes. DAG traversal starts at earlier revision
677 iterable of nodes. DAG traversal starts at earlier revision
672 ``start`` and iterates forward until any node in ``stop`` is
678 ``start`` and iterates forward until any node in ``stop`` is
673 encountered.
679 encountered.
674 """
680 """
675
681
676 def children(self, node):
682 def children(self, node):
677 """Obtain nodes that are children of a node.
683 """Obtain nodes that are children of a node.
678
684
679 Returns a list of nodes.
685 Returns a list of nodes.
680 """
686 """
681
687
682
688
683 class ifiledata(Protocol):
689 class ifiledata(Protocol):
684 """Storage interface for data storage of a specific file.
690 """Storage interface for data storage of a specific file.
685
691
686 This complements ``ifileindex`` and provides an interface for accessing
692 This complements ``ifileindex`` and provides an interface for accessing
687 data for a tracked file.
693 data for a tracked file.
688 """
694 """
689
695
690 def size(self, rev):
696 def size(self, rev):
691 """Obtain the fulltext size of file data.
697 """Obtain the fulltext size of file data.
692
698
693 Any metadata is excluded from size measurements.
699 Any metadata is excluded from size measurements.
694 """
700 """
695
701
696 def revision(self, node):
702 def revision(self, node):
697 """Obtain fulltext data for a node.
703 """Obtain fulltext data for a node.
698
704
699 By default, any storage transformations are applied before the data
705 By default, any storage transformations are applied before the data
700 is returned. If ``raw`` is True, non-raw storage transformations
706 is returned. If ``raw`` is True, non-raw storage transformations
701 are not applied.
707 are not applied.
702
708
703 The fulltext data may contain a header containing metadata. Most
709 The fulltext data may contain a header containing metadata. Most
704 consumers should use ``read()`` to obtain the actual file data.
710 consumers should use ``read()`` to obtain the actual file data.
705 """
711 """
706
712
707 def rawdata(self, node):
713 def rawdata(self, node):
708 """Obtain raw data for a node."""
714 """Obtain raw data for a node."""
709
715
710 def read(self, node):
716 def read(self, node):
711 """Resolve file fulltext data.
717 """Resolve file fulltext data.
712
718
713 This is similar to ``revision()`` except any metadata in the data
719 This is similar to ``revision()`` except any metadata in the data
714 headers is stripped.
720 headers is stripped.
715 """
721 """
716
722
717 def renamed(self, node):
723 def renamed(self, node):
718 """Obtain copy metadata for a node.
724 """Obtain copy metadata for a node.
719
725
720 Returns ``False`` if no copy metadata is stored or a 2-tuple of
726 Returns ``False`` if no copy metadata is stored or a 2-tuple of
721 (path, node) from which this revision was copied.
727 (path, node) from which this revision was copied.
722 """
728 """
723
729
724 def cmp(self, node, fulltext):
730 def cmp(self, node, fulltext):
725 """Compare fulltext to another revision.
731 """Compare fulltext to another revision.
726
732
727 Returns True if the fulltext is different from what is stored.
733 Returns True if the fulltext is different from what is stored.
728
734
729 This takes copy metadata into account.
735 This takes copy metadata into account.
730
736
731 TODO better document the copy metadata and censoring logic.
737 TODO better document the copy metadata and censoring logic.
732 """
738 """
733
739
734 def emitrevisions(
740 def emitrevisions(
735 self,
741 self,
736 nodes,
742 nodes,
737 nodesorder=None,
743 nodesorder=None,
738 revisiondata=False,
744 revisiondata=False,
739 assumehaveparentrevisions=False,
745 assumehaveparentrevisions=False,
740 deltamode=CG_DELTAMODE_STD,
746 deltamode=CG_DELTAMODE_STD,
741 ):
747 ):
742 """Produce ``irevisiondelta`` for revisions.
748 """Produce ``irevisiondelta`` for revisions.
743
749
744 Given an iterable of nodes, emits objects conforming to the
750 Given an iterable of nodes, emits objects conforming to the
745 ``irevisiondelta`` interface that describe revisions in storage.
751 ``irevisiondelta`` interface that describe revisions in storage.
746
752
747 This method is a generator.
753 This method is a generator.
748
754
749 The input nodes may be unordered. Implementations must ensure that a
755 The input nodes may be unordered. Implementations must ensure that a
750 node's parents are emitted before the node itself. Transitively, this
756 node's parents are emitted before the node itself. Transitively, this
751 means that a node may only be emitted once all its ancestors in
757 means that a node may only be emitted once all its ancestors in
752 ``nodes`` have also been emitted.
758 ``nodes`` have also been emitted.
753
759
754 By default, emits "index" data (the ``node``, ``p1node``, and
760 By default, emits "index" data (the ``node``, ``p1node``, and
755 ``p2node`` attributes). If ``revisiondata`` is set, revision data
761 ``p2node`` attributes). If ``revisiondata`` is set, revision data
756 will also be present on the emitted objects.
762 will also be present on the emitted objects.
757
763
758 With default argument values, implementations can choose to emit
764 With default argument values, implementations can choose to emit
759 either fulltext revision data or a delta. When emitting deltas,
765 either fulltext revision data or a delta. When emitting deltas,
760 implementations must consider whether the delta's base revision
766 implementations must consider whether the delta's base revision
761 fulltext is available to the receiver.
767 fulltext is available to the receiver.
762
768
763 The base revision fulltext is guaranteed to be available if any of
769 The base revision fulltext is guaranteed to be available if any of
764 the following are met:
770 the following are met:
765
771
766 * Its fulltext revision was emitted by this method call.
772 * Its fulltext revision was emitted by this method call.
767 * A delta for that revision was emitted by this method call.
773 * A delta for that revision was emitted by this method call.
768 * ``assumehaveparentrevisions`` is True and the base revision is a
774 * ``assumehaveparentrevisions`` is True and the base revision is a
769 parent of the node.
775 parent of the node.
770
776
771 ``nodesorder`` can be used to control the order that revisions are
777 ``nodesorder`` can be used to control the order that revisions are
772 emitted. By default, revisions can be reordered as long as they are
778 emitted. By default, revisions can be reordered as long as they are
773 in DAG topological order (see above). If the value is ``nodes``,
779 in DAG topological order (see above). If the value is ``nodes``,
774 the iteration order from ``nodes`` should be used. If the value is
780 the iteration order from ``nodes`` should be used. If the value is
775 ``storage``, then the native order from the backing storage layer
781 ``storage``, then the native order from the backing storage layer
776 is used. (Not all storage layers will have strong ordering and behavior
782 is used. (Not all storage layers will have strong ordering and behavior
777 of this mode is storage-dependent.) ``nodes`` ordering can force
783 of this mode is storage-dependent.) ``nodes`` ordering can force
778 revisions to be emitted before their ancestors, so consumers should
784 revisions to be emitted before their ancestors, so consumers should
779 use it with care.
785 use it with care.
780
786
781 The ``linknode`` attribute on the returned ``irevisiondelta`` may not
787 The ``linknode`` attribute on the returned ``irevisiondelta`` may not
782 be set and it is the caller's responsibility to resolve it, if needed.
788 be set and it is the caller's responsibility to resolve it, if needed.
783
789
784 If ``deltamode`` is CG_DELTAMODE_PREV and revision data is requested,
790 If ``deltamode`` is CG_DELTAMODE_PREV and revision data is requested,
785 all revision data should be emitted as deltas against the revision
791 all revision data should be emitted as deltas against the revision
786 emitted just prior. The initial revision should be a delta against its
792 emitted just prior. The initial revision should be a delta against its
787 1st parent.
793 1st parent.
788 """
794 """
789
795
790
796
791 class ifilemutation(Protocol):
797 class ifilemutation(Protocol):
792 """Storage interface for mutation events of a tracked file."""
798 """Storage interface for mutation events of a tracked file."""
793
799
794 def add(self, filedata, meta, transaction, linkrev, p1, p2):
800 def add(self, filedata, meta, transaction, linkrev, p1, p2):
795 """Add a new revision to the store.
801 """Add a new revision to the store.
796
802
797 Takes file data, dictionary of metadata, a transaction, linkrev,
803 Takes file data, dictionary of metadata, a transaction, linkrev,
798 and parent nodes.
804 and parent nodes.
799
805
800 Returns the node that was added.
806 Returns the node that was added.
801
807
802 May no-op if a revision matching the supplied data is already stored.
808 May no-op if a revision matching the supplied data is already stored.
803 """
809 """
804
810
805 def addrevision(
811 def addrevision(
806 self,
812 self,
807 revisiondata,
813 revisiondata,
808 transaction,
814 transaction,
809 linkrev,
815 linkrev,
810 p1,
816 p1,
811 p2,
817 p2,
812 node=None,
818 node=None,
813 flags=0,
819 flags=0,
814 cachedelta=None,
820 cachedelta=None,
815 ):
821 ):
816 """Add a new revision to the store and return its number.
822 """Add a new revision to the store and return its number.
817
823
818 This is similar to ``add()`` except it operates at a lower level.
824 This is similar to ``add()`` except it operates at a lower level.
819
825
820 The data passed in already contains a metadata header, if any.
826 The data passed in already contains a metadata header, if any.
821
827
822 ``node`` and ``flags`` can be used to define the expected node and
828 ``node`` and ``flags`` can be used to define the expected node and
823 the flags to use with storage. ``flags`` is a bitwise value composed
829 the flags to use with storage. ``flags`` is a bitwise value composed
824 of the various ``REVISION_FLAG_*`` constants.
830 of the various ``REVISION_FLAG_*`` constants.
825
831
826 ``add()`` is usually called when adding files from e.g. the working
832 ``add()`` is usually called when adding files from e.g. the working
827 directory. ``addrevision()`` is often called by ``add()`` and for
833 directory. ``addrevision()`` is often called by ``add()`` and for
828 scenarios where revision data has already been computed, such as when
834 scenarios where revision data has already been computed, such as when
829 applying raw data from a peer repo.
835 applying raw data from a peer repo.
830 """
836 """
831
837
832 def addgroup(
838 def addgroup(
833 self,
839 self,
834 deltas,
840 deltas,
835 linkmapper,
841 linkmapper,
836 transaction,
842 transaction,
837 addrevisioncb=None,
843 addrevisioncb=None,
838 duplicaterevisioncb=None,
844 duplicaterevisioncb=None,
839 maybemissingparents=False,
845 maybemissingparents=False,
840 ):
846 ):
841 """Process a series of deltas for storage.
847 """Process a series of deltas for storage.
842
848
843 ``deltas`` is an iterable of 7-tuples of
849 ``deltas`` is an iterable of 7-tuples of
844 (node, p1, p2, linknode, deltabase, delta, flags) defining revisions
850 (node, p1, p2, linknode, deltabase, delta, flags) defining revisions
845 to add.
851 to add.
846
852
847 The ``delta`` field contains ``mpatch`` data to apply to a base
853 The ``delta`` field contains ``mpatch`` data to apply to a base
848 revision, identified by ``deltabase``. The base node can be
854 revision, identified by ``deltabase``. The base node can be
849 ``nullid``, in which case the header from the delta can be ignored
855 ``nullid``, in which case the header from the delta can be ignored
850 and the delta used as the fulltext.
856 and the delta used as the fulltext.
851
857
852 ``alwayscache`` instructs the lower layers to cache the content of the
858 ``alwayscache`` instructs the lower layers to cache the content of the
853 newly added revision, even if it needs to be explicitly computed.
859 newly added revision, even if it needs to be explicitly computed.
854 This used to be the default when ``addrevisioncb`` was provided up to
860 This used to be the default when ``addrevisioncb`` was provided up to
855 Mercurial 5.8.
861 Mercurial 5.8.
856
862
857 ``addrevisioncb`` should be called for each new rev as it is committed.
863 ``addrevisioncb`` should be called for each new rev as it is committed.
858 ``duplicaterevisioncb`` should be called for all revs with a
864 ``duplicaterevisioncb`` should be called for all revs with a
859 pre-existing node.
865 pre-existing node.
860
866
861 ``maybemissingparents`` is a bool indicating whether the incoming
867 ``maybemissingparents`` is a bool indicating whether the incoming
862 data may reference parents/ancestor revisions that aren't present.
868 data may reference parents/ancestor revisions that aren't present.
863 This flag is set when receiving data into a "shallow" store that
869 This flag is set when receiving data into a "shallow" store that
864 doesn't hold all history.
870 doesn't hold all history.
865
871
866 Returns a list of nodes that were processed. A node will be in the list
872 Returns a list of nodes that were processed. A node will be in the list
867 even if it existed in the store previously.
873 even if it existed in the store previously.
868 """
874 """
869
875
870 def censorrevision(self, tr, node, tombstone=b''):
876 def censorrevision(self, tr, node, tombstone=b''):
871 """Remove the content of a single revision.
877 """Remove the content of a single revision.
872
878
873 The specified ``node`` will have its content purged from storage.
879 The specified ``node`` will have its content purged from storage.
874 Future attempts to access the revision data for this node will
880 Future attempts to access the revision data for this node will
875 result in failure.
881 result in failure.
876
882
877 A ``tombstone`` message can optionally be stored. This message may be
883 A ``tombstone`` message can optionally be stored. This message may be
878 displayed to users when they attempt to access the missing revision
884 displayed to users when they attempt to access the missing revision
879 data.
885 data.
880
886
881 Storage backends may have stored deltas against the previous content
887 Storage backends may have stored deltas against the previous content
882 in this revision. As part of censoring a revision, these storage
888 in this revision. As part of censoring a revision, these storage
883 backends are expected to rewrite any internally stored deltas such
889 backends are expected to rewrite any internally stored deltas such
884 that they no longer reference the deleted content.
890 that they no longer reference the deleted content.
885 """
891 """
886
892
887 def getstrippoint(self, minlink):
893 def getstrippoint(self, minlink):
888 """Find the minimum revision that must be stripped to strip a linkrev.
894 """Find the minimum revision that must be stripped to strip a linkrev.
889
895
890 Returns a 2-tuple containing the minimum revision number and a set
896 Returns a 2-tuple containing the minimum revision number and a set
891 of all revisions numbers that would be broken by this strip.
897 of all revisions numbers that would be broken by this strip.
892
898
893 TODO this is highly revlog centric and should be abstracted into
899 TODO this is highly revlog centric and should be abstracted into
894 a higher-level deletion API. ``repair.strip()`` relies on this.
900 a higher-level deletion API. ``repair.strip()`` relies on this.
895 """
901 """
896
902
897 def strip(self, minlink, transaction):
903 def strip(self, minlink, transaction):
898 """Remove storage of items starting at a linkrev.
904 """Remove storage of items starting at a linkrev.
899
905
900 This uses ``getstrippoint()`` to determine the first node to remove.
906 This uses ``getstrippoint()`` to determine the first node to remove.
901 Then it effectively truncates storage for all revisions after that.
907 Then it effectively truncates storage for all revisions after that.
902
908
903 TODO this is highly revlog centric and should be abstracted into a
909 TODO this is highly revlog centric and should be abstracted into a
904 higher-level deletion API.
910 higher-level deletion API.
905 """
911 """
906
912
907
913
908 class ifilestorage(ifileindex, ifiledata, ifilemutation):
914 class ifilestorage(ifileindex, ifiledata, ifilemutation):
909 """Complete storage interface for a single tracked file."""
915 """Complete storage interface for a single tracked file."""
910
916
911 def files(self):
917 def files(self):
912 """Obtain paths that are backing storage for this file.
918 """Obtain paths that are backing storage for this file.
913
919
914 TODO this is used heavily by verify code and there should probably
920 TODO this is used heavily by verify code and there should probably
915 be a better API for that.
921 be a better API for that.
916 """
922 """
917
923
918 def storageinfo(
924 def storageinfo(
919 self,
925 self,
920 exclusivefiles=False,
926 exclusivefiles=False,
921 sharedfiles=False,
927 sharedfiles=False,
922 revisionscount=False,
928 revisionscount=False,
923 trackedsize=False,
929 trackedsize=False,
924 storedsize=False,
930 storedsize=False,
925 ):
931 ):
926 """Obtain information about storage for this file's data.
932 """Obtain information about storage for this file's data.
927
933
928 Returns a dict describing storage for this tracked path. The keys
934 Returns a dict describing storage for this tracked path. The keys
929 in the dict map to arguments of the same. The arguments are bools
935 in the dict map to arguments of the same. The arguments are bools
930 indicating whether to calculate and obtain that data.
936 indicating whether to calculate and obtain that data.
931
937
932 exclusivefiles
938 exclusivefiles
933 Iterable of (vfs, path) describing files that are exclusively
939 Iterable of (vfs, path) describing files that are exclusively
934 used to back storage for this tracked path.
940 used to back storage for this tracked path.
935
941
936 sharedfiles
942 sharedfiles
937 Iterable of (vfs, path) describing files that are used to back
943 Iterable of (vfs, path) describing files that are used to back
938 storage for this tracked path. Those files may also provide storage
944 storage for this tracked path. Those files may also provide storage
939 for other stored entities.
945 for other stored entities.
940
946
941 revisionscount
947 revisionscount
942 Number of revisions available for retrieval.
948 Number of revisions available for retrieval.
943
949
944 trackedsize
950 trackedsize
945 Total size in bytes of all tracked revisions. This is a sum of the
951 Total size in bytes of all tracked revisions. This is a sum of the
946 length of the fulltext of all revisions.
952 length of the fulltext of all revisions.
947
953
948 storedsize
954 storedsize
949 Total size in bytes used to store data for all tracked revisions.
955 Total size in bytes used to store data for all tracked revisions.
950 This is commonly less than ``trackedsize`` due to internal usage
956 This is commonly less than ``trackedsize`` due to internal usage
951 of deltas rather than fulltext revisions.
957 of deltas rather than fulltext revisions.
952
958
953 Not all storage backends may support all queries are have a reasonable
959 Not all storage backends may support all queries are have a reasonable
954 value to use. In that case, the value should be set to ``None`` and
960 value to use. In that case, the value should be set to ``None`` and
955 callers are expected to handle this special value.
961 callers are expected to handle this special value.
956 """
962 """
957
963
958 def verifyintegrity(self, state):
964 def verifyintegrity(self, state):
959 """Verifies the integrity of file storage.
965 """Verifies the integrity of file storage.
960
966
961 ``state`` is a dict holding state of the verifier process. It can be
967 ``state`` is a dict holding state of the verifier process. It can be
962 used to communicate data between invocations of multiple storage
968 used to communicate data between invocations of multiple storage
963 primitives.
969 primitives.
964
970
965 If individual revisions cannot have their revision content resolved,
971 If individual revisions cannot have their revision content resolved,
966 the method is expected to set the ``skipread`` key to a set of nodes
972 the method is expected to set the ``skipread`` key to a set of nodes
967 that encountered problems. If set, the method can also add the node(s)
973 that encountered problems. If set, the method can also add the node(s)
968 to ``safe_renamed`` in order to indicate nodes that may perform the
974 to ``safe_renamed`` in order to indicate nodes that may perform the
969 rename checks with currently accessible data.
975 rename checks with currently accessible data.
970
976
971 The method yields objects conforming to the ``iverifyproblem``
977 The method yields objects conforming to the ``iverifyproblem``
972 interface.
978 interface.
973 """
979 """
974
980
975
981
976 class idirs(Protocol):
982 class idirs(Protocol):
977 """Interface representing a collection of directories from paths.
983 """Interface representing a collection of directories from paths.
978
984
979 This interface is essentially a derived data structure representing
985 This interface is essentially a derived data structure representing
980 directories from a collection of paths.
986 directories from a collection of paths.
981 """
987 """
982
988
983 def addpath(self, path):
989 def addpath(self, path):
984 """Add a path to the collection.
990 """Add a path to the collection.
985
991
986 All directories in the path will be added to the collection.
992 All directories in the path will be added to the collection.
987 """
993 """
988
994
989 def delpath(self, path):
995 def delpath(self, path):
990 """Remove a path from the collection.
996 """Remove a path from the collection.
991
997
992 If the removal was the last path in a particular directory, the
998 If the removal was the last path in a particular directory, the
993 directory is removed from the collection.
999 directory is removed from the collection.
994 """
1000 """
995
1001
996 def __iter__(self):
1002 def __iter__(self):
997 """Iterate over the directories in this collection of paths."""
1003 """Iterate over the directories in this collection of paths."""
998
1004
999 def __contains__(self, path):
1005 def __contains__(self, path):
1000 """Whether a specific directory is in this collection."""
1006 """Whether a specific directory is in this collection."""
1001
1007
1002
1008
1003 class imanifestdict(Protocol):
1009 class imanifestdict(Protocol):
1004 """Interface representing a manifest data structure.
1010 """Interface representing a manifest data structure.
1005
1011
1006 A manifest is effectively a dict mapping paths to entries. Each entry
1012 A manifest is effectively a dict mapping paths to entries. Each entry
1007 consists of a binary node and extra flags affecting that entry.
1013 consists of a binary node and extra flags affecting that entry.
1008 """
1014 """
1009
1015
1010 def __getitem__(self, path):
1016 def __getitem__(self, path):
1011 """Returns the binary node value for a path in the manifest.
1017 """Returns the binary node value for a path in the manifest.
1012
1018
1013 Raises ``KeyError`` if the path does not exist in the manifest.
1019 Raises ``KeyError`` if the path does not exist in the manifest.
1014
1020
1015 Equivalent to ``self.find(path)[0]``.
1021 Equivalent to ``self.find(path)[0]``.
1016 """
1022 """
1017
1023
1018 def find(self, path):
1024 def find(self, path):
1019 """Returns the entry for a path in the manifest.
1025 """Returns the entry for a path in the manifest.
1020
1026
1021 Returns a 2-tuple of (node, flags).
1027 Returns a 2-tuple of (node, flags).
1022
1028
1023 Raises ``KeyError`` if the path does not exist in the manifest.
1029 Raises ``KeyError`` if the path does not exist in the manifest.
1024 """
1030 """
1025
1031
1026 def __len__(self):
1032 def __len__(self):
1027 """Return the number of entries in the manifest."""
1033 """Return the number of entries in the manifest."""
1028
1034
1029 def __nonzero__(self):
1035 def __nonzero__(self):
1030 """Returns True if the manifest has entries, False otherwise."""
1036 """Returns True if the manifest has entries, False otherwise."""
1031
1037
1032 __bool__ = __nonzero__
1038 __bool__ = __nonzero__
1033
1039
1034 def set(self, path, node, flags):
1040 def set(self, path, node, flags):
1035 """Define the node value and flags for a path in the manifest.
1041 """Define the node value and flags for a path in the manifest.
1036
1042
1037 Equivalent to __setitem__ followed by setflag, but can be more efficient.
1043 Equivalent to __setitem__ followed by setflag, but can be more efficient.
1038 """
1044 """
1039
1045
1040 def __setitem__(self, path, node):
1046 def __setitem__(self, path, node):
1041 """Define the node value for a path in the manifest.
1047 """Define the node value for a path in the manifest.
1042
1048
1043 If the path is already in the manifest, its flags will be copied to
1049 If the path is already in the manifest, its flags will be copied to
1044 the new entry.
1050 the new entry.
1045 """
1051 """
1046
1052
1047 def __contains__(self, path):
1053 def __contains__(self, path):
1048 """Whether a path exists in the manifest."""
1054 """Whether a path exists in the manifest."""
1049
1055
1050 def __delitem__(self, path):
1056 def __delitem__(self, path):
1051 """Remove a path from the manifest.
1057 """Remove a path from the manifest.
1052
1058
1053 Raises ``KeyError`` if the path is not in the manifest.
1059 Raises ``KeyError`` if the path is not in the manifest.
1054 """
1060 """
1055
1061
1056 def __iter__(self):
1062 def __iter__(self):
1057 """Iterate over paths in the manifest."""
1063 """Iterate over paths in the manifest."""
1058
1064
1059 def iterkeys(self):
1065 def iterkeys(self):
1060 """Iterate over paths in the manifest."""
1066 """Iterate over paths in the manifest."""
1061
1067
1062 def keys(self):
1068 def keys(self):
1063 """Obtain a list of paths in the manifest."""
1069 """Obtain a list of paths in the manifest."""
1064
1070
1065 def filesnotin(self, other, match=None):
1071 def filesnotin(self, other, match=None):
1066 """Obtain the set of paths in this manifest but not in another.
1072 """Obtain the set of paths in this manifest but not in another.
1067
1073
1068 ``match`` is an optional matcher function to be applied to both
1074 ``match`` is an optional matcher function to be applied to both
1069 manifests.
1075 manifests.
1070
1076
1071 Returns a set of paths.
1077 Returns a set of paths.
1072 """
1078 """
1073
1079
1074 def dirs(self):
1080 def dirs(self):
1075 """Returns an object implementing the ``idirs`` interface."""
1081 """Returns an object implementing the ``idirs`` interface."""
1076
1082
1077 def hasdir(self, dir):
1083 def hasdir(self, dir):
1078 """Returns a bool indicating if a directory is in this manifest."""
1084 """Returns a bool indicating if a directory is in this manifest."""
1079
1085
1080 def walk(self, match):
1086 def walk(self, match):
1081 """Generator of paths in manifest satisfying a matcher.
1087 """Generator of paths in manifest satisfying a matcher.
1082
1088
1083 If the matcher has explicit files listed and they don't exist in
1089 If the matcher has explicit files listed and they don't exist in
1084 the manifest, ``match.bad()`` is called for each missing file.
1090 the manifest, ``match.bad()`` is called for each missing file.
1085 """
1091 """
1086
1092
1087 def diff(self, other, match=None, clean=False):
1093 def diff(self, other, match=None, clean=False):
1088 """Find differences between this manifest and another.
1094 """Find differences between this manifest and another.
1089
1095
1090 This manifest is compared to ``other``.
1096 This manifest is compared to ``other``.
1091
1097
1092 If ``match`` is provided, the two manifests are filtered against this
1098 If ``match`` is provided, the two manifests are filtered against this
1093 matcher and only entries satisfying the matcher are compared.
1099 matcher and only entries satisfying the matcher are compared.
1094
1100
1095 If ``clean`` is True, unchanged files are included in the returned
1101 If ``clean`` is True, unchanged files are included in the returned
1096 object.
1102 object.
1097
1103
1098 Returns a dict with paths as keys and values of 2-tuples of 2-tuples of
1104 Returns a dict with paths as keys and values of 2-tuples of 2-tuples of
1099 the form ``((node1, flag1), (node2, flag2))`` where ``(node1, flag1)``
1105 the form ``((node1, flag1), (node2, flag2))`` where ``(node1, flag1)``
1100 represents the node and flags for this manifest and ``(node2, flag2)``
1106 represents the node and flags for this manifest and ``(node2, flag2)``
1101 are the same for the other manifest.
1107 are the same for the other manifest.
1102 """
1108 """
1103
1109
1104 def setflag(self, path, flag):
1110 def setflag(self, path, flag):
1105 """Set the flag value for a given path.
1111 """Set the flag value for a given path.
1106
1112
1107 Raises ``KeyError`` if the path is not already in the manifest.
1113 Raises ``KeyError`` if the path is not already in the manifest.
1108 """
1114 """
1109
1115
1110 def get(self, path, default=None):
1116 def get(self, path, default=None):
1111 """Obtain the node value for a path or a default value if missing."""
1117 """Obtain the node value for a path or a default value if missing."""
1112
1118
1113 def flags(self, path):
1119 def flags(self, path):
1114 """Return the flags value for a path (default: empty bytestring)."""
1120 """Return the flags value for a path (default: empty bytestring)."""
1115
1121
1116 def copy(self):
1122 def copy(self):
1117 """Return a copy of this manifest."""
1123 """Return a copy of this manifest."""
1118
1124
1119 def items(self):
1125 def items(self):
1120 """Returns an iterable of (path, node) for items in this manifest."""
1126 """Returns an iterable of (path, node) for items in this manifest."""
1121
1127
1122 def iteritems(self):
1128 def iteritems(self):
1123 """Identical to items()."""
1129 """Identical to items()."""
1124
1130
1125 def iterentries(self):
1131 def iterentries(self):
1126 """Returns an iterable of (path, node, flags) for this manifest.
1132 """Returns an iterable of (path, node, flags) for this manifest.
1127
1133
1128 Similar to ``iteritems()`` except items are a 3-tuple and include
1134 Similar to ``iteritems()`` except items are a 3-tuple and include
1129 flags.
1135 flags.
1130 """
1136 """
1131
1137
1132 def text(self):
1138 def text(self):
1133 """Obtain the raw data representation for this manifest.
1139 """Obtain the raw data representation for this manifest.
1134
1140
1135 Result is used to create a manifest revision.
1141 Result is used to create a manifest revision.
1136 """
1142 """
1137
1143
1138 def fastdelta(self, base, changes):
1144 def fastdelta(self, base, changes):
1139 """Obtain a delta between this manifest and another given changes.
1145 """Obtain a delta between this manifest and another given changes.
1140
1146
1141 ``base`` in the raw data representation for another manifest.
1147 ``base`` in the raw data representation for another manifest.
1142
1148
1143 ``changes`` is an iterable of ``(path, to_delete)``.
1149 ``changes`` is an iterable of ``(path, to_delete)``.
1144
1150
1145 Returns a 2-tuple containing ``bytearray(self.text())`` and the
1151 Returns a 2-tuple containing ``bytearray(self.text())`` and the
1146 delta between ``base`` and this manifest.
1152 delta between ``base`` and this manifest.
1147
1153
1148 If this manifest implementation can't support ``fastdelta()``,
1154 If this manifest implementation can't support ``fastdelta()``,
1149 raise ``mercurial.manifest.FastdeltaUnavailable``.
1155 raise ``mercurial.manifest.FastdeltaUnavailable``.
1150 """
1156 """
1151
1157
1152
1158
1153 class imanifestrevisionbase(Protocol):
1159 class imanifestrevisionbase(Protocol):
1154 """Base interface representing a single revision of a manifest.
1160 """Base interface representing a single revision of a manifest.
1155
1161
1156 Should not be used as a primary interface: should always be inherited
1162 Should not be used as a primary interface: should always be inherited
1157 as part of a larger interface.
1163 as part of a larger interface.
1158 """
1164 """
1159
1165
1160 def copy(self):
1166 def copy(self):
1161 """Obtain a copy of this manifest instance.
1167 """Obtain a copy of this manifest instance.
1162
1168
1163 Returns an object conforming to the ``imanifestrevisionwritable``
1169 Returns an object conforming to the ``imanifestrevisionwritable``
1164 interface. The instance will be associated with the same
1170 interface. The instance will be associated with the same
1165 ``imanifestlog`` collection as this instance.
1171 ``imanifestlog`` collection as this instance.
1166 """
1172 """
1167
1173
1168 def read(self):
1174 def read(self):
1169 """Obtain the parsed manifest data structure.
1175 """Obtain the parsed manifest data structure.
1170
1176
1171 The returned object conforms to the ``imanifestdict`` interface.
1177 The returned object conforms to the ``imanifestdict`` interface.
1172 """
1178 """
1173
1179
1174
1180
1175 class imanifestrevisionstored(imanifestrevisionbase, Protocol):
1181 class imanifestrevisionstored(imanifestrevisionbase, Protocol):
1176 """Interface representing a manifest revision committed to storage."""
1182 """Interface representing a manifest revision committed to storage."""
1177
1183
1178 @abc.abstractmethod
1184 @abc.abstractmethod
1179 def node(self) -> bytes:
1185 def node(self) -> bytes:
1180 """The binary node for this manifest."""
1186 """The binary node for this manifest."""
1181
1187
1182 parents: list[bytes]
1188 parents: list[bytes]
1183 """List of binary nodes that are parents for this manifest revision."""
1189 """List of binary nodes that are parents for this manifest revision."""
1184
1190
1185 @abc.abstractmethod
1191 @abc.abstractmethod
1186 def readdelta(self, shallow: bool = False):
1192 def readdelta(self, shallow: bool = False):
1187 """Obtain the manifest data structure representing changes from parent.
1193 """Obtain the manifest data structure representing changes from parent.
1188
1194
1189 This manifest is compared to its 1st parent. A new manifest
1195 This manifest is compared to its 1st parent. A new manifest
1190 representing those differences is constructed.
1196 representing those differences is constructed.
1191
1197
1192 If `shallow` is True, this will read the delta for this directory,
1198 If `shallow` is True, this will read the delta for this directory,
1193 without recursively reading subdirectory manifests. Instead, any
1199 without recursively reading subdirectory manifests. Instead, any
1194 subdirectory entry will be reported as it appears in the manifest, i.e.
1200 subdirectory entry will be reported as it appears in the manifest, i.e.
1195 the subdirectory will be reported among files and distinguished only by
1201 the subdirectory will be reported among files and distinguished only by
1196 its 't' flag. This only apply if the underlying manifest support it.
1202 its 't' flag. This only apply if the underlying manifest support it.
1197
1203
1198 The returned object conforms to the ``imanifestdict`` interface.
1204 The returned object conforms to the ``imanifestdict`` interface.
1199 """
1205 """
1200
1206
1201 @abc.abstractmethod
1207 @abc.abstractmethod
1202 def read_any_fast_delta(
1208 def read_any_fast_delta(
1203 self,
1209 self,
1204 valid_bases: Collection[int] | None = None,
1210 valid_bases: Collection[int] | None = None,
1205 *,
1211 *,
1206 shallow: bool = False,
1212 shallow: bool = False,
1207 ):
1213 ):
1208 """read some manifest information as fast if possible
1214 """read some manifest information as fast if possible
1209
1215
1210 This might return a "delta", a manifest object containing only file
1216 This might return a "delta", a manifest object containing only file
1211 changed compared to another revisions. The `valid_bases` argument
1217 changed compared to another revisions. The `valid_bases` argument
1212 control the set of revision that might be used as a base.
1218 control the set of revision that might be used as a base.
1213
1219
1214 If no delta can be retrieved quickly, a full read of the manifest will
1220 If no delta can be retrieved quickly, a full read of the manifest will
1215 be performed instead.
1221 be performed instead.
1216
1222
1217 The function return a tuple with two elements. The first one is the
1223 The function return a tuple with two elements. The first one is the
1218 delta base used (or None if we did a full read), the second one is the
1224 delta base used (or None if we did a full read), the second one is the
1219 manifest information.
1225 manifest information.
1220
1226
1221 If `shallow` is True, this will read the delta for this directory,
1227 If `shallow` is True, this will read the delta for this directory,
1222 without recursively reading subdirectory manifests. Instead, any
1228 without recursively reading subdirectory manifests. Instead, any
1223 subdirectory entry will be reported as it appears in the manifest, i.e.
1229 subdirectory entry will be reported as it appears in the manifest, i.e.
1224 the subdirectory will be reported among files and distinguished only by
1230 the subdirectory will be reported among files and distinguished only by
1225 its 't' flag. This only apply if the underlying manifest support it.
1231 its 't' flag. This only apply if the underlying manifest support it.
1226
1232
1227 The returned object conforms to the ``imanifestdict`` interface.
1233 The returned object conforms to the ``imanifestdict`` interface.
1228 """
1234 """
1229
1235
1230 @abc.abstractmethod
1236 @abc.abstractmethod
1231 def read_delta_parents(self, *, shallow: bool = False, exact: bool = True):
1237 def read_delta_parents(self, *, shallow: bool = False, exact: bool = True):
1232 """return a diff from this revision against both parents.
1238 """return a diff from this revision against both parents.
1233
1239
1234 If `exact` is False, this might return a superset of the diff, containing
1240 If `exact` is False, this might return a superset of the diff, containing
1235 files that are actually present as is in one of the parents.
1241 files that are actually present as is in one of the parents.
1236
1242
1237 If `shallow` is True, this will read the delta for this directory,
1243 If `shallow` is True, this will read the delta for this directory,
1238 without recursively reading subdirectory manifests. Instead, any
1244 without recursively reading subdirectory manifests. Instead, any
1239 subdirectory entry will be reported as it appears in the manifest, i.e.
1245 subdirectory entry will be reported as it appears in the manifest, i.e.
1240 the subdirectory will be reported among files and distinguished only by
1246 the subdirectory will be reported among files and distinguished only by
1241 its 't' flag. This only apply if the underlying manifest support it.
1247 its 't' flag. This only apply if the underlying manifest support it.
1242
1248
1243 The returned object conforms to the ``imanifestdict`` interface."""
1249 The returned object conforms to the ``imanifestdict`` interface."""
1244
1250
1245 @abc.abstractmethod
1251 @abc.abstractmethod
1246 def read_delta_new_entries(self, *, shallow: bool = False):
1252 def read_delta_new_entries(self, *, shallow: bool = False):
1247 """Return a manifest containing just the entries that might be new to
1253 """Return a manifest containing just the entries that might be new to
1248 the repository.
1254 the repository.
1249
1255
1250 This is often equivalent to a diff against both parents, but without
1256 This is often equivalent to a diff against both parents, but without
1251 garantee. For performance reason, It might contains more files in some cases.
1257 garantee. For performance reason, It might contains more files in some cases.
1252
1258
1253 If `shallow` is True, this will read the delta for this directory,
1259 If `shallow` is True, this will read the delta for this directory,
1254 without recursively reading subdirectory manifests. Instead, any
1260 without recursively reading subdirectory manifests. Instead, any
1255 subdirectory entry will be reported as it appears in the manifest, i.e.
1261 subdirectory entry will be reported as it appears in the manifest, i.e.
1256 the subdirectory will be reported among files and distinguished only by
1262 the subdirectory will be reported among files and distinguished only by
1257 its 't' flag. This only apply if the underlying manifest support it.
1263 its 't' flag. This only apply if the underlying manifest support it.
1258
1264
1259 The returned object conforms to the ``imanifestdict`` interface."""
1265 The returned object conforms to the ``imanifestdict`` interface."""
1260
1266
1261 @abc.abstractmethod
1267 @abc.abstractmethod
1262 def readfast(self, shallow: bool = False):
1268 def readfast(self, shallow: bool = False):
1263 """Calls either ``read()`` or ``readdelta()``.
1269 """Calls either ``read()`` or ``readdelta()``.
1264
1270
1265 The faster of the two options is called.
1271 The faster of the two options is called.
1266 """
1272 """
1267
1273
1268 @abc.abstractmethod
1274 @abc.abstractmethod
1269 def find(self, key: bytes) -> tuple[bytes, bytes]:
1275 def find(self, key: bytes) -> tuple[bytes, bytes]:
1270 """Calls ``self.read().find(key)``.
1276 """Calls ``self.read().find(key)``.
1271
1277
1272 Returns a 2-tuple of ``(node, flags)`` or raises ``KeyError``.
1278 Returns a 2-tuple of ``(node, flags)`` or raises ``KeyError``.
1273 """
1279 """
1274
1280
1275
1281
1276 class imanifestrevisionwritable(imanifestrevisionbase):
1282 class imanifestrevisionwritable(imanifestrevisionbase):
1277 """Interface representing a manifest revision that can be committed."""
1283 """Interface representing a manifest revision that can be committed."""
1278
1284
1279 def write(
1285 def write(
1280 self, transaction, linkrev, p1node, p2node, added, removed, match=None
1286 self, transaction, linkrev, p1node, p2node, added, removed, match=None
1281 ):
1287 ):
1282 """Add this revision to storage.
1288 """Add this revision to storage.
1283
1289
1284 Takes a transaction object, the changeset revision number it will
1290 Takes a transaction object, the changeset revision number it will
1285 be associated with, its parent nodes, and lists of added and
1291 be associated with, its parent nodes, and lists of added and
1286 removed paths.
1292 removed paths.
1287
1293
1288 If match is provided, storage can choose not to inspect or write out
1294 If match is provided, storage can choose not to inspect or write out
1289 items that do not match. Storage is still required to be able to provide
1295 items that do not match. Storage is still required to be able to provide
1290 the full manifest in the future for any directories written (these
1296 the full manifest in the future for any directories written (these
1291 manifests should not be "narrowed on disk").
1297 manifests should not be "narrowed on disk").
1292
1298
1293 Returns the binary node of the created revision.
1299 Returns the binary node of the created revision.
1294 """
1300 """
1295
1301
1296
1302
1297 class imanifeststorage(Protocol):
1303 class imanifeststorage(Protocol):
1298 """Storage interface for manifest data."""
1304 """Storage interface for manifest data."""
1299
1305
1300 nodeconstants = interfaceutil.Attribute(
1306 nodeconstants: NodeConstants
1301 """nodeconstants used by the current repository."""
1307 """nodeconstants used by the current repository."""
1302 )
1308
1303
1309 tree: bytes
1304 tree = interfaceutil.Attribute(
1310 """The path to the directory this manifest tracks.
1305 """The path to the directory this manifest tracks.
1311
1306
1312 The empty bytestring represents the root manifest.
1307 The empty bytestring represents the root manifest.
1313 """
1308 """
1314
1309 )
1315 index: ifilerevisionssequence
1310
1316 """An ``ifilerevisionssequence`` instance."""
1311 index = interfaceutil.Attribute(
1317
1312 """An ``ifilerevisionssequence`` instance."""
1318 opener: Vfs
1313 )
1319 """VFS opener to use to access underlying files used for storage.
1314
1320
1315 opener = interfaceutil.Attribute(
1321 TODO this is revlog specific and should not be exposed.
1316 """VFS opener to use to access underlying files used for storage.
1322 """
1317
1323
1318 TODO this is revlog specific and should not be exposed.
1324 # TODO: finish type hints
1319 """
1325 fulltextcache: dict
1320 )
1326 """Dict with cache of fulltexts.
1321
1327
1322 fulltextcache = interfaceutil.Attribute(
1328 TODO this doesn't feel appropriate for the storage interface.
1323 """Dict with cache of fulltexts.
1329 """
1324
1325 TODO this doesn't feel appropriate for the storage interface.
1326 """
1327 )
1328
1330
1329 def __len__(self):
1331 def __len__(self):
1330 """Obtain the number of revisions stored for this manifest."""
1332 """Obtain the number of revisions stored for this manifest."""
1331
1333
1332 def __iter__(self):
1334 def __iter__(self):
1333 """Iterate over revision numbers for this manifest."""
1335 """Iterate over revision numbers for this manifest."""
1334
1336
1335 def rev(self, node):
1337 def rev(self, node):
1336 """Obtain the revision number given a binary node.
1338 """Obtain the revision number given a binary node.
1337
1339
1338 Raises ``error.LookupError`` if the node is not known.
1340 Raises ``error.LookupError`` if the node is not known.
1339 """
1341 """
1340
1342
1341 def node(self, rev):
1343 def node(self, rev):
1342 """Obtain the node value given a revision number.
1344 """Obtain the node value given a revision number.
1343
1345
1344 Raises ``error.LookupError`` if the revision is not known.
1346 Raises ``error.LookupError`` if the revision is not known.
1345 """
1347 """
1346
1348
1347 def lookup(self, value):
1349 def lookup(self, value):
1348 """Attempt to resolve a value to a node.
1350 """Attempt to resolve a value to a node.
1349
1351
1350 Value can be a binary node, hex node, revision number, or a bytes
1352 Value can be a binary node, hex node, revision number, or a bytes
1351 that can be converted to an integer.
1353 that can be converted to an integer.
1352
1354
1353 Raises ``error.LookupError`` if a ndoe could not be resolved.
1355 Raises ``error.LookupError`` if a ndoe could not be resolved.
1354 """
1356 """
1355
1357
1356 def parents(self, node):
1358 def parents(self, node):
1357 """Returns a 2-tuple of parent nodes for a node.
1359 """Returns a 2-tuple of parent nodes for a node.
1358
1360
1359 Values will be ``nullid`` if the parent is empty.
1361 Values will be ``nullid`` if the parent is empty.
1360 """
1362 """
1361
1363
1362 def parentrevs(self, rev):
1364 def parentrevs(self, rev):
1363 """Like parents() but operates on revision numbers."""
1365 """Like parents() but operates on revision numbers."""
1364
1366
1365 def linkrev(self, rev):
1367 def linkrev(self, rev):
1366 """Obtain the changeset revision number a revision is linked to."""
1368 """Obtain the changeset revision number a revision is linked to."""
1367
1369
1368 def revision(self, node):
1370 def revision(self, node):
1369 """Obtain fulltext data for a node."""
1371 """Obtain fulltext data for a node."""
1370
1372
1371 def rawdata(self, node):
1373 def rawdata(self, node):
1372 """Obtain raw data for a node."""
1374 """Obtain raw data for a node."""
1373
1375
1374 def revdiff(self, rev1, rev2):
1376 def revdiff(self, rev1, rev2):
1375 """Obtain a delta between two revision numbers.
1377 """Obtain a delta between two revision numbers.
1376
1378
1377 The returned data is the result of ``bdiff.bdiff()`` on the raw
1379 The returned data is the result of ``bdiff.bdiff()`` on the raw
1378 revision data.
1380 revision data.
1379 """
1381 """
1380
1382
1381 def cmp(self, node, fulltext):
1383 def cmp(self, node, fulltext):
1382 """Compare fulltext to another revision.
1384 """Compare fulltext to another revision.
1383
1385
1384 Returns True if the fulltext is different from what is stored.
1386 Returns True if the fulltext is different from what is stored.
1385 """
1387 """
1386
1388
1387 def emitrevisions(
1389 def emitrevisions(
1388 self,
1390 self,
1389 nodes,
1391 nodes,
1390 nodesorder=None,
1392 nodesorder=None,
1391 revisiondata=False,
1393 revisiondata=False,
1392 assumehaveparentrevisions=False,
1394 assumehaveparentrevisions=False,
1393 ):
1395 ):
1394 """Produce ``irevisiondelta`` describing revisions.
1396 """Produce ``irevisiondelta`` describing revisions.
1395
1397
1396 See the documentation for ``ifiledata`` for more.
1398 See the documentation for ``ifiledata`` for more.
1397 """
1399 """
1398
1400
1399 def addgroup(
1401 def addgroup(
1400 self,
1402 self,
1401 deltas,
1403 deltas,
1402 linkmapper,
1404 linkmapper,
1403 transaction,
1405 transaction,
1404 addrevisioncb=None,
1406 addrevisioncb=None,
1405 duplicaterevisioncb=None,
1407 duplicaterevisioncb=None,
1406 ):
1408 ):
1407 """Process a series of deltas for storage.
1409 """Process a series of deltas for storage.
1408
1410
1409 See the documentation in ``ifilemutation`` for more.
1411 See the documentation in ``ifilemutation`` for more.
1410 """
1412 """
1411
1413
1412 def rawsize(self, rev):
1414 def rawsize(self, rev):
1413 """Obtain the size of tracked data.
1415 """Obtain the size of tracked data.
1414
1416
1415 Is equivalent to ``len(m.rawdata(node))``.
1417 Is equivalent to ``len(m.rawdata(node))``.
1416
1418
1417 TODO this method is only used by upgrade code and may be removed.
1419 TODO this method is only used by upgrade code and may be removed.
1418 """
1420 """
1419
1421
1420 def getstrippoint(self, minlink):
1422 def getstrippoint(self, minlink):
1421 """Find minimum revision that must be stripped to strip a linkrev.
1423 """Find minimum revision that must be stripped to strip a linkrev.
1422
1424
1423 See the documentation in ``ifilemutation`` for more.
1425 See the documentation in ``ifilemutation`` for more.
1424 """
1426 """
1425
1427
1426 def strip(self, minlink, transaction):
1428 def strip(self, minlink, transaction):
1427 """Remove storage of items starting at a linkrev.
1429 """Remove storage of items starting at a linkrev.
1428
1430
1429 See the documentation in ``ifilemutation`` for more.
1431 See the documentation in ``ifilemutation`` for more.
1430 """
1432 """
1431
1433
1432 def checksize(self):
1434 def checksize(self):
1433 """Obtain the expected sizes of backing files.
1435 """Obtain the expected sizes of backing files.
1434
1436
1435 TODO this is used by verify and it should not be part of the interface.
1437 TODO this is used by verify and it should not be part of the interface.
1436 """
1438 """
1437
1439
1438 def files(self):
1440 def files(self):
1439 """Obtain paths that are backing storage for this manifest.
1441 """Obtain paths that are backing storage for this manifest.
1440
1442
1441 TODO this is used by verify and there should probably be a better API
1443 TODO this is used by verify and there should probably be a better API
1442 for this functionality.
1444 for this functionality.
1443 """
1445 """
1444
1446
1445 def deltaparent(self, rev):
1447 def deltaparent(self, rev):
1446 """Obtain the revision that a revision is delta'd against.
1448 """Obtain the revision that a revision is delta'd against.
1447
1449
1448 TODO delta encoding is an implementation detail of storage and should
1450 TODO delta encoding is an implementation detail of storage and should
1449 not be exposed to the storage interface.
1451 not be exposed to the storage interface.
1450 """
1452 """
1451
1453
1452 def clone(self, tr, dest, **kwargs):
1454 def clone(self, tr, dest, **kwargs):
1453 """Clone this instance to another."""
1455 """Clone this instance to another."""
1454
1456
1455 def clearcaches(self, clear_persisted_data=False):
1457 def clearcaches(self, clear_persisted_data=False):
1456 """Clear any caches associated with this instance."""
1458 """Clear any caches associated with this instance."""
1457
1459
1458 def dirlog(self, d):
1460 def dirlog(self, d):
1459 """Obtain a manifest storage instance for a tree."""
1461 """Obtain a manifest storage instance for a tree."""
1460
1462
1461 def add(
1463 def add(
1462 self,
1464 self,
1463 m,
1465 m,
1464 transaction,
1466 transaction,
1465 link,
1467 link,
1466 p1,
1468 p1,
1467 p2,
1469 p2,
1468 added,
1470 added,
1469 removed,
1471 removed,
1470 readtree=None,
1472 readtree=None,
1471 match=None,
1473 match=None,
1472 ):
1474 ):
1473 """Add a revision to storage.
1475 """Add a revision to storage.
1474
1476
1475 ``m`` is an object conforming to ``imanifestdict``.
1477 ``m`` is an object conforming to ``imanifestdict``.
1476
1478
1477 ``link`` is the linkrev revision number.
1479 ``link`` is the linkrev revision number.
1478
1480
1479 ``p1`` and ``p2`` are the parent revision numbers.
1481 ``p1`` and ``p2`` are the parent revision numbers.
1480
1482
1481 ``added`` and ``removed`` are iterables of added and removed paths,
1483 ``added`` and ``removed`` are iterables of added and removed paths,
1482 respectively.
1484 respectively.
1483
1485
1484 ``readtree`` is a function that can be used to read the child tree(s)
1486 ``readtree`` is a function that can be used to read the child tree(s)
1485 when recursively writing the full tree structure when using
1487 when recursively writing the full tree structure when using
1486 treemanifets.
1488 treemanifets.
1487
1489
1488 ``match`` is a matcher that can be used to hint to storage that not all
1490 ``match`` is a matcher that can be used to hint to storage that not all
1489 paths must be inspected; this is an optimization and can be safely
1491 paths must be inspected; this is an optimization and can be safely
1490 ignored. Note that the storage must still be able to reproduce a full
1492 ignored. Note that the storage must still be able to reproduce a full
1491 manifest including files that did not match.
1493 manifest including files that did not match.
1492 """
1494 """
1493
1495
1494 def storageinfo(
1496 def storageinfo(
1495 self,
1497 self,
1496 exclusivefiles=False,
1498 exclusivefiles=False,
1497 sharedfiles=False,
1499 sharedfiles=False,
1498 revisionscount=False,
1500 revisionscount=False,
1499 trackedsize=False,
1501 trackedsize=False,
1500 storedsize=False,
1502 storedsize=False,
1501 ):
1503 ):
1502 """Obtain information about storage for this manifest's data.
1504 """Obtain information about storage for this manifest's data.
1503
1505
1504 See ``ifilestorage.storageinfo()`` for a description of this method.
1506 See ``ifilestorage.storageinfo()`` for a description of this method.
1505 This one behaves the same way, except for manifest data.
1507 This one behaves the same way, except for manifest data.
1506 """
1508 """
1507
1509
1508 def get_revlog(self):
1510 def get_revlog(self):
1509 """return an actual revlog instance if any
1511 """return an actual revlog instance if any
1510
1512
1511 This exist because a lot of code leverage the fact the underlying
1513 This exist because a lot of code leverage the fact the underlying
1512 storage is a revlog for optimization, so giving simple way to access
1514 storage is a revlog for optimization, so giving simple way to access
1513 the revlog instance helps such code.
1515 the revlog instance helps such code.
1514 """
1516 """
1515
1517
1516
1518
1517 class imanifestlog(Protocol):
1519 class imanifestlog(Protocol):
1518 """Interface representing a collection of manifest snapshots.
1520 """Interface representing a collection of manifest snapshots.
1519
1521
1520 Represents the root manifest in a repository.
1522 Represents the root manifest in a repository.
1521
1523
1522 Also serves as a means to access nested tree manifests and to cache
1524 Also serves as a means to access nested tree manifests and to cache
1523 tree manifests.
1525 tree manifests.
1524 """
1526 """
1525
1527
1526 nodeconstants = interfaceutil.Attribute(
1528 nodeconstants = interfaceutil.Attribute(
1527 """nodeconstants used by the current repository."""
1529 """nodeconstants used by the current repository."""
1528 )
1530 )
1529
1531
1530 narrowed = interfaceutil.Attribute(
1532 narrowed = interfaceutil.Attribute(
1531 """True, is the manifest is narrowed by a matcher"""
1533 """True, is the manifest is narrowed by a matcher"""
1532 )
1534 )
1533
1535
1534 def __getitem__(self, node):
1536 def __getitem__(self, node):
1535 """Obtain a manifest instance for a given binary node.
1537 """Obtain a manifest instance for a given binary node.
1536
1538
1537 Equivalent to calling ``self.get('', node)``.
1539 Equivalent to calling ``self.get('', node)``.
1538
1540
1539 The returned object conforms to the ``imanifestrevisionstored``
1541 The returned object conforms to the ``imanifestrevisionstored``
1540 interface.
1542 interface.
1541 """
1543 """
1542
1544
1543 def get(self, tree, node, verify=True):
1545 def get(self, tree, node, verify=True):
1544 """Retrieve the manifest instance for a given directory and binary node.
1546 """Retrieve the manifest instance for a given directory and binary node.
1545
1547
1546 ``node`` always refers to the node of the root manifest (which will be
1548 ``node`` always refers to the node of the root manifest (which will be
1547 the only manifest if flat manifests are being used).
1549 the only manifest if flat manifests are being used).
1548
1550
1549 If ``tree`` is the empty string, the root manifest is returned.
1551 If ``tree`` is the empty string, the root manifest is returned.
1550 Otherwise the manifest for the specified directory will be returned
1552 Otherwise the manifest for the specified directory will be returned
1551 (requires tree manifests).
1553 (requires tree manifests).
1552
1554
1553 If ``verify`` is True, ``LookupError`` is raised if the node is not
1555 If ``verify`` is True, ``LookupError`` is raised if the node is not
1554 known.
1556 known.
1555
1557
1556 The returned object conforms to the ``imanifestrevisionstored``
1558 The returned object conforms to the ``imanifestrevisionstored``
1557 interface.
1559 interface.
1558 """
1560 """
1559
1561
1560 def getstorage(self, tree):
1562 def getstorage(self, tree):
1561 """Retrieve an interface to storage for a particular tree.
1563 """Retrieve an interface to storage for a particular tree.
1562
1564
1563 If ``tree`` is the empty bytestring, storage for the root manifest will
1565 If ``tree`` is the empty bytestring, storage for the root manifest will
1564 be returned. Otherwise storage for a tree manifest is returned.
1566 be returned. Otherwise storage for a tree manifest is returned.
1565
1567
1566 TODO formalize interface for returned object.
1568 TODO formalize interface for returned object.
1567 """
1569 """
1568
1570
1569 def clearcaches(self, clear_persisted_data: bool = False) -> None:
1571 def clearcaches(self, clear_persisted_data: bool = False) -> None:
1570 """Clear caches associated with this collection."""
1572 """Clear caches associated with this collection."""
1571
1573
1572 def rev(self, node):
1574 def rev(self, node):
1573 """Obtain the revision number for a binary node.
1575 """Obtain the revision number for a binary node.
1574
1576
1575 Raises ``error.LookupError`` if the node is not known.
1577 Raises ``error.LookupError`` if the node is not known.
1576 """
1578 """
1577
1579
1578 def update_caches(self, transaction):
1580 def update_caches(self, transaction):
1579 """update whatever cache are relevant for the used storage."""
1581 """update whatever cache are relevant for the used storage."""
1580
1582
1581
1583
1582 class ilocalrepositoryfilestorage(Protocol):
1584 class ilocalrepositoryfilestorage(Protocol):
1583 """Local repository sub-interface providing access to tracked file storage.
1585 """Local repository sub-interface providing access to tracked file storage.
1584
1586
1585 This interface defines how a repository accesses storage for a single
1587 This interface defines how a repository accesses storage for a single
1586 tracked file path.
1588 tracked file path.
1587 """
1589 """
1588
1590
1589 def file(self, f):
1591 def file(self, f):
1590 """Obtain a filelog for a tracked path.
1592 """Obtain a filelog for a tracked path.
1591
1593
1592 The returned type conforms to the ``ifilestorage`` interface.
1594 The returned type conforms to the ``ifilestorage`` interface.
1593 """
1595 """
1594
1596
1595
1597
1596 class ilocalrepositorymain(Protocol):
1598 class ilocalrepositorymain(Protocol):
1597 """Main interface for local repositories.
1599 """Main interface for local repositories.
1598
1600
1599 This currently captures the reality of things - not how things should be.
1601 This currently captures the reality of things - not how things should be.
1600 """
1602 """
1601
1603
1602 nodeconstants = interfaceutil.Attribute(
1604 nodeconstants = interfaceutil.Attribute(
1603 """Constant nodes matching the hash function used by the repository."""
1605 """Constant nodes matching the hash function used by the repository."""
1604 )
1606 )
1605 nullid = interfaceutil.Attribute(
1607 nullid = interfaceutil.Attribute(
1606 """null revision for the hash function used by the repository."""
1608 """null revision for the hash function used by the repository."""
1607 )
1609 )
1608
1610
1609 supported = interfaceutil.Attribute(
1611 supported = interfaceutil.Attribute(
1610 """Set of requirements that this repo is capable of opening."""
1612 """Set of requirements that this repo is capable of opening."""
1611 )
1613 )
1612
1614
1613 requirements = interfaceutil.Attribute(
1615 requirements = interfaceutil.Attribute(
1614 """Set of requirements this repo uses."""
1616 """Set of requirements this repo uses."""
1615 )
1617 )
1616
1618
1617 features = interfaceutil.Attribute(
1619 features = interfaceutil.Attribute(
1618 """Set of "features" this repository supports.
1620 """Set of "features" this repository supports.
1619
1621
1620 A "feature" is a loosely-defined term. It can refer to a feature
1622 A "feature" is a loosely-defined term. It can refer to a feature
1621 in the classical sense or can describe an implementation detail
1623 in the classical sense or can describe an implementation detail
1622 of the repository. For example, a ``readonly`` feature may denote
1624 of the repository. For example, a ``readonly`` feature may denote
1623 the repository as read-only. Or a ``revlogfilestore`` feature may
1625 the repository as read-only. Or a ``revlogfilestore`` feature may
1624 denote that the repository is using revlogs for file storage.
1626 denote that the repository is using revlogs for file storage.
1625
1627
1626 The intent of features is to provide a machine-queryable mechanism
1628 The intent of features is to provide a machine-queryable mechanism
1627 for repo consumers to test for various repository characteristics.
1629 for repo consumers to test for various repository characteristics.
1628
1630
1629 Features are similar to ``requirements``. The main difference is that
1631 Features are similar to ``requirements``. The main difference is that
1630 requirements are stored on-disk and represent requirements to open the
1632 requirements are stored on-disk and represent requirements to open the
1631 repository. Features are more run-time capabilities of the repository
1633 repository. Features are more run-time capabilities of the repository
1632 and more granular capabilities (which may be derived from requirements).
1634 and more granular capabilities (which may be derived from requirements).
1633 """
1635 """
1634 )
1636 )
1635
1637
1636 filtername = interfaceutil.Attribute(
1638 filtername = interfaceutil.Attribute(
1637 """Name of the repoview that is active on this repo."""
1639 """Name of the repoview that is active on this repo."""
1638 )
1640 )
1639
1641
1640 vfs_map = interfaceutil.Attribute(
1642 vfs_map = interfaceutil.Attribute(
1641 """a bytes-key β†’ vfs mapping used by transaction and others"""
1643 """a bytes-key β†’ vfs mapping used by transaction and others"""
1642 )
1644 )
1643
1645
1644 wvfs = interfaceutil.Attribute(
1646 wvfs = interfaceutil.Attribute(
1645 """VFS used to access the working directory."""
1647 """VFS used to access the working directory."""
1646 )
1648 )
1647
1649
1648 vfs = interfaceutil.Attribute(
1650 vfs = interfaceutil.Attribute(
1649 """VFS rooted at the .hg directory.
1651 """VFS rooted at the .hg directory.
1650
1652
1651 Used to access repository data not in the store.
1653 Used to access repository data not in the store.
1652 """
1654 """
1653 )
1655 )
1654
1656
1655 svfs = interfaceutil.Attribute(
1657 svfs = interfaceutil.Attribute(
1656 """VFS rooted at the store.
1658 """VFS rooted at the store.
1657
1659
1658 Used to access repository data in the store. Typically .hg/store.
1660 Used to access repository data in the store. Typically .hg/store.
1659 But can point elsewhere if the store is shared.
1661 But can point elsewhere if the store is shared.
1660 """
1662 """
1661 )
1663 )
1662
1664
1663 root = interfaceutil.Attribute(
1665 root = interfaceutil.Attribute(
1664 """Path to the root of the working directory."""
1666 """Path to the root of the working directory."""
1665 )
1667 )
1666
1668
1667 path = interfaceutil.Attribute("""Path to the .hg directory.""")
1669 path = interfaceutil.Attribute("""Path to the .hg directory.""")
1668
1670
1669 origroot = interfaceutil.Attribute(
1671 origroot = interfaceutil.Attribute(
1670 """The filesystem path that was used to construct the repo."""
1672 """The filesystem path that was used to construct the repo."""
1671 )
1673 )
1672
1674
1673 auditor = interfaceutil.Attribute(
1675 auditor = interfaceutil.Attribute(
1674 """A pathauditor for the working directory.
1676 """A pathauditor for the working directory.
1675
1677
1676 This checks if a path refers to a nested repository.
1678 This checks if a path refers to a nested repository.
1677
1679
1678 Operates on the filesystem.
1680 Operates on the filesystem.
1679 """
1681 """
1680 )
1682 )
1681
1683
1682 nofsauditor = interfaceutil.Attribute(
1684 nofsauditor = interfaceutil.Attribute(
1683 """A pathauditor for the working directory.
1685 """A pathauditor for the working directory.
1684
1686
1685 This is like ``auditor`` except it doesn't do filesystem checks.
1687 This is like ``auditor`` except it doesn't do filesystem checks.
1686 """
1688 """
1687 )
1689 )
1688
1690
1689 baseui = interfaceutil.Attribute(
1691 baseui = interfaceutil.Attribute(
1690 """Original ui instance passed into constructor."""
1692 """Original ui instance passed into constructor."""
1691 )
1693 )
1692
1694
1693 ui = interfaceutil.Attribute("""Main ui instance for this instance.""")
1695 ui = interfaceutil.Attribute("""Main ui instance for this instance.""")
1694
1696
1695 sharedpath = interfaceutil.Attribute(
1697 sharedpath = interfaceutil.Attribute(
1696 """Path to the .hg directory of the repo this repo was shared from."""
1698 """Path to the .hg directory of the repo this repo was shared from."""
1697 )
1699 )
1698
1700
1699 store = interfaceutil.Attribute("""A store instance.""")
1701 store = interfaceutil.Attribute("""A store instance.""")
1700
1702
1701 spath = interfaceutil.Attribute("""Path to the store.""")
1703 spath = interfaceutil.Attribute("""Path to the store.""")
1702
1704
1703 sjoin = interfaceutil.Attribute("""Alias to self.store.join.""")
1705 sjoin = interfaceutil.Attribute("""Alias to self.store.join.""")
1704
1706
1705 cachevfs = interfaceutil.Attribute(
1707 cachevfs = interfaceutil.Attribute(
1706 """A VFS used to access the cache directory.
1708 """A VFS used to access the cache directory.
1707
1709
1708 Typically .hg/cache.
1710 Typically .hg/cache.
1709 """
1711 """
1710 )
1712 )
1711
1713
1712 wcachevfs = interfaceutil.Attribute(
1714 wcachevfs = interfaceutil.Attribute(
1713 """A VFS used to access the cache directory dedicated to working copy
1715 """A VFS used to access the cache directory dedicated to working copy
1714
1716
1715 Typically .hg/wcache.
1717 Typically .hg/wcache.
1716 """
1718 """
1717 )
1719 )
1718
1720
1719 filteredrevcache = interfaceutil.Attribute(
1721 filteredrevcache = interfaceutil.Attribute(
1720 """Holds sets of revisions to be filtered."""
1722 """Holds sets of revisions to be filtered."""
1721 )
1723 )
1722
1724
1723 names = interfaceutil.Attribute("""A ``namespaces`` instance.""")
1725 names = interfaceutil.Attribute("""A ``namespaces`` instance.""")
1724
1726
1725 filecopiesmode = interfaceutil.Attribute(
1727 filecopiesmode = interfaceutil.Attribute(
1726 """The way files copies should be dealt with in this repo."""
1728 """The way files copies should be dealt with in this repo."""
1727 )
1729 )
1728
1730
1729 def close(self):
1731 def close(self):
1730 """Close the handle on this repository."""
1732 """Close the handle on this repository."""
1731
1733
1732 def peer(self, path=None):
1734 def peer(self, path=None):
1733 """Obtain an object conforming to the ``peer`` interface."""
1735 """Obtain an object conforming to the ``peer`` interface."""
1734
1736
1735 def unfiltered(self):
1737 def unfiltered(self):
1736 """Obtain an unfiltered/raw view of this repo."""
1738 """Obtain an unfiltered/raw view of this repo."""
1737
1739
1738 def filtered(self, name, visibilityexceptions=None):
1740 def filtered(self, name, visibilityexceptions=None):
1739 """Obtain a named view of this repository."""
1741 """Obtain a named view of this repository."""
1740
1742
1741 obsstore = interfaceutil.Attribute("""A store of obsolescence data.""")
1743 obsstore = interfaceutil.Attribute("""A store of obsolescence data.""")
1742
1744
1743 changelog = interfaceutil.Attribute("""A handle on the changelog revlog.""")
1745 changelog = interfaceutil.Attribute("""A handle on the changelog revlog.""")
1744
1746
1745 manifestlog = interfaceutil.Attribute(
1747 manifestlog = interfaceutil.Attribute(
1746 """An instance conforming to the ``imanifestlog`` interface.
1748 """An instance conforming to the ``imanifestlog`` interface.
1747
1749
1748 Provides access to manifests for the repository.
1750 Provides access to manifests for the repository.
1749 """
1751 """
1750 )
1752 )
1751
1753
1752 dirstate = interfaceutil.Attribute("""Working directory state.""")
1754 dirstate = interfaceutil.Attribute("""Working directory state.""")
1753
1755
1754 narrowpats = interfaceutil.Attribute(
1756 narrowpats = interfaceutil.Attribute(
1755 """Matcher patterns for this repository's narrowspec."""
1757 """Matcher patterns for this repository's narrowspec."""
1756 )
1758 )
1757
1759
1758 def narrowmatch(self, match=None, includeexact=False):
1760 def narrowmatch(self, match=None, includeexact=False):
1759 """Obtain a matcher for the narrowspec."""
1761 """Obtain a matcher for the narrowspec."""
1760
1762
1761 def setnarrowpats(self, newincludes, newexcludes):
1763 def setnarrowpats(self, newincludes, newexcludes):
1762 """Define the narrowspec for this repository."""
1764 """Define the narrowspec for this repository."""
1763
1765
1764 def __getitem__(self, changeid):
1766 def __getitem__(self, changeid):
1765 """Try to resolve a changectx."""
1767 """Try to resolve a changectx."""
1766
1768
1767 def __contains__(self, changeid):
1769 def __contains__(self, changeid):
1768 """Whether a changeset exists."""
1770 """Whether a changeset exists."""
1769
1771
1770 def __nonzero__(self):
1772 def __nonzero__(self):
1771 """Always returns True."""
1773 """Always returns True."""
1772 return True
1774 return True
1773
1775
1774 __bool__ = __nonzero__
1776 __bool__ = __nonzero__
1775
1777
1776 def __len__(self):
1778 def __len__(self):
1777 """Returns the number of changesets in the repo."""
1779 """Returns the number of changesets in the repo."""
1778
1780
1779 def __iter__(self):
1781 def __iter__(self):
1780 """Iterate over revisions in the changelog."""
1782 """Iterate over revisions in the changelog."""
1781
1783
1782 def revs(self, expr, *args):
1784 def revs(self, expr, *args):
1783 """Evaluate a revset.
1785 """Evaluate a revset.
1784
1786
1785 Emits revisions.
1787 Emits revisions.
1786 """
1788 """
1787
1789
1788 def set(self, expr, *args):
1790 def set(self, expr, *args):
1789 """Evaluate a revset.
1791 """Evaluate a revset.
1790
1792
1791 Emits changectx instances.
1793 Emits changectx instances.
1792 """
1794 """
1793
1795
1794 def anyrevs(self, specs, user=False, localalias=None):
1796 def anyrevs(self, specs, user=False, localalias=None):
1795 """Find revisions matching one of the given revsets."""
1797 """Find revisions matching one of the given revsets."""
1796
1798
1797 def url(self):
1799 def url(self):
1798 """Returns a string representing the location of this repo."""
1800 """Returns a string representing the location of this repo."""
1799
1801
1800 def hook(self, name, throw=False, **args):
1802 def hook(self, name, throw=False, **args):
1801 """Call a hook."""
1803 """Call a hook."""
1802
1804
1803 def tags(self):
1805 def tags(self):
1804 """Return a mapping of tag to node."""
1806 """Return a mapping of tag to node."""
1805
1807
1806 def tagtype(self, tagname):
1808 def tagtype(self, tagname):
1807 """Return the type of a given tag."""
1809 """Return the type of a given tag."""
1808
1810
1809 def tagslist(self):
1811 def tagslist(self):
1810 """Return a list of tags ordered by revision."""
1812 """Return a list of tags ordered by revision."""
1811
1813
1812 def nodetags(self, node):
1814 def nodetags(self, node):
1813 """Return the tags associated with a node."""
1815 """Return the tags associated with a node."""
1814
1816
1815 def nodebookmarks(self, node):
1817 def nodebookmarks(self, node):
1816 """Return the list of bookmarks pointing to the specified node."""
1818 """Return the list of bookmarks pointing to the specified node."""
1817
1819
1818 def branchmap(self):
1820 def branchmap(self):
1819 """Return a mapping of branch to heads in that branch."""
1821 """Return a mapping of branch to heads in that branch."""
1820
1822
1821 def revbranchcache(self):
1823 def revbranchcache(self):
1822 pass
1824 pass
1823
1825
1824 def register_changeset(self, rev, changelogrevision):
1826 def register_changeset(self, rev, changelogrevision):
1825 """Extension point for caches for new nodes.
1827 """Extension point for caches for new nodes.
1826
1828
1827 Multiple consumers are expected to need parts of the changelogrevision,
1829 Multiple consumers are expected to need parts of the changelogrevision,
1828 so it is provided as optimization to avoid duplicate lookups. A simple
1830 so it is provided as optimization to avoid duplicate lookups. A simple
1829 cache would be fragile when other revisions are accessed, too."""
1831 cache would be fragile when other revisions are accessed, too."""
1830 pass
1832 pass
1831
1833
1832 def branchtip(self, branchtip, ignoremissing=False):
1834 def branchtip(self, branchtip, ignoremissing=False):
1833 """Return the tip node for a given branch."""
1835 """Return the tip node for a given branch."""
1834
1836
1835 def lookup(self, key):
1837 def lookup(self, key):
1836 """Resolve the node for a revision."""
1838 """Resolve the node for a revision."""
1837
1839
1838 def lookupbranch(self, key):
1840 def lookupbranch(self, key):
1839 """Look up the branch name of the given revision or branch name."""
1841 """Look up the branch name of the given revision or branch name."""
1840
1842
1841 def known(self, nodes):
1843 def known(self, nodes):
1842 """Determine whether a series of nodes is known.
1844 """Determine whether a series of nodes is known.
1843
1845
1844 Returns a list of bools.
1846 Returns a list of bools.
1845 """
1847 """
1846
1848
1847 def local(self):
1849 def local(self):
1848 """Whether the repository is local."""
1850 """Whether the repository is local."""
1849 return True
1851 return True
1850
1852
1851 def publishing(self):
1853 def publishing(self):
1852 """Whether the repository is a publishing repository."""
1854 """Whether the repository is a publishing repository."""
1853
1855
1854 def cancopy(self):
1856 def cancopy(self):
1855 pass
1857 pass
1856
1858
1857 def shared(self):
1859 def shared(self):
1858 """The type of shared repository or None."""
1860 """The type of shared repository or None."""
1859
1861
1860 def wjoin(self, f, *insidef):
1862 def wjoin(self, f, *insidef):
1861 """Calls self.vfs.reljoin(self.root, f, *insidef)"""
1863 """Calls self.vfs.reljoin(self.root, f, *insidef)"""
1862
1864
1863 def setparents(self, p1, p2):
1865 def setparents(self, p1, p2):
1864 """Set the parent nodes of the working directory."""
1866 """Set the parent nodes of the working directory."""
1865
1867
1866 def filectx(self, path, changeid=None, fileid=None):
1868 def filectx(self, path, changeid=None, fileid=None):
1867 """Obtain a filectx for the given file revision."""
1869 """Obtain a filectx for the given file revision."""
1868
1870
1869 def getcwd(self):
1871 def getcwd(self):
1870 """Obtain the current working directory from the dirstate."""
1872 """Obtain the current working directory from the dirstate."""
1871
1873
1872 def pathto(self, f, cwd=None):
1874 def pathto(self, f, cwd=None):
1873 """Obtain the relative path to a file."""
1875 """Obtain the relative path to a file."""
1874
1876
1875 def adddatafilter(self, name, fltr):
1877 def adddatafilter(self, name, fltr):
1876 pass
1878 pass
1877
1879
1878 def wread(self, filename):
1880 def wread(self, filename):
1879 """Read a file from wvfs, using data filters."""
1881 """Read a file from wvfs, using data filters."""
1880
1882
1881 def wwrite(self, filename, data, flags, backgroundclose=False, **kwargs):
1883 def wwrite(self, filename, data, flags, backgroundclose=False, **kwargs):
1882 """Write data to a file in the wvfs, using data filters."""
1884 """Write data to a file in the wvfs, using data filters."""
1883
1885
1884 def wwritedata(self, filename, data):
1886 def wwritedata(self, filename, data):
1885 """Resolve data for writing to the wvfs, using data filters."""
1887 """Resolve data for writing to the wvfs, using data filters."""
1886
1888
1887 def currenttransaction(self):
1889 def currenttransaction(self):
1888 """Obtain the current transaction instance or None."""
1890 """Obtain the current transaction instance or None."""
1889
1891
1890 def transaction(self, desc, report=None):
1892 def transaction(self, desc, report=None):
1891 """Open a new transaction to write to the repository."""
1893 """Open a new transaction to write to the repository."""
1892
1894
1893 def undofiles(self):
1895 def undofiles(self):
1894 """Returns a list of (vfs, path) for files to undo transactions."""
1896 """Returns a list of (vfs, path) for files to undo transactions."""
1895
1897
1896 def recover(self):
1898 def recover(self):
1897 """Roll back an interrupted transaction."""
1899 """Roll back an interrupted transaction."""
1898
1900
1899 def rollback(self, dryrun=False, force=False):
1901 def rollback(self, dryrun=False, force=False):
1900 """Undo the last transaction.
1902 """Undo the last transaction.
1901
1903
1902 DANGEROUS.
1904 DANGEROUS.
1903 """
1905 """
1904
1906
1905 def updatecaches(self, tr=None, full=False, caches=None):
1907 def updatecaches(self, tr=None, full=False, caches=None):
1906 """Warm repo caches."""
1908 """Warm repo caches."""
1907
1909
1908 def invalidatecaches(self):
1910 def invalidatecaches(self):
1909 """Invalidate cached data due to the repository mutating."""
1911 """Invalidate cached data due to the repository mutating."""
1910
1912
1911 def invalidatevolatilesets(self):
1913 def invalidatevolatilesets(self):
1912 pass
1914 pass
1913
1915
1914 def invalidatedirstate(self):
1916 def invalidatedirstate(self):
1915 """Invalidate the dirstate."""
1917 """Invalidate the dirstate."""
1916
1918
1917 def invalidate(self, clearfilecache=False):
1919 def invalidate(self, clearfilecache=False):
1918 pass
1920 pass
1919
1921
1920 def invalidateall(self):
1922 def invalidateall(self):
1921 pass
1923 pass
1922
1924
1923 def lock(self, wait=True):
1925 def lock(self, wait=True):
1924 """Lock the repository store and return a lock instance."""
1926 """Lock the repository store and return a lock instance."""
1925
1927
1926 def currentlock(self):
1928 def currentlock(self):
1927 """Return the lock if it's held or None."""
1929 """Return the lock if it's held or None."""
1928
1930
1929 def wlock(self, wait=True):
1931 def wlock(self, wait=True):
1930 """Lock the non-store parts of the repository."""
1932 """Lock the non-store parts of the repository."""
1931
1933
1932 def currentwlock(self):
1934 def currentwlock(self):
1933 """Return the wlock if it's held or None."""
1935 """Return the wlock if it's held or None."""
1934
1936
1935 def checkcommitpatterns(self, wctx, match, status, fail):
1937 def checkcommitpatterns(self, wctx, match, status, fail):
1936 pass
1938 pass
1937
1939
1938 def commit(
1940 def commit(
1939 self,
1941 self,
1940 text=b'',
1942 text=b'',
1941 user=None,
1943 user=None,
1942 date=None,
1944 date=None,
1943 match=None,
1945 match=None,
1944 force=False,
1946 force=False,
1945 editor=False,
1947 editor=False,
1946 extra=None,
1948 extra=None,
1947 ):
1949 ):
1948 """Add a new revision to the repository."""
1950 """Add a new revision to the repository."""
1949
1951
1950 def commitctx(self, ctx, error=False, origctx=None):
1952 def commitctx(self, ctx, error=False, origctx=None):
1951 """Commit a commitctx instance to the repository."""
1953 """Commit a commitctx instance to the repository."""
1952
1954
1953 def destroying(self):
1955 def destroying(self):
1954 """Inform the repository that nodes are about to be destroyed."""
1956 """Inform the repository that nodes are about to be destroyed."""
1955
1957
1956 def destroyed(self):
1958 def destroyed(self):
1957 """Inform the repository that nodes have been destroyed."""
1959 """Inform the repository that nodes have been destroyed."""
1958
1960
1959 def status(
1961 def status(
1960 self,
1962 self,
1961 node1=b'.',
1963 node1=b'.',
1962 node2=None,
1964 node2=None,
1963 match=None,
1965 match=None,
1964 ignored=False,
1966 ignored=False,
1965 clean=False,
1967 clean=False,
1966 unknown=False,
1968 unknown=False,
1967 listsubrepos=False,
1969 listsubrepos=False,
1968 ):
1970 ):
1969 """Convenience method to call repo[x].status()."""
1971 """Convenience method to call repo[x].status()."""
1970
1972
1971 def addpostdsstatus(self, ps):
1973 def addpostdsstatus(self, ps):
1972 pass
1974 pass
1973
1975
1974 def postdsstatus(self):
1976 def postdsstatus(self):
1975 pass
1977 pass
1976
1978
1977 def clearpostdsstatus(self):
1979 def clearpostdsstatus(self):
1978 pass
1980 pass
1979
1981
1980 def heads(self, start=None):
1982 def heads(self, start=None):
1981 """Obtain list of nodes that are DAG heads."""
1983 """Obtain list of nodes that are DAG heads."""
1982
1984
1983 def branchheads(self, branch=None, start=None, closed=False):
1985 def branchheads(self, branch=None, start=None, closed=False):
1984 pass
1986 pass
1985
1987
1986 def branches(self, nodes):
1988 def branches(self, nodes):
1987 pass
1989 pass
1988
1990
1989 def between(self, pairs):
1991 def between(self, pairs):
1990 pass
1992 pass
1991
1993
1992 def checkpush(self, pushop):
1994 def checkpush(self, pushop):
1993 pass
1995 pass
1994
1996
1995 prepushoutgoinghooks = interfaceutil.Attribute("""util.hooks instance.""")
1997 prepushoutgoinghooks = interfaceutil.Attribute("""util.hooks instance.""")
1996
1998
1997 def pushkey(self, namespace, key, old, new):
1999 def pushkey(self, namespace, key, old, new):
1998 pass
2000 pass
1999
2001
2000 def listkeys(self, namespace):
2002 def listkeys(self, namespace):
2001 pass
2003 pass
2002
2004
2003 def debugwireargs(self, one, two, three=None, four=None, five=None):
2005 def debugwireargs(self, one, two, three=None, four=None, five=None):
2004 pass
2006 pass
2005
2007
2006 def savecommitmessage(self, text):
2008 def savecommitmessage(self, text):
2007 pass
2009 pass
2008
2010
2009 def register_sidedata_computer(
2011 def register_sidedata_computer(
2010 self, kind, category, keys, computer, flags, replace=False
2012 self, kind, category, keys, computer, flags, replace=False
2011 ):
2013 ):
2012 pass
2014 pass
2013
2015
2014 def register_wanted_sidedata(self, category):
2016 def register_wanted_sidedata(self, category):
2015 pass
2017 pass
2016
2018
2017
2019
2018 class completelocalrepository(
2020 class completelocalrepository(
2019 ilocalrepositorymain, ilocalrepositoryfilestorage
2021 ilocalrepositorymain, ilocalrepositoryfilestorage
2020 ):
2022 ):
2021 """Complete interface for a local repository."""
2023 """Complete interface for a local repository."""
2022
2024
2023
2025
2024 class iwireprotocolcommandcacher(Protocol):
2026 class iwireprotocolcommandcacher(Protocol):
2025 """Represents a caching backend for wire protocol commands.
2027 """Represents a caching backend for wire protocol commands.
2026
2028
2027 Wire protocol version 2 supports transparent caching of many commands.
2029 Wire protocol version 2 supports transparent caching of many commands.
2028 To leverage this caching, servers can activate objects that cache
2030 To leverage this caching, servers can activate objects that cache
2029 command responses. Objects handle both cache writing and reading.
2031 command responses. Objects handle both cache writing and reading.
2030 This interface defines how that response caching mechanism works.
2032 This interface defines how that response caching mechanism works.
2031
2033
2032 Wire protocol version 2 commands emit a series of objects that are
2034 Wire protocol version 2 commands emit a series of objects that are
2033 serialized and sent to the client. The caching layer exists between
2035 serialized and sent to the client. The caching layer exists between
2034 the invocation of the command function and the sending of its output
2036 the invocation of the command function and the sending of its output
2035 objects to an output layer.
2037 objects to an output layer.
2036
2038
2037 Instances of this interface represent a binding to a cache that
2039 Instances of this interface represent a binding to a cache that
2038 can serve a response (in place of calling a command function) and/or
2040 can serve a response (in place of calling a command function) and/or
2039 write responses to a cache for subsequent use.
2041 write responses to a cache for subsequent use.
2040
2042
2041 When a command request arrives, the following happens with regards
2043 When a command request arrives, the following happens with regards
2042 to this interface:
2044 to this interface:
2043
2045
2044 1. The server determines whether the command request is cacheable.
2046 1. The server determines whether the command request is cacheable.
2045 2. If it is, an instance of this interface is spawned.
2047 2. If it is, an instance of this interface is spawned.
2046 3. The cacher is activated in a context manager (``__enter__`` is called).
2048 3. The cacher is activated in a context manager (``__enter__`` is called).
2047 4. A cache *key* for that request is derived. This will call the
2049 4. A cache *key* for that request is derived. This will call the
2048 instance's ``adjustcachekeystate()`` method so the derivation
2050 instance's ``adjustcachekeystate()`` method so the derivation
2049 can be influenced.
2051 can be influenced.
2050 5. The cacher is informed of the derived cache key via a call to
2052 5. The cacher is informed of the derived cache key via a call to
2051 ``setcachekey()``.
2053 ``setcachekey()``.
2052 6. The cacher's ``lookup()`` method is called to test for presence of
2054 6. The cacher's ``lookup()`` method is called to test for presence of
2053 the derived key in the cache.
2055 the derived key in the cache.
2054 7. If ``lookup()`` returns a hit, that cached result is used in place
2056 7. If ``lookup()`` returns a hit, that cached result is used in place
2055 of invoking the command function. ``__exit__`` is called and the instance
2057 of invoking the command function. ``__exit__`` is called and the instance
2056 is discarded.
2058 is discarded.
2057 8. The command function is invoked.
2059 8. The command function is invoked.
2058 9. ``onobject()`` is called for each object emitted by the command
2060 9. ``onobject()`` is called for each object emitted by the command
2059 function.
2061 function.
2060 10. After the final object is seen, ``onfinished()`` is called.
2062 10. After the final object is seen, ``onfinished()`` is called.
2061 11. ``__exit__`` is called to signal the end of use of the instance.
2063 11. ``__exit__`` is called to signal the end of use of the instance.
2062
2064
2063 Cache *key* derivation can be influenced by the instance.
2065 Cache *key* derivation can be influenced by the instance.
2064
2066
2065 Cache keys are initially derived by a deterministic representation of
2067 Cache keys are initially derived by a deterministic representation of
2066 the command request. This includes the command name, arguments, protocol
2068 the command request. This includes the command name, arguments, protocol
2067 version, etc. This initial key derivation is performed by CBOR-encoding a
2069 version, etc. This initial key derivation is performed by CBOR-encoding a
2068 data structure and feeding that output into a hasher.
2070 data structure and feeding that output into a hasher.
2069
2071
2070 Instances of this interface can influence this initial key derivation
2072 Instances of this interface can influence this initial key derivation
2071 via ``adjustcachekeystate()``.
2073 via ``adjustcachekeystate()``.
2072
2074
2073 The instance is informed of the derived cache key via a call to
2075 The instance is informed of the derived cache key via a call to
2074 ``setcachekey()``. The instance must store the key locally so it can
2076 ``setcachekey()``. The instance must store the key locally so it can
2075 be consulted on subsequent operations that may require it.
2077 be consulted on subsequent operations that may require it.
2076
2078
2077 When constructed, the instance has access to a callable that can be used
2079 When constructed, the instance has access to a callable that can be used
2078 for encoding response objects. This callable receives as its single
2080 for encoding response objects. This callable receives as its single
2079 argument an object emitted by a command function. It returns an iterable
2081 argument an object emitted by a command function. It returns an iterable
2080 of bytes chunks representing the encoded object. Unless the cacher is
2082 of bytes chunks representing the encoded object. Unless the cacher is
2081 caching native Python objects in memory or has a way of reconstructing
2083 caching native Python objects in memory or has a way of reconstructing
2082 the original Python objects, implementations typically call this function
2084 the original Python objects, implementations typically call this function
2083 to produce bytes from the output objects and then store those bytes in
2085 to produce bytes from the output objects and then store those bytes in
2084 the cache. When it comes time to re-emit those bytes, they are wrapped
2086 the cache. When it comes time to re-emit those bytes, they are wrapped
2085 in a ``wireprototypes.encodedresponse`` instance to tell the output
2087 in a ``wireprototypes.encodedresponse`` instance to tell the output
2086 layer that they are pre-encoded.
2088 layer that they are pre-encoded.
2087
2089
2088 When receiving the objects emitted by the command function, instances
2090 When receiving the objects emitted by the command function, instances
2089 can choose what to do with those objects. The simplest thing to do is
2091 can choose what to do with those objects. The simplest thing to do is
2090 re-emit the original objects. They will be forwarded to the output
2092 re-emit the original objects. They will be forwarded to the output
2091 layer and will be processed as if the cacher did not exist.
2093 layer and will be processed as if the cacher did not exist.
2092
2094
2093 Implementations could also choose to not emit objects - instead locally
2095 Implementations could also choose to not emit objects - instead locally
2094 buffering objects or their encoded representation. They could then emit
2096 buffering objects or their encoded representation. They could then emit
2095 a single "coalesced" object when ``onfinished()`` is called. In
2097 a single "coalesced" object when ``onfinished()`` is called. In
2096 this way, the implementation would function as a filtering layer of
2098 this way, the implementation would function as a filtering layer of
2097 sorts.
2099 sorts.
2098
2100
2099 When caching objects, typically the encoded form of the object will
2101 When caching objects, typically the encoded form of the object will
2100 be stored. Keep in mind that if the original object is forwarded to
2102 be stored. Keep in mind that if the original object is forwarded to
2101 the output layer, it will need to be encoded there as well. For large
2103 the output layer, it will need to be encoded there as well. For large
2102 output, this redundant encoding could add overhead. Implementations
2104 output, this redundant encoding could add overhead. Implementations
2103 could wrap the encoded object data in ``wireprototypes.encodedresponse``
2105 could wrap the encoded object data in ``wireprototypes.encodedresponse``
2104 instances to avoid this overhead.
2106 instances to avoid this overhead.
2105 """
2107 """
2106
2108
2107 def __enter__(self):
2109 def __enter__(self):
2108 """Marks the instance as active.
2110 """Marks the instance as active.
2109
2111
2110 Should return self.
2112 Should return self.
2111 """
2113 """
2112
2114
2113 def __exit__(self, exctype, excvalue, exctb):
2115 def __exit__(self, exctype, excvalue, exctb):
2114 """Called when cacher is no longer used.
2116 """Called when cacher is no longer used.
2115
2117
2116 This can be used by implementations to perform cleanup actions (e.g.
2118 This can be used by implementations to perform cleanup actions (e.g.
2117 disconnecting network sockets, aborting a partially cached response.
2119 disconnecting network sockets, aborting a partially cached response.
2118 """
2120 """
2119
2121
2120 def adjustcachekeystate(self, state):
2122 def adjustcachekeystate(self, state):
2121 """Influences cache key derivation by adjusting state to derive key.
2123 """Influences cache key derivation by adjusting state to derive key.
2122
2124
2123 A dict defining the state used to derive the cache key is passed.
2125 A dict defining the state used to derive the cache key is passed.
2124
2126
2125 Implementations can modify this dict to record additional state that
2127 Implementations can modify this dict to record additional state that
2126 is wanted to influence key derivation.
2128 is wanted to influence key derivation.
2127
2129
2128 Implementations are *highly* encouraged to not modify or delete
2130 Implementations are *highly* encouraged to not modify or delete
2129 existing keys.
2131 existing keys.
2130 """
2132 """
2131
2133
2132 def setcachekey(self, key):
2134 def setcachekey(self, key):
2133 """Record the derived cache key for this request.
2135 """Record the derived cache key for this request.
2134
2136
2135 Instances may mutate the key for internal usage, as desired. e.g.
2137 Instances may mutate the key for internal usage, as desired. e.g.
2136 instances may wish to prepend the repo name, introduce path
2138 instances may wish to prepend the repo name, introduce path
2137 components for filesystem or URL addressing, etc. Behavior is up to
2139 components for filesystem or URL addressing, etc. Behavior is up to
2138 the cache.
2140 the cache.
2139
2141
2140 Returns a bool indicating if the request is cacheable by this
2142 Returns a bool indicating if the request is cacheable by this
2141 instance.
2143 instance.
2142 """
2144 """
2143
2145
2144 def lookup(self):
2146 def lookup(self):
2145 """Attempt to resolve an entry in the cache.
2147 """Attempt to resolve an entry in the cache.
2146
2148
2147 The instance is instructed to look for the cache key that it was
2149 The instance is instructed to look for the cache key that it was
2148 informed about via the call to ``setcachekey()``.
2150 informed about via the call to ``setcachekey()``.
2149
2151
2150 If there's no cache hit or the cacher doesn't wish to use the cached
2152 If there's no cache hit or the cacher doesn't wish to use the cached
2151 entry, ``None`` should be returned.
2153 entry, ``None`` should be returned.
2152
2154
2153 Else, a dict defining the cached result should be returned. The
2155 Else, a dict defining the cached result should be returned. The
2154 dict may have the following keys:
2156 dict may have the following keys:
2155
2157
2156 objs
2158 objs
2157 An iterable of objects that should be sent to the client. That
2159 An iterable of objects that should be sent to the client. That
2158 iterable of objects is expected to be what the command function
2160 iterable of objects is expected to be what the command function
2159 would return if invoked or an equivalent representation thereof.
2161 would return if invoked or an equivalent representation thereof.
2160 """
2162 """
2161
2163
2162 def onobject(self, obj):
2164 def onobject(self, obj):
2163 """Called when a new object is emitted from the command function.
2165 """Called when a new object is emitted from the command function.
2164
2166
2165 Receives as its argument the object that was emitted from the
2167 Receives as its argument the object that was emitted from the
2166 command function.
2168 command function.
2167
2169
2168 This method returns an iterator of objects to forward to the output
2170 This method returns an iterator of objects to forward to the output
2169 layer. The easiest implementation is a generator that just
2171 layer. The easiest implementation is a generator that just
2170 ``yield obj``.
2172 ``yield obj``.
2171 """
2173 """
2172
2174
2173 def onfinished(self):
2175 def onfinished(self):
2174 """Called after all objects have been emitted from the command function.
2176 """Called after all objects have been emitted from the command function.
2175
2177
2176 Implementations should return an iterator of objects to forward to
2178 Implementations should return an iterator of objects to forward to
2177 the output layer.
2179 the output layer.
2178
2180
2179 This method can be a generator.
2181 This method can be a generator.
2180 """
2182 """
General Comments 0
You need to be logged in to leave comments. Login now