##// END OF EJS Templates
interfaces: convert `repository.imanifestlog` from zope `Attribute` attrs...
Matt Harbison -
r53379:ed70604d default
parent child Browse files
Show More
@@ -1,2209 +1,2207
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 1282 class imanifestrevisionwritable(imanifestrevisionbase, Protocol):
1283 1283 """Interface representing a manifest revision that can be committed."""
1284 1284
1285 1285 @abc.abstractmethod
1286 1286 def write(
1287 1287 self, transaction, linkrev, p1node, p2node, added, removed, match=None
1288 1288 ):
1289 1289 """Add this revision to storage.
1290 1290
1291 1291 Takes a transaction object, the changeset revision number it will
1292 1292 be associated with, its parent nodes, and lists of added and
1293 1293 removed paths.
1294 1294
1295 1295 If match is provided, storage can choose not to inspect or write out
1296 1296 items that do not match. Storage is still required to be able to provide
1297 1297 the full manifest in the future for any directories written (these
1298 1298 manifests should not be "narrowed on disk").
1299 1299
1300 1300 Returns the binary node of the created revision.
1301 1301 """
1302 1302
1303 1303
1304 1304 class imanifeststorage(Protocol):
1305 1305 """Storage interface for manifest data."""
1306 1306
1307 1307 nodeconstants: NodeConstants
1308 1308 """nodeconstants used by the current repository."""
1309 1309
1310 1310 tree: bytes
1311 1311 """The path to the directory this manifest tracks.
1312 1312
1313 1313 The empty bytestring represents the root manifest.
1314 1314 """
1315 1315
1316 1316 index: ifilerevisionssequence
1317 1317 """An ``ifilerevisionssequence`` instance."""
1318 1318
1319 1319 opener: Vfs
1320 1320 """VFS opener to use to access underlying files used for storage.
1321 1321
1322 1322 TODO this is revlog specific and should not be exposed.
1323 1323 """
1324 1324
1325 1325 # TODO: finish type hints
1326 1326 fulltextcache: dict
1327 1327 """Dict with cache of fulltexts.
1328 1328
1329 1329 TODO this doesn't feel appropriate for the storage interface.
1330 1330 """
1331 1331
1332 1332 @abc.abstractmethod
1333 1333 def __len__(self):
1334 1334 """Obtain the number of revisions stored for this manifest."""
1335 1335
1336 1336 @abc.abstractmethod
1337 1337 def __iter__(self):
1338 1338 """Iterate over revision numbers for this manifest."""
1339 1339
1340 1340 @abc.abstractmethod
1341 1341 def rev(self, node):
1342 1342 """Obtain the revision number given a binary node.
1343 1343
1344 1344 Raises ``error.LookupError`` if the node is not known.
1345 1345 """
1346 1346
1347 1347 @abc.abstractmethod
1348 1348 def node(self, rev):
1349 1349 """Obtain the node value given a revision number.
1350 1350
1351 1351 Raises ``error.LookupError`` if the revision is not known.
1352 1352 """
1353 1353
1354 1354 @abc.abstractmethod
1355 1355 def lookup(self, value):
1356 1356 """Attempt to resolve a value to a node.
1357 1357
1358 1358 Value can be a binary node, hex node, revision number, or a bytes
1359 1359 that can be converted to an integer.
1360 1360
1361 1361 Raises ``error.LookupError`` if a ndoe could not be resolved.
1362 1362 """
1363 1363
1364 1364 @abc.abstractmethod
1365 1365 def parents(self, node):
1366 1366 """Returns a 2-tuple of parent nodes for a node.
1367 1367
1368 1368 Values will be ``nullid`` if the parent is empty.
1369 1369 """
1370 1370
1371 1371 @abc.abstractmethod
1372 1372 def parentrevs(self, rev):
1373 1373 """Like parents() but operates on revision numbers."""
1374 1374
1375 1375 @abc.abstractmethod
1376 1376 def linkrev(self, rev):
1377 1377 """Obtain the changeset revision number a revision is linked to."""
1378 1378
1379 1379 @abc.abstractmethod
1380 1380 def revision(self, node):
1381 1381 """Obtain fulltext data for a node."""
1382 1382
1383 1383 @abc.abstractmethod
1384 1384 def rawdata(self, node):
1385 1385 """Obtain raw data for a node."""
1386 1386
1387 1387 @abc.abstractmethod
1388 1388 def revdiff(self, rev1, rev2):
1389 1389 """Obtain a delta between two revision numbers.
1390 1390
1391 1391 The returned data is the result of ``bdiff.bdiff()`` on the raw
1392 1392 revision data.
1393 1393 """
1394 1394
1395 1395 @abc.abstractmethod
1396 1396 def cmp(self, node, fulltext):
1397 1397 """Compare fulltext to another revision.
1398 1398
1399 1399 Returns True if the fulltext is different from what is stored.
1400 1400 """
1401 1401
1402 1402 @abc.abstractmethod
1403 1403 def emitrevisions(
1404 1404 self,
1405 1405 nodes,
1406 1406 nodesorder=None,
1407 1407 revisiondata=False,
1408 1408 assumehaveparentrevisions=False,
1409 1409 ):
1410 1410 """Produce ``irevisiondelta`` describing revisions.
1411 1411
1412 1412 See the documentation for ``ifiledata`` for more.
1413 1413 """
1414 1414
1415 1415 @abc.abstractmethod
1416 1416 def addgroup(
1417 1417 self,
1418 1418 deltas,
1419 1419 linkmapper,
1420 1420 transaction,
1421 1421 addrevisioncb=None,
1422 1422 duplicaterevisioncb=None,
1423 1423 ):
1424 1424 """Process a series of deltas for storage.
1425 1425
1426 1426 See the documentation in ``ifilemutation`` for more.
1427 1427 """
1428 1428
1429 1429 @abc.abstractmethod
1430 1430 def rawsize(self, rev):
1431 1431 """Obtain the size of tracked data.
1432 1432
1433 1433 Is equivalent to ``len(m.rawdata(node))``.
1434 1434
1435 1435 TODO this method is only used by upgrade code and may be removed.
1436 1436 """
1437 1437
1438 1438 @abc.abstractmethod
1439 1439 def getstrippoint(self, minlink):
1440 1440 """Find minimum revision that must be stripped to strip a linkrev.
1441 1441
1442 1442 See the documentation in ``ifilemutation`` for more.
1443 1443 """
1444 1444
1445 1445 @abc.abstractmethod
1446 1446 def strip(self, minlink, transaction):
1447 1447 """Remove storage of items starting at a linkrev.
1448 1448
1449 1449 See the documentation in ``ifilemutation`` for more.
1450 1450 """
1451 1451
1452 1452 @abc.abstractmethod
1453 1453 def checksize(self):
1454 1454 """Obtain the expected sizes of backing files.
1455 1455
1456 1456 TODO this is used by verify and it should not be part of the interface.
1457 1457 """
1458 1458
1459 1459 @abc.abstractmethod
1460 1460 def files(self):
1461 1461 """Obtain paths that are backing storage for this manifest.
1462 1462
1463 1463 TODO this is used by verify and there should probably be a better API
1464 1464 for this functionality.
1465 1465 """
1466 1466
1467 1467 @abc.abstractmethod
1468 1468 def deltaparent(self, rev):
1469 1469 """Obtain the revision that a revision is delta'd against.
1470 1470
1471 1471 TODO delta encoding is an implementation detail of storage and should
1472 1472 not be exposed to the storage interface.
1473 1473 """
1474 1474
1475 1475 @abc.abstractmethod
1476 1476 def clone(self, tr, dest, **kwargs):
1477 1477 """Clone this instance to another."""
1478 1478
1479 1479 @abc.abstractmethod
1480 1480 def clearcaches(self, clear_persisted_data=False):
1481 1481 """Clear any caches associated with this instance."""
1482 1482
1483 1483 @abc.abstractmethod
1484 1484 def dirlog(self, d):
1485 1485 """Obtain a manifest storage instance for a tree."""
1486 1486
1487 1487 @abc.abstractmethod
1488 1488 def add(
1489 1489 self,
1490 1490 m,
1491 1491 transaction,
1492 1492 link,
1493 1493 p1,
1494 1494 p2,
1495 1495 added,
1496 1496 removed,
1497 1497 readtree=None,
1498 1498 match=None,
1499 1499 ):
1500 1500 """Add a revision to storage.
1501 1501
1502 1502 ``m`` is an object conforming to ``imanifestdict``.
1503 1503
1504 1504 ``link`` is the linkrev revision number.
1505 1505
1506 1506 ``p1`` and ``p2`` are the parent revision numbers.
1507 1507
1508 1508 ``added`` and ``removed`` are iterables of added and removed paths,
1509 1509 respectively.
1510 1510
1511 1511 ``readtree`` is a function that can be used to read the child tree(s)
1512 1512 when recursively writing the full tree structure when using
1513 1513 treemanifets.
1514 1514
1515 1515 ``match`` is a matcher that can be used to hint to storage that not all
1516 1516 paths must be inspected; this is an optimization and can be safely
1517 1517 ignored. Note that the storage must still be able to reproduce a full
1518 1518 manifest including files that did not match.
1519 1519 """
1520 1520
1521 1521 @abc.abstractmethod
1522 1522 def storageinfo(
1523 1523 self,
1524 1524 exclusivefiles=False,
1525 1525 sharedfiles=False,
1526 1526 revisionscount=False,
1527 1527 trackedsize=False,
1528 1528 storedsize=False,
1529 1529 ):
1530 1530 """Obtain information about storage for this manifest's data.
1531 1531
1532 1532 See ``ifilestorage.storageinfo()`` for a description of this method.
1533 1533 This one behaves the same way, except for manifest data.
1534 1534 """
1535 1535
1536 1536 @abc.abstractmethod
1537 1537 def get_revlog(self):
1538 1538 """return an actual revlog instance if any
1539 1539
1540 1540 This exist because a lot of code leverage the fact the underlying
1541 1541 storage is a revlog for optimization, so giving simple way to access
1542 1542 the revlog instance helps such code.
1543 1543 """
1544 1544
1545 1545
1546 1546 class imanifestlog(Protocol):
1547 1547 """Interface representing a collection of manifest snapshots.
1548 1548
1549 1549 Represents the root manifest in a repository.
1550 1550
1551 1551 Also serves as a means to access nested tree manifests and to cache
1552 1552 tree manifests.
1553 1553 """
1554 1554
1555 nodeconstants = interfaceutil.Attribute(
1556 """nodeconstants used by the current repository."""
1557 )
1558
1559 narrowed = interfaceutil.Attribute(
1560 """True, is the manifest is narrowed by a matcher"""
1561 )
1555 nodeconstants: NodeConstants
1556 """nodeconstants used by the current repository."""
1557
1558 narrowed: bool
1559 """True, is the manifest is narrowed by a matcher"""
1562 1560
1563 1561 def __getitem__(self, node):
1564 1562 """Obtain a manifest instance for a given binary node.
1565 1563
1566 1564 Equivalent to calling ``self.get('', node)``.
1567 1565
1568 1566 The returned object conforms to the ``imanifestrevisionstored``
1569 1567 interface.
1570 1568 """
1571 1569
1572 1570 def get(self, tree, node, verify=True):
1573 1571 """Retrieve the manifest instance for a given directory and binary node.
1574 1572
1575 1573 ``node`` always refers to the node of the root manifest (which will be
1576 1574 the only manifest if flat manifests are being used).
1577 1575
1578 1576 If ``tree`` is the empty string, the root manifest is returned.
1579 1577 Otherwise the manifest for the specified directory will be returned
1580 1578 (requires tree manifests).
1581 1579
1582 1580 If ``verify`` is True, ``LookupError`` is raised if the node is not
1583 1581 known.
1584 1582
1585 1583 The returned object conforms to the ``imanifestrevisionstored``
1586 1584 interface.
1587 1585 """
1588 1586
1589 1587 def getstorage(self, tree):
1590 1588 """Retrieve an interface to storage for a particular tree.
1591 1589
1592 1590 If ``tree`` is the empty bytestring, storage for the root manifest will
1593 1591 be returned. Otherwise storage for a tree manifest is returned.
1594 1592
1595 1593 TODO formalize interface for returned object.
1596 1594 """
1597 1595
1598 1596 def clearcaches(self, clear_persisted_data: bool = False) -> None:
1599 1597 """Clear caches associated with this collection."""
1600 1598
1601 1599 def rev(self, node):
1602 1600 """Obtain the revision number for a binary node.
1603 1601
1604 1602 Raises ``error.LookupError`` if the node is not known.
1605 1603 """
1606 1604
1607 1605 def update_caches(self, transaction):
1608 1606 """update whatever cache are relevant for the used storage."""
1609 1607
1610 1608
1611 1609 class ilocalrepositoryfilestorage(Protocol):
1612 1610 """Local repository sub-interface providing access to tracked file storage.
1613 1611
1614 1612 This interface defines how a repository accesses storage for a single
1615 1613 tracked file path.
1616 1614 """
1617 1615
1618 1616 def file(self, f):
1619 1617 """Obtain a filelog for a tracked path.
1620 1618
1621 1619 The returned type conforms to the ``ifilestorage`` interface.
1622 1620 """
1623 1621
1624 1622
1625 1623 class ilocalrepositorymain(Protocol):
1626 1624 """Main interface for local repositories.
1627 1625
1628 1626 This currently captures the reality of things - not how things should be.
1629 1627 """
1630 1628
1631 1629 nodeconstants = interfaceutil.Attribute(
1632 1630 """Constant nodes matching the hash function used by the repository."""
1633 1631 )
1634 1632 nullid = interfaceutil.Attribute(
1635 1633 """null revision for the hash function used by the repository."""
1636 1634 )
1637 1635
1638 1636 supported = interfaceutil.Attribute(
1639 1637 """Set of requirements that this repo is capable of opening."""
1640 1638 )
1641 1639
1642 1640 requirements = interfaceutil.Attribute(
1643 1641 """Set of requirements this repo uses."""
1644 1642 )
1645 1643
1646 1644 features = interfaceutil.Attribute(
1647 1645 """Set of "features" this repository supports.
1648 1646
1649 1647 A "feature" is a loosely-defined term. It can refer to a feature
1650 1648 in the classical sense or can describe an implementation detail
1651 1649 of the repository. For example, a ``readonly`` feature may denote
1652 1650 the repository as read-only. Or a ``revlogfilestore`` feature may
1653 1651 denote that the repository is using revlogs for file storage.
1654 1652
1655 1653 The intent of features is to provide a machine-queryable mechanism
1656 1654 for repo consumers to test for various repository characteristics.
1657 1655
1658 1656 Features are similar to ``requirements``. The main difference is that
1659 1657 requirements are stored on-disk and represent requirements to open the
1660 1658 repository. Features are more run-time capabilities of the repository
1661 1659 and more granular capabilities (which may be derived from requirements).
1662 1660 """
1663 1661 )
1664 1662
1665 1663 filtername = interfaceutil.Attribute(
1666 1664 """Name of the repoview that is active on this repo."""
1667 1665 )
1668 1666
1669 1667 vfs_map = interfaceutil.Attribute(
1670 1668 """a bytes-key β†’ vfs mapping used by transaction and others"""
1671 1669 )
1672 1670
1673 1671 wvfs = interfaceutil.Attribute(
1674 1672 """VFS used to access the working directory."""
1675 1673 )
1676 1674
1677 1675 vfs = interfaceutil.Attribute(
1678 1676 """VFS rooted at the .hg directory.
1679 1677
1680 1678 Used to access repository data not in the store.
1681 1679 """
1682 1680 )
1683 1681
1684 1682 svfs = interfaceutil.Attribute(
1685 1683 """VFS rooted at the store.
1686 1684
1687 1685 Used to access repository data in the store. Typically .hg/store.
1688 1686 But can point elsewhere if the store is shared.
1689 1687 """
1690 1688 )
1691 1689
1692 1690 root = interfaceutil.Attribute(
1693 1691 """Path to the root of the working directory."""
1694 1692 )
1695 1693
1696 1694 path = interfaceutil.Attribute("""Path to the .hg directory.""")
1697 1695
1698 1696 origroot = interfaceutil.Attribute(
1699 1697 """The filesystem path that was used to construct the repo."""
1700 1698 )
1701 1699
1702 1700 auditor = interfaceutil.Attribute(
1703 1701 """A pathauditor for the working directory.
1704 1702
1705 1703 This checks if a path refers to a nested repository.
1706 1704
1707 1705 Operates on the filesystem.
1708 1706 """
1709 1707 )
1710 1708
1711 1709 nofsauditor = interfaceutil.Attribute(
1712 1710 """A pathauditor for the working directory.
1713 1711
1714 1712 This is like ``auditor`` except it doesn't do filesystem checks.
1715 1713 """
1716 1714 )
1717 1715
1718 1716 baseui = interfaceutil.Attribute(
1719 1717 """Original ui instance passed into constructor."""
1720 1718 )
1721 1719
1722 1720 ui = interfaceutil.Attribute("""Main ui instance for this instance.""")
1723 1721
1724 1722 sharedpath = interfaceutil.Attribute(
1725 1723 """Path to the .hg directory of the repo this repo was shared from."""
1726 1724 )
1727 1725
1728 1726 store = interfaceutil.Attribute("""A store instance.""")
1729 1727
1730 1728 spath = interfaceutil.Attribute("""Path to the store.""")
1731 1729
1732 1730 sjoin = interfaceutil.Attribute("""Alias to self.store.join.""")
1733 1731
1734 1732 cachevfs = interfaceutil.Attribute(
1735 1733 """A VFS used to access the cache directory.
1736 1734
1737 1735 Typically .hg/cache.
1738 1736 """
1739 1737 )
1740 1738
1741 1739 wcachevfs = interfaceutil.Attribute(
1742 1740 """A VFS used to access the cache directory dedicated to working copy
1743 1741
1744 1742 Typically .hg/wcache.
1745 1743 """
1746 1744 )
1747 1745
1748 1746 filteredrevcache = interfaceutil.Attribute(
1749 1747 """Holds sets of revisions to be filtered."""
1750 1748 )
1751 1749
1752 1750 names = interfaceutil.Attribute("""A ``namespaces`` instance.""")
1753 1751
1754 1752 filecopiesmode = interfaceutil.Attribute(
1755 1753 """The way files copies should be dealt with in this repo."""
1756 1754 )
1757 1755
1758 1756 def close(self):
1759 1757 """Close the handle on this repository."""
1760 1758
1761 1759 def peer(self, path=None):
1762 1760 """Obtain an object conforming to the ``peer`` interface."""
1763 1761
1764 1762 def unfiltered(self):
1765 1763 """Obtain an unfiltered/raw view of this repo."""
1766 1764
1767 1765 def filtered(self, name, visibilityexceptions=None):
1768 1766 """Obtain a named view of this repository."""
1769 1767
1770 1768 obsstore = interfaceutil.Attribute("""A store of obsolescence data.""")
1771 1769
1772 1770 changelog = interfaceutil.Attribute("""A handle on the changelog revlog.""")
1773 1771
1774 1772 manifestlog = interfaceutil.Attribute(
1775 1773 """An instance conforming to the ``imanifestlog`` interface.
1776 1774
1777 1775 Provides access to manifests for the repository.
1778 1776 """
1779 1777 )
1780 1778
1781 1779 dirstate = interfaceutil.Attribute("""Working directory state.""")
1782 1780
1783 1781 narrowpats = interfaceutil.Attribute(
1784 1782 """Matcher patterns for this repository's narrowspec."""
1785 1783 )
1786 1784
1787 1785 def narrowmatch(self, match=None, includeexact=False):
1788 1786 """Obtain a matcher for the narrowspec."""
1789 1787
1790 1788 def setnarrowpats(self, newincludes, newexcludes):
1791 1789 """Define the narrowspec for this repository."""
1792 1790
1793 1791 def __getitem__(self, changeid):
1794 1792 """Try to resolve a changectx."""
1795 1793
1796 1794 def __contains__(self, changeid):
1797 1795 """Whether a changeset exists."""
1798 1796
1799 1797 def __nonzero__(self):
1800 1798 """Always returns True."""
1801 1799 return True
1802 1800
1803 1801 __bool__ = __nonzero__
1804 1802
1805 1803 def __len__(self):
1806 1804 """Returns the number of changesets in the repo."""
1807 1805
1808 1806 def __iter__(self):
1809 1807 """Iterate over revisions in the changelog."""
1810 1808
1811 1809 def revs(self, expr, *args):
1812 1810 """Evaluate a revset.
1813 1811
1814 1812 Emits revisions.
1815 1813 """
1816 1814
1817 1815 def set(self, expr, *args):
1818 1816 """Evaluate a revset.
1819 1817
1820 1818 Emits changectx instances.
1821 1819 """
1822 1820
1823 1821 def anyrevs(self, specs, user=False, localalias=None):
1824 1822 """Find revisions matching one of the given revsets."""
1825 1823
1826 1824 def url(self):
1827 1825 """Returns a string representing the location of this repo."""
1828 1826
1829 1827 def hook(self, name, throw=False, **args):
1830 1828 """Call a hook."""
1831 1829
1832 1830 def tags(self):
1833 1831 """Return a mapping of tag to node."""
1834 1832
1835 1833 def tagtype(self, tagname):
1836 1834 """Return the type of a given tag."""
1837 1835
1838 1836 def tagslist(self):
1839 1837 """Return a list of tags ordered by revision."""
1840 1838
1841 1839 def nodetags(self, node):
1842 1840 """Return the tags associated with a node."""
1843 1841
1844 1842 def nodebookmarks(self, node):
1845 1843 """Return the list of bookmarks pointing to the specified node."""
1846 1844
1847 1845 def branchmap(self):
1848 1846 """Return a mapping of branch to heads in that branch."""
1849 1847
1850 1848 def revbranchcache(self):
1851 1849 pass
1852 1850
1853 1851 def register_changeset(self, rev, changelogrevision):
1854 1852 """Extension point for caches for new nodes.
1855 1853
1856 1854 Multiple consumers are expected to need parts of the changelogrevision,
1857 1855 so it is provided as optimization to avoid duplicate lookups. A simple
1858 1856 cache would be fragile when other revisions are accessed, too."""
1859 1857 pass
1860 1858
1861 1859 def branchtip(self, branchtip, ignoremissing=False):
1862 1860 """Return the tip node for a given branch."""
1863 1861
1864 1862 def lookup(self, key):
1865 1863 """Resolve the node for a revision."""
1866 1864
1867 1865 def lookupbranch(self, key):
1868 1866 """Look up the branch name of the given revision or branch name."""
1869 1867
1870 1868 def known(self, nodes):
1871 1869 """Determine whether a series of nodes is known.
1872 1870
1873 1871 Returns a list of bools.
1874 1872 """
1875 1873
1876 1874 def local(self):
1877 1875 """Whether the repository is local."""
1878 1876 return True
1879 1877
1880 1878 def publishing(self):
1881 1879 """Whether the repository is a publishing repository."""
1882 1880
1883 1881 def cancopy(self):
1884 1882 pass
1885 1883
1886 1884 def shared(self):
1887 1885 """The type of shared repository or None."""
1888 1886
1889 1887 def wjoin(self, f, *insidef):
1890 1888 """Calls self.vfs.reljoin(self.root, f, *insidef)"""
1891 1889
1892 1890 def setparents(self, p1, p2):
1893 1891 """Set the parent nodes of the working directory."""
1894 1892
1895 1893 def filectx(self, path, changeid=None, fileid=None):
1896 1894 """Obtain a filectx for the given file revision."""
1897 1895
1898 1896 def getcwd(self):
1899 1897 """Obtain the current working directory from the dirstate."""
1900 1898
1901 1899 def pathto(self, f, cwd=None):
1902 1900 """Obtain the relative path to a file."""
1903 1901
1904 1902 def adddatafilter(self, name, fltr):
1905 1903 pass
1906 1904
1907 1905 def wread(self, filename):
1908 1906 """Read a file from wvfs, using data filters."""
1909 1907
1910 1908 def wwrite(self, filename, data, flags, backgroundclose=False, **kwargs):
1911 1909 """Write data to a file in the wvfs, using data filters."""
1912 1910
1913 1911 def wwritedata(self, filename, data):
1914 1912 """Resolve data for writing to the wvfs, using data filters."""
1915 1913
1916 1914 def currenttransaction(self):
1917 1915 """Obtain the current transaction instance or None."""
1918 1916
1919 1917 def transaction(self, desc, report=None):
1920 1918 """Open a new transaction to write to the repository."""
1921 1919
1922 1920 def undofiles(self):
1923 1921 """Returns a list of (vfs, path) for files to undo transactions."""
1924 1922
1925 1923 def recover(self):
1926 1924 """Roll back an interrupted transaction."""
1927 1925
1928 1926 def rollback(self, dryrun=False, force=False):
1929 1927 """Undo the last transaction.
1930 1928
1931 1929 DANGEROUS.
1932 1930 """
1933 1931
1934 1932 def updatecaches(self, tr=None, full=False, caches=None):
1935 1933 """Warm repo caches."""
1936 1934
1937 1935 def invalidatecaches(self):
1938 1936 """Invalidate cached data due to the repository mutating."""
1939 1937
1940 1938 def invalidatevolatilesets(self):
1941 1939 pass
1942 1940
1943 1941 def invalidatedirstate(self):
1944 1942 """Invalidate the dirstate."""
1945 1943
1946 1944 def invalidate(self, clearfilecache=False):
1947 1945 pass
1948 1946
1949 1947 def invalidateall(self):
1950 1948 pass
1951 1949
1952 1950 def lock(self, wait=True):
1953 1951 """Lock the repository store and return a lock instance."""
1954 1952
1955 1953 def currentlock(self):
1956 1954 """Return the lock if it's held or None."""
1957 1955
1958 1956 def wlock(self, wait=True):
1959 1957 """Lock the non-store parts of the repository."""
1960 1958
1961 1959 def currentwlock(self):
1962 1960 """Return the wlock if it's held or None."""
1963 1961
1964 1962 def checkcommitpatterns(self, wctx, match, status, fail):
1965 1963 pass
1966 1964
1967 1965 def commit(
1968 1966 self,
1969 1967 text=b'',
1970 1968 user=None,
1971 1969 date=None,
1972 1970 match=None,
1973 1971 force=False,
1974 1972 editor=False,
1975 1973 extra=None,
1976 1974 ):
1977 1975 """Add a new revision to the repository."""
1978 1976
1979 1977 def commitctx(self, ctx, error=False, origctx=None):
1980 1978 """Commit a commitctx instance to the repository."""
1981 1979
1982 1980 def destroying(self):
1983 1981 """Inform the repository that nodes are about to be destroyed."""
1984 1982
1985 1983 def destroyed(self):
1986 1984 """Inform the repository that nodes have been destroyed."""
1987 1985
1988 1986 def status(
1989 1987 self,
1990 1988 node1=b'.',
1991 1989 node2=None,
1992 1990 match=None,
1993 1991 ignored=False,
1994 1992 clean=False,
1995 1993 unknown=False,
1996 1994 listsubrepos=False,
1997 1995 ):
1998 1996 """Convenience method to call repo[x].status()."""
1999 1997
2000 1998 def addpostdsstatus(self, ps):
2001 1999 pass
2002 2000
2003 2001 def postdsstatus(self):
2004 2002 pass
2005 2003
2006 2004 def clearpostdsstatus(self):
2007 2005 pass
2008 2006
2009 2007 def heads(self, start=None):
2010 2008 """Obtain list of nodes that are DAG heads."""
2011 2009
2012 2010 def branchheads(self, branch=None, start=None, closed=False):
2013 2011 pass
2014 2012
2015 2013 def branches(self, nodes):
2016 2014 pass
2017 2015
2018 2016 def between(self, pairs):
2019 2017 pass
2020 2018
2021 2019 def checkpush(self, pushop):
2022 2020 pass
2023 2021
2024 2022 prepushoutgoinghooks = interfaceutil.Attribute("""util.hooks instance.""")
2025 2023
2026 2024 def pushkey(self, namespace, key, old, new):
2027 2025 pass
2028 2026
2029 2027 def listkeys(self, namespace):
2030 2028 pass
2031 2029
2032 2030 def debugwireargs(self, one, two, three=None, four=None, five=None):
2033 2031 pass
2034 2032
2035 2033 def savecommitmessage(self, text):
2036 2034 pass
2037 2035
2038 2036 def register_sidedata_computer(
2039 2037 self, kind, category, keys, computer, flags, replace=False
2040 2038 ):
2041 2039 pass
2042 2040
2043 2041 def register_wanted_sidedata(self, category):
2044 2042 pass
2045 2043
2046 2044
2047 2045 class completelocalrepository(
2048 2046 ilocalrepositorymain, ilocalrepositoryfilestorage
2049 2047 ):
2050 2048 """Complete interface for a local repository."""
2051 2049
2052 2050
2053 2051 class iwireprotocolcommandcacher(Protocol):
2054 2052 """Represents a caching backend for wire protocol commands.
2055 2053
2056 2054 Wire protocol version 2 supports transparent caching of many commands.
2057 2055 To leverage this caching, servers can activate objects that cache
2058 2056 command responses. Objects handle both cache writing and reading.
2059 2057 This interface defines how that response caching mechanism works.
2060 2058
2061 2059 Wire protocol version 2 commands emit a series of objects that are
2062 2060 serialized and sent to the client. The caching layer exists between
2063 2061 the invocation of the command function and the sending of its output
2064 2062 objects to an output layer.
2065 2063
2066 2064 Instances of this interface represent a binding to a cache that
2067 2065 can serve a response (in place of calling a command function) and/or
2068 2066 write responses to a cache for subsequent use.
2069 2067
2070 2068 When a command request arrives, the following happens with regards
2071 2069 to this interface:
2072 2070
2073 2071 1. The server determines whether the command request is cacheable.
2074 2072 2. If it is, an instance of this interface is spawned.
2075 2073 3. The cacher is activated in a context manager (``__enter__`` is called).
2076 2074 4. A cache *key* for that request is derived. This will call the
2077 2075 instance's ``adjustcachekeystate()`` method so the derivation
2078 2076 can be influenced.
2079 2077 5. The cacher is informed of the derived cache key via a call to
2080 2078 ``setcachekey()``.
2081 2079 6. The cacher's ``lookup()`` method is called to test for presence of
2082 2080 the derived key in the cache.
2083 2081 7. If ``lookup()`` returns a hit, that cached result is used in place
2084 2082 of invoking the command function. ``__exit__`` is called and the instance
2085 2083 is discarded.
2086 2084 8. The command function is invoked.
2087 2085 9. ``onobject()`` is called for each object emitted by the command
2088 2086 function.
2089 2087 10. After the final object is seen, ``onfinished()`` is called.
2090 2088 11. ``__exit__`` is called to signal the end of use of the instance.
2091 2089
2092 2090 Cache *key* derivation can be influenced by the instance.
2093 2091
2094 2092 Cache keys are initially derived by a deterministic representation of
2095 2093 the command request. This includes the command name, arguments, protocol
2096 2094 version, etc. This initial key derivation is performed by CBOR-encoding a
2097 2095 data structure and feeding that output into a hasher.
2098 2096
2099 2097 Instances of this interface can influence this initial key derivation
2100 2098 via ``adjustcachekeystate()``.
2101 2099
2102 2100 The instance is informed of the derived cache key via a call to
2103 2101 ``setcachekey()``. The instance must store the key locally so it can
2104 2102 be consulted on subsequent operations that may require it.
2105 2103
2106 2104 When constructed, the instance has access to a callable that can be used
2107 2105 for encoding response objects. This callable receives as its single
2108 2106 argument an object emitted by a command function. It returns an iterable
2109 2107 of bytes chunks representing the encoded object. Unless the cacher is
2110 2108 caching native Python objects in memory or has a way of reconstructing
2111 2109 the original Python objects, implementations typically call this function
2112 2110 to produce bytes from the output objects and then store those bytes in
2113 2111 the cache. When it comes time to re-emit those bytes, they are wrapped
2114 2112 in a ``wireprototypes.encodedresponse`` instance to tell the output
2115 2113 layer that they are pre-encoded.
2116 2114
2117 2115 When receiving the objects emitted by the command function, instances
2118 2116 can choose what to do with those objects. The simplest thing to do is
2119 2117 re-emit the original objects. They will be forwarded to the output
2120 2118 layer and will be processed as if the cacher did not exist.
2121 2119
2122 2120 Implementations could also choose to not emit objects - instead locally
2123 2121 buffering objects or their encoded representation. They could then emit
2124 2122 a single "coalesced" object when ``onfinished()`` is called. In
2125 2123 this way, the implementation would function as a filtering layer of
2126 2124 sorts.
2127 2125
2128 2126 When caching objects, typically the encoded form of the object will
2129 2127 be stored. Keep in mind that if the original object is forwarded to
2130 2128 the output layer, it will need to be encoded there as well. For large
2131 2129 output, this redundant encoding could add overhead. Implementations
2132 2130 could wrap the encoded object data in ``wireprototypes.encodedresponse``
2133 2131 instances to avoid this overhead.
2134 2132 """
2135 2133
2136 2134 def __enter__(self):
2137 2135 """Marks the instance as active.
2138 2136
2139 2137 Should return self.
2140 2138 """
2141 2139
2142 2140 def __exit__(self, exctype, excvalue, exctb):
2143 2141 """Called when cacher is no longer used.
2144 2142
2145 2143 This can be used by implementations to perform cleanup actions (e.g.
2146 2144 disconnecting network sockets, aborting a partially cached response.
2147 2145 """
2148 2146
2149 2147 def adjustcachekeystate(self, state):
2150 2148 """Influences cache key derivation by adjusting state to derive key.
2151 2149
2152 2150 A dict defining the state used to derive the cache key is passed.
2153 2151
2154 2152 Implementations can modify this dict to record additional state that
2155 2153 is wanted to influence key derivation.
2156 2154
2157 2155 Implementations are *highly* encouraged to not modify or delete
2158 2156 existing keys.
2159 2157 """
2160 2158
2161 2159 def setcachekey(self, key):
2162 2160 """Record the derived cache key for this request.
2163 2161
2164 2162 Instances may mutate the key for internal usage, as desired. e.g.
2165 2163 instances may wish to prepend the repo name, introduce path
2166 2164 components for filesystem or URL addressing, etc. Behavior is up to
2167 2165 the cache.
2168 2166
2169 2167 Returns a bool indicating if the request is cacheable by this
2170 2168 instance.
2171 2169 """
2172 2170
2173 2171 def lookup(self):
2174 2172 """Attempt to resolve an entry in the cache.
2175 2173
2176 2174 The instance is instructed to look for the cache key that it was
2177 2175 informed about via the call to ``setcachekey()``.
2178 2176
2179 2177 If there's no cache hit or the cacher doesn't wish to use the cached
2180 2178 entry, ``None`` should be returned.
2181 2179
2182 2180 Else, a dict defining the cached result should be returned. The
2183 2181 dict may have the following keys:
2184 2182
2185 2183 objs
2186 2184 An iterable of objects that should be sent to the client. That
2187 2185 iterable of objects is expected to be what the command function
2188 2186 would return if invoked or an equivalent representation thereof.
2189 2187 """
2190 2188
2191 2189 def onobject(self, obj):
2192 2190 """Called when a new object is emitted from the command function.
2193 2191
2194 2192 Receives as its argument the object that was emitted from the
2195 2193 command function.
2196 2194
2197 2195 This method returns an iterator of objects to forward to the output
2198 2196 layer. The easiest implementation is a generator that just
2199 2197 ``yield obj``.
2200 2198 """
2201 2199
2202 2200 def onfinished(self):
2203 2201 """Called after all objects have been emitted from the command function.
2204 2202
2205 2203 Implementations should return an iterator of objects to forward to
2206 2204 the output layer.
2207 2205
2208 2206 This method can be a generator.
2209 2207 """
General Comments 0
You need to be logged in to leave comments. Login now