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