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