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