##// END OF EJS Templates
wireprotov2: unify file revision collection and linknode derivation...
Gregory Szorc -
r40960:08cfa77d default
parent child Browse files
Show More
@@ -1,722 +1,724 b''
1 1 **Experimental and under active development**
2 2
3 3 This section documents the wire protocol commands exposed to transports
4 4 using the frame-based protocol. The set of commands exposed through
5 5 these transports is distinct from the set of commands exposed to legacy
6 6 transports.
7 7
8 8 The frame-based protocol uses CBOR to encode command execution requests.
9 9 All command arguments must be mapped to a specific or set of CBOR data
10 10 types.
11 11
12 12 The response to many commands is also CBOR. There is no common response
13 13 format: each command defines its own response format.
14 14
15 15 TODOs
16 16 =====
17 17
18 18 * Add "node namespace" support to each command. In order to support
19 19 SHA-1 hash transition, we want servers to be able to expose different
20 20 "node namespaces" for the same data. Every command operating on nodes
21 21 should specify which "node namespace" it is operating on and responses
22 22 should encode the "node namespace" accordingly.
23 23
24 24 Commands
25 25 ========
26 26
27 27 The sections below detail all commands available to wire protocol version
28 28 2.
29 29
30 30 branchmap
31 31 ---------
32 32
33 33 Obtain heads in named branches.
34 34
35 35 Receives no arguments.
36 36
37 37 The response is a map with bytestring keys defining the branch name.
38 38 Values are arrays of bytestring defining raw changeset nodes.
39 39
40 40 capabilities
41 41 ------------
42 42
43 43 Obtain the server's capabilities.
44 44
45 45 Receives no arguments.
46 46
47 47 This command is typically called only as part of the handshake during
48 48 initial connection establishment.
49 49
50 50 The response is a map with bytestring keys defining server information.
51 51
52 52 The defined keys are:
53 53
54 54 commands
55 55 A map defining available wire protocol commands on this server.
56 56
57 57 Keys in the map are the names of commands that can be invoked. Values
58 58 are maps defining information about that command. The bytestring keys
59 59 are:
60 60
61 61 args
62 62 (map) Describes arguments accepted by the command.
63 63
64 64 Keys are bytestrings denoting the argument name.
65 65
66 66 Values are maps describing the argument. The map has the following
67 67 bytestring keys:
68 68
69 69 default
70 70 (varied) The default value for this argument if not specified. Only
71 71 present if ``required`` is not true.
72 72
73 73 required
74 74 (boolean) Whether the argument must be specified. Failure to send
75 75 required arguments will result in an error executing the command.
76 76
77 77 type
78 78 (bytestring) The type of the argument. e.g. ``bytes`` or ``bool``.
79 79
80 80 validvalues
81 81 (set) Values that are recognized for this argument. Some arguments
82 82 only allow a fixed set of values to be specified. These arguments
83 83 may advertise that set in this key. If this set is advertised and
84 84 a value not in this set is specified, the command should result
85 85 in error.
86 86
87 87 permissions
88 88 An array of permissions required to execute this command.
89 89
90 90 *
91 91 (various) Individual commands may define extra keys that supplement
92 92 generic command metadata. See the command definition for more.
93 93
94 94 framingmediatypes
95 95 An array of bytestrings defining the supported framing protocol
96 96 media types. Servers will not accept media types not in this list.
97 97
98 98 pathfilterprefixes
99 99 (set of bytestring) Matcher prefixes that are recognized when performing
100 100 path filtering. Specifying a path filter whose type/prefix does not
101 101 match one in this set will likely be rejected by the server.
102 102
103 103 rawrepoformats
104 104 An array of storage formats the repository is using. This set of
105 105 requirements can be used to determine whether a client can read a
106 106 *raw* copy of file data available.
107 107
108 108 redirect
109 109 A map declaring potential *content redirects* that may be used by this
110 110 server. Contains the following bytestring keys:
111 111
112 112 targets
113 113 (array of maps) Potential redirect targets. Values are maps describing
114 114 this target in more detail. Each map has the following bytestring keys:
115 115
116 116 name
117 117 (bytestring) Identifier for this target. The identifier will be used
118 118 by clients to uniquely identify this target.
119 119
120 120 protocol
121 121 (bytestring) High-level network protocol. Values can be
122 122 ``http``, ```https``, ``ssh``, etc.
123 123
124 124 uris
125 125 (array of bytestrings) Representative URIs for this target.
126 126
127 127 snirequired (optional)
128 128 (boolean) Indicates whether Server Name Indication is required
129 129 to use this target. Defaults to False.
130 130
131 131 tlsversions (optional)
132 132 (array of bytestring) Indicates which TLS versions are supported by
133 133 this target. Values are ``1.1``, ``1.2``, ``1.3``, etc.
134 134
135 135 hashes
136 136 (array of bytestring) Indicates support for hashing algorithms that are
137 137 used to ensure content integrity. Values include ``sha1``, ``sha256``,
138 138 etc.
139 139
140 140 changesetdata
141 141 -------------
142 142
143 143 Obtain various data related to changesets.
144 144
145 145 The command accepts the following arguments:
146 146
147 147 revisions
148 148 (array of maps) Specifies revisions whose data is being requested. Each
149 149 value in the array is a map describing revisions. See the
150 150 *Revisions Specifiers* section below for the format of this map.
151 151
152 152 Data will be sent for the union of all revisions resolved by all
153 153 revision specifiers.
154 154
155 155 Only revision specifiers operating on changeset revisions are allowed.
156 156
157 157 fields
158 158 (set of bytestring) Which data associated with changelog revisions to
159 159 fetch. The following values are recognized:
160 160
161 161 bookmarks
162 162 Bookmarks associated with a revision.
163 163
164 164 parents
165 165 Parent revisions.
166 166
167 167 phase
168 168 The phase state of a revision.
169 169
170 170 revision
171 171 The raw, revision data for the changelog entry. The hash of this data
172 172 will match the revision's node value.
173 173
174 174 The response bytestream starts with a CBOR map describing the data that follows.
175 175 This map has the following bytestring keys:
176 176
177 177 totalitems
178 178 (unsigned integer) Total number of changelog revisions whose data is being
179 179 transferred. This maps to the set of revisions in the requested node
180 180 range, not the total number of records that follow (see below for why).
181 181
182 182 Following the map header is a series of 0 or more CBOR values. If values
183 183 are present, the first value will always be a map describing a single changeset
184 184 revision.
185 185
186 186 If the ``fieldsfollowing`` key is present, the map will immediately be followed
187 187 by N CBOR bytestring values, where N is the number of elements in
188 188 ``fieldsfollowing``. Each bytestring value corresponds to a field denoted
189 189 by ``fieldsfollowing``.
190 190
191 191 Following the optional bytestring field values is the next revision descriptor
192 192 map, or end of stream.
193 193
194 194 Each revision descriptor map has the following bytestring keys:
195 195
196 196 node
197 197 (bytestring) The node value for this revision. This is the SHA-1 hash of
198 198 the raw revision data.
199 199
200 200 bookmarks (optional)
201 201 (array of bytestrings) Bookmarks attached to this revision. Only present
202 202 if ``bookmarks`` data is being requested and the revision has bookmarks
203 203 attached.
204 204
205 205 fieldsfollowing (optional)
206 206 (array of 2-array) Denotes what fields immediately follow this map. Each
207 207 value is an array with 2 elements: the bytestring field name and an unsigned
208 208 integer describing the length of the data, in bytes.
209 209
210 210 If this key isn't present, no special fields will follow this map.
211 211
212 212 The following fields may be present:
213 213
214 214 revision
215 215 Raw, revision data for the changelog entry. Contains a serialized form
216 216 of the changeset data, including the author, date, commit message, set
217 217 of changed files, manifest node, and other metadata.
218 218
219 219 Only present if the ``revision`` field was requested.
220 220
221 221 parents (optional)
222 222 (array of bytestrings) The nodes representing the parent revisions of this
223 223 revision. Only present if ``parents`` data is being requested.
224 224
225 225 phase (optional)
226 226 (bytestring) The phase that a revision is in. Recognized values are
227 227 ``secret``, ``draft``, and ``public``. Only present if ``phase`` data
228 228 is being requested.
229 229
230 230 The set of changeset revisions emitted may not match the exact set of
231 231 changesets requested. Furthermore, the set of keys present on each
232 232 map may vary. This is to facilitate emitting changeset updates as well
233 233 as new revisions.
234 234
235 235 For example, if the request wants ``phase`` and ``revision`` data,
236 236 the response may contain entries for each changeset in the common nodes
237 237 set with the ``phase`` key and without the ``revision`` key in order
238 238 to reflect a phase-only update.
239 239
240 240 TODO support different revision selection mechanisms (e.g. non-public, specific
241 241 revisions)
242 242 TODO support different hash "namespaces" for revisions (e.g. sha-1 versus other)
243 243 TODO support emitting obsolescence data
244 244 TODO support filtering based on relevant paths (narrow clone)
245 245 TODO support hgtagsfnodes cache / tags data
246 246 TODO support branch heads cache
247 247 TODO consider unify query mechanism. e.g. as an array of "query descriptors"
248 248 rather than a set of top-level arguments that have semantics when combined.
249 249
250 250 filedata
251 251 --------
252 252
253 253 Obtain various data related to an individual tracked file.
254 254
255 255 The command accepts the following arguments:
256 256
257 257 fields
258 258 (set of bytestring) Which data associated with a file to fetch.
259 259 The following values are recognized:
260 260
261 261 linknode
262 262 The changeset node introducing this revision.
263 263
264 264 parents
265 265 Parent nodes for the revision.
266 266
267 267 revision
268 268 The raw revision data for a file.
269 269
270 270 haveparents
271 271 (bool) Whether the client has the parent revisions of all requested
272 272 nodes. If set, the server may emit revision data as deltas against
273 273 any parent revision. If not set, the server MUST only emit deltas for
274 274 revisions previously emitted by this command.
275 275
276 276 False is assumed in the absence of any value.
277 277
278 278 nodes
279 279 (array of bytestrings) File nodes whose data to retrieve.
280 280
281 281 path
282 282 (bytestring) Path of the tracked file whose data to retrieve.
283 283
284 284 TODO allow specifying revisions via alternate means (such as from
285 285 changeset revisions or ranges)
286 286
287 287 The response bytestream starts with a CBOR map describing the data that
288 288 follows. It has the following bytestream keys:
289 289
290 290 totalitems
291 291 (unsigned integer) Total number of file revisions whose data is
292 292 being returned.
293 293
294 294 Following the map header is a series of 0 or more CBOR values. If values
295 295 are present, the first value will always be a map describing a single changeset
296 296 revision.
297 297
298 298 If the ``fieldsfollowing`` key is present, the map will immediately be followed
299 299 by N CBOR bytestring values, where N is the number of elements in
300 300 ``fieldsfollowing``. Each bytestring value corresponds to a field denoted
301 301 by ``fieldsfollowing``.
302 302
303 303 Following the optional bytestring field values is the next revision descriptor
304 304 map, or end of stream.
305 305
306 306 Each revision descriptor map has the following bytestring keys:
307 307
308 308 Each map has the following bytestring keys:
309 309
310 310 node
311 311 (bytestring) The node of the file revision whose data is represented.
312 312
313 313 deltabasenode
314 314 (bytestring) Node of the file revision the following delta is against.
315 315
316 316 Only present if the ``revision`` field is requested and delta data
317 317 follows this map.
318 318
319 319 fieldsfollowing
320 320 (array of 2-array) Denotes extra bytestring fields that following this map.
321 321 See the documentation for ``changesetdata`` for semantics.
322 322
323 323 The following named fields may be present:
324 324
325 325 ``delta``
326 326 The delta data to use to construct the fulltext revision.
327 327
328 328 Only present if the ``revision`` field is requested and a delta is
329 329 being emitted. The ``deltabasenode`` top-level key will also be
330 330 present if this field is being emitted.
331 331
332 332 ``revision``
333 333 The fulltext revision data for this manifest. Only present if the
334 334 ``revision`` field is requested and a fulltext revision is being emitted.
335 335
336 336 parents
337 337 (array of bytestring) The nodes of the parents of this file revision.
338 338
339 339 Only present if the ``parents`` field is requested.
340 340
341 341 When ``revision`` data is requested, the server chooses to emit either fulltext
342 342 revision data or a delta. What the server decides can be inferred by looking
343 343 for the presence of the ``delta`` or ``revision`` keys in the
344 344 ``fieldsfollowing`` array.
345 345
346 346 filesdata
347 347 ---------
348 348
349 349 Obtain various data related to multiple tracked files for specific changesets.
350 350
351 351 This command is similar to ``filedata`` with the main difference being that
352 352 individual requests operate on multiple file paths. This allows clients to
353 353 request data for multiple paths by issuing a single command.
354 354
355 355 The command accepts the following arguments:
356 356
357 357 fields
358 358 (set of bytestring) Which data associated with a file to fetch.
359 359 The following values are recognized:
360 360
361 361 linknode
362 362 The changeset node introducing this revision.
363 363
364 364 parents
365 365 Parent nodes for the revision.
366 366
367 367 revision
368 368 The raw revision data for a file.
369 369
370 370 haveparents
371 371 (bool) Whether the client has the parent revisions of all requested
372 372 nodes.
373 373
374 374 pathfilter
375 375 (map) Defines a filter that determines what file paths are relevant.
376 376
377 377 See the *Path Filters* section for more.
378 378
379 379 If the argument is omitted, it is assumed that all paths are relevant.
380 380
381 381 revisions
382 382 (array of maps) Specifies revisions whose data is being requested. Each value
383 383 in the array is a map describing revisions. See the *Revisions Specifiers*
384 384 section below for the format of this map.
385 385
386 386 Data will be sent for the union of all revisions resolved by all revision
387 387 specifiers.
388 388
389 389 Only revision specifiers operating on changeset revisions are allowed.
390 390
391 391 The response bytestream starts with a CBOR map describing the data that
392 392 follows. This map has the following bytestring keys:
393 393
394 394 totalpaths
395 395 (unsigned integer) Total number of paths whose data is being transferred.
396 396
397 397 totalitems
398 398 (unsigned integer) Total number of file revisions whose data is being
399 399 transferred.
400 400
401 401 Following the map header are 0 or more sequences of CBOR values. Each sequence
402 402 represents data for a specific tracked path. Each sequence begins with a CBOR
403 403 map describing the file data that follows. Following that map is N CBOR values
404 404 describing file revision data. The format of this data is identical to that
405 405 returned by the ``filedata`` command.
406 406
407 407 Each sequence's map header has the following bytestring keys:
408 408
409 409 path
410 410 (bytestring) The tracked file path whose data follows.
411 411
412 412 totalitems
413 413 (unsigned integer) Total number of file revisions whose data is being
414 414 transferred.
415 415
416 416 The ``haveparents`` argument has significant implications on the data
417 417 transferred.
418 418
419 419 When ``haveparents`` is true, the command MAY only emit data for file
420 420 revisions introduced by the set of changeset revisions whose data is being
421 421 requested. In other words, the command may assume that all file revisions
422 422 for all relevant paths for ancestors of the requested changeset revisions
423 423 are present on the receiver.
424 424
425 425 When ``haveparents`` is false, the command MUST assume that the receiver
426 426 has no file revisions data. This means that all referenced file revisions
427 427 in the queried set of changeset revisions will be sent.
428 428
429 TODO we'll probably want a more complicated mechanism for the client to
430 specify which ancestor revisions are known.
429 TODO we want a more complicated mechanism for the client to specify which
430 ancestor revisions are known. This is needed so intelligent deltas can be
431 emitted and so updated linknodes can be sent if the client needs to adjust
432 its linknodes for existing file nodes to older changeset revisions.
431 433 TODO we may want to make linknodes an array so multiple changesets can be
432 434 marked as introducing a file revision, since this can occur with e.g. hidden
433 435 changesets.
434 436
435 437 heads
436 438 -----
437 439
438 440 Obtain DAG heads in the repository.
439 441
440 442 The command accepts the following arguments:
441 443
442 444 publiconly (optional)
443 445 (boolean) If set, operate on the DAG for public phase changesets only.
444 446 Non-public (i.e. draft) phase DAG heads will not be returned.
445 447
446 448 The response is a CBOR array of bytestrings defining changeset nodes
447 449 of DAG heads. The array can be empty if the repository is empty or no
448 450 changesets satisfied the request.
449 451
450 452 TODO consider exposing phase of heads in response
451 453
452 454 known
453 455 -----
454 456
455 457 Determine whether a series of changeset nodes is known to the server.
456 458
457 459 The command accepts the following arguments:
458 460
459 461 nodes
460 462 (array of bytestrings) List of changeset nodes whose presence to
461 463 query.
462 464
463 465 The response is a bytestring where each byte contains a 0 or 1 for the
464 466 corresponding requested node at the same index.
465 467
466 468 TODO use a bit array for even more compact response
467 469
468 470 listkeys
469 471 --------
470 472
471 473 List values in a specified ``pushkey`` namespace.
472 474
473 475 The command receives the following arguments:
474 476
475 477 namespace
476 478 (bytestring) Pushkey namespace to query.
477 479
478 480 The response is a map with bytestring keys and values.
479 481
480 482 TODO consider using binary to represent nodes in certain pushkey namespaces.
481 483
482 484 lookup
483 485 ------
484 486
485 487 Try to resolve a value to a changeset revision.
486 488
487 489 Unlike ``known`` which operates on changeset nodes, lookup operates on
488 490 node fragments and other names that a user may use.
489 491
490 492 The command receives the following arguments:
491 493
492 494 key
493 495 (bytestring) Value to try to resolve.
494 496
495 497 On success, returns a bytestring containing the resolved node.
496 498
497 499 manifestdata
498 500 ------------
499 501
500 502 Obtain various data related to manifests (which are lists of files in
501 503 a revision).
502 504
503 505 The command accepts the following arguments:
504 506
505 507 fields
506 508 (set of bytestring) Which data associated with manifests to fetch.
507 509 The following values are recognized:
508 510
509 511 parents
510 512 Parent nodes for the manifest.
511 513
512 514 revision
513 515 The raw revision data for the manifest.
514 516
515 517 haveparents
516 518 (bool) Whether the client has the parent revisions of all requested
517 519 nodes. If set, the server may emit revision data as deltas against
518 520 any parent revision. If not set, the server MUST only emit deltas for
519 521 revisions previously emitted by this command.
520 522
521 523 False is assumed in the absence of any value.
522 524
523 525 nodes
524 526 (array of bytestring) Manifest nodes whose data to retrieve.
525 527
526 528 tree
527 529 (bytestring) Path to manifest to retrieve. The empty bytestring represents
528 530 the root manifest. All other values represent directories/trees within
529 531 the repository.
530 532
531 533 TODO allow specifying revisions via alternate means (such as from changeset
532 534 revisions or ranges)
533 535 TODO consider recursive expansion of manifests (with path filtering for
534 536 narrow use cases)
535 537
536 538 The response bytestream starts with a CBOR map describing the data that
537 539 follows. It has the following bytestring keys:
538 540
539 541 totalitems
540 542 (unsigned integer) Total number of manifest revisions whose data is
541 543 being returned.
542 544
543 545 Following the map header is a series of 0 or more CBOR values. If values
544 546 are present, the first value will always be a map describing a single manifest
545 547 revision.
546 548
547 549 If the ``fieldsfollowing`` key is present, the map will immediately be followed
548 550 by N CBOR bytestring values, where N is the number of elements in
549 551 ``fieldsfollowing``. Each bytestring value corresponds to a field denoted
550 552 by ``fieldsfollowing``.
551 553
552 554 Following the optional bytestring field values is the next revision descriptor
553 555 map, or end of stream.
554 556
555 557 Each revision descriptor map has the following bytestring keys:
556 558
557 559 node
558 560 (bytestring) The node of the manifest revision whose data is represented.
559 561
560 562 deltabasenode
561 563 (bytestring) The node that the delta representation of this revision is
562 564 computed against. Only present if the ``revision`` field is requested and
563 565 a delta is being emitted.
564 566
565 567 fieldsfollowing
566 568 (array of 2-array) Denotes extra bytestring fields that following this map.
567 569 See the documentation for ``changesetdata`` for semantics.
568 570
569 571 The following named fields may be present:
570 572
571 573 ``delta``
572 574 The delta data to use to construct the fulltext revision.
573 575
574 576 Only present if the ``revision`` field is requested and a delta is
575 577 being emitted. The ``deltabasenode`` top-level key will also be
576 578 present if this field is being emitted.
577 579
578 580 ``revision``
579 581 The fulltext revision data for this manifest. Only present if the
580 582 ``revision`` field is requested and a fulltext revision is being emitted.
581 583
582 584 parents
583 585 (array of bytestring) The nodes of the parents of this manifest revision.
584 586 Only present if the ``parents`` field is requested.
585 587
586 588 When ``revision`` data is requested, the server chooses to emit either fulltext
587 589 revision data or a delta. What the server decides can be inferred by looking
588 590 for the presence of ``delta`` or ``revision`` in the ``fieldsfollowing`` array.
589 591
590 592 Servers MAY advertise the following extra fields in the capabilities
591 593 descriptor for this command:
592 594
593 595 recommendedbatchsize
594 596 (unsigned integer) Number of revisions the server recommends as a batch
595 597 query size. If defined, clients needing to issue multiple ``manifestdata``
596 598 commands to obtain needed data SHOULD construct their commands to have
597 599 this many revisions per request.
598 600
599 601 pushkey
600 602 -------
601 603
602 604 Set a value using the ``pushkey`` protocol.
603 605
604 606 The command receives the following arguments:
605 607
606 608 namespace
607 609 (bytestring) Pushkey namespace to operate on.
608 610 key
609 611 (bytestring) The pushkey key to set.
610 612 old
611 613 (bytestring) Old value for this key.
612 614 new
613 615 (bytestring) New value for this key.
614 616
615 617 TODO consider using binary to represent nodes is certain pushkey namespaces.
616 618 TODO better define response type and meaning.
617 619
618 620 rawstorefiledata
619 621 ----------------
620 622
621 623 Allows retrieving raw files used to store repository data.
622 624
623 625 The command accepts the following arguments:
624 626
625 627 files
626 628 (array of bytestring) Describes the files that should be retrieved.
627 629
628 630 The meaning of values in this array is dependent on the storage backend used
629 631 by the server.
630 632
631 633 The response bytestream starts with a CBOR map describing the data that follows.
632 634 This map has the following bytestring keys:
633 635
634 636 filecount
635 637 (unsigned integer) Total number of files whose data is being transferred.
636 638
637 639 totalsize
638 640 (unsigned integer) Total size in bytes of files data that will be
639 641 transferred. This is file on-disk size and not wire size.
640 642
641 643 Following the map header are N file segments. Each file segment consists of a
642 644 CBOR map followed by an indefinite length bytestring. Each map has the following
643 645 bytestring keys:
644 646
645 647 location
646 648 (bytestring) Denotes the location in the repository where the file should be
647 649 written. Values map to vfs instances to use for the writing.
648 650
649 651 path
650 652 (bytestring) Path of file being transferred. Path is the raw store
651 653 path and can be any sequence of bytes that can be tracked in a Mercurial
652 654 manifest.
653 655
654 656 size
655 657 (unsigned integer) Size of file data. This will be the final written
656 658 file size. The total size of the data that follows the CBOR map
657 659 will be greater due to encoding overhead of CBOR.
658 660
659 661 TODO this command is woefully incomplete. If we are to move forward with a
660 662 stream clone analog, it needs a lot more metadata around how to describe what
661 663 files are available to retrieve, other semantics.
662 664
663 665 Revision Specifiers
664 666 ===================
665 667
666 668 A *revision specifier* is a map that evaluates to a set of revisions.
667 669
668 670 A *revision specifier* has a ``type`` key that defines the revision
669 671 selection type to perform. Other keys in the map are used in a
670 672 type-specific manner.
671 673
672 674 The following types are defined:
673 675
674 676 changesetexplicit
675 677 An explicit set of enumerated changeset revisions.
676 678
677 679 The ``nodes`` key MUST contain an array of full binary nodes, expressed
678 680 as bytestrings.
679 681
680 682 changesetexplicitdepth
681 683 Like ``changesetexplicit``, but contains a ``depth`` key defining the
682 684 unsigned integer number of ancestor revisions to also resolve. For each
683 685 value in ``nodes``, DAG ancestors will be walked until up to N total
684 686 revisions from that ancestry walk are present in the final resolved set.
685 687
686 688 changesetdagrange
687 689 Defines revisions via a DAG range of changesets on the changelog.
688 690
689 691 The ``roots`` key MUST contain an array of full, binary node values
690 692 representing the *root* revisions.
691 693
692 694 The ``heads`` key MUST contain an array of full, binary nodes values
693 695 representing the *head* revisions.
694 696
695 697 The DAG range between ``roots`` and ``heads`` will be resolved and all
696 698 revisions between will be used. Nodes in ``roots`` are not part of the
697 699 resolved set. Nodes in ``heads`` are. The ``roots`` array may be empty.
698 700 The ``heads`` array MUST be defined.
699 701
700 702 Path Filters
701 703 ============
702 704
703 705 Various commands accept a *path filter* argument that defines the set of file
704 706 paths relevant to the request.
705 707
706 708 A *path filter* is defined as a map with the bytestring keys ``include`` and
707 709 ``exclude``. Each is an array of bytestring values. Each value defines a pattern
708 710 rule (see :hg:`help patterns`) that is used to match file paths.
709 711
710 712 A path matches the path filter if it is matched by a rule in the ``include``
711 713 set but doesn't match a rule in the ``exclude`` set. In other words, a path
712 714 matcher takes the union of all ``include`` patterns and then substracts the
713 715 union of all ``exclude`` patterns.
714 716
715 717 Patterns MUST be prefixed with their pattern type. Only the following pattern
716 718 types are allowed: ``path:``, ``rootfilesin:``.
717 719
718 720 If the ``include`` key is omitted, it is assumed that all paths are
719 721 relevant. The patterns from ``exclude`` will still be used, if defined.
720 722
721 723 An example value is ``path:tests/foo``, which would match a file named
722 724 ``tests/foo`` or a directory ``tests/foo`` and all files under it.
@@ -1,1465 +1,1455 b''
1 1 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2 2 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 3 #
4 4 # This software may be used and distributed according to the terms of the
5 5 # GNU General Public License version 2 or any later version.
6 6
7 7 from __future__ import absolute_import
8 8
9 9 import collections
10 10 import contextlib
11 11 import hashlib
12 12
13 13 from .i18n import _
14 14 from .node import (
15 15 hex,
16 16 nullid,
17 17 )
18 18 from . import (
19 19 discovery,
20 20 encoding,
21 21 error,
22 22 match as matchmod,
23 23 narrowspec,
24 24 pycompat,
25 25 streamclone,
26 26 util,
27 27 wireprotoframing,
28 28 wireprototypes,
29 29 )
30 30 from .utils import (
31 31 cborutil,
32 32 interfaceutil,
33 33 stringutil,
34 34 )
35 35
36 36 FRAMINGTYPE = b'application/mercurial-exp-framing-0006'
37 37
38 38 HTTP_WIREPROTO_V2 = wireprototypes.HTTP_WIREPROTO_V2
39 39
40 40 COMMANDS = wireprototypes.commanddict()
41 41
42 42 # Value inserted into cache key computation function. Change the value to
43 43 # force new cache keys for every command request. This should be done when
44 44 # there is a change to how caching works, etc.
45 45 GLOBAL_CACHE_VERSION = 1
46 46
47 47 def handlehttpv2request(rctx, req, res, checkperm, urlparts):
48 48 from .hgweb import common as hgwebcommon
49 49
50 50 # URL space looks like: <permissions>/<command>, where <permission> can
51 51 # be ``ro`` or ``rw`` to signal read-only or read-write, respectively.
52 52
53 53 # Root URL does nothing meaningful... yet.
54 54 if not urlparts:
55 55 res.status = b'200 OK'
56 56 res.headers[b'Content-Type'] = b'text/plain'
57 57 res.setbodybytes(_('HTTP version 2 API handler'))
58 58 return
59 59
60 60 if len(urlparts) == 1:
61 61 res.status = b'404 Not Found'
62 62 res.headers[b'Content-Type'] = b'text/plain'
63 63 res.setbodybytes(_('do not know how to process %s\n') %
64 64 req.dispatchpath)
65 65 return
66 66
67 67 permission, command = urlparts[0:2]
68 68
69 69 if permission not in (b'ro', b'rw'):
70 70 res.status = b'404 Not Found'
71 71 res.headers[b'Content-Type'] = b'text/plain'
72 72 res.setbodybytes(_('unknown permission: %s') % permission)
73 73 return
74 74
75 75 if req.method != 'POST':
76 76 res.status = b'405 Method Not Allowed'
77 77 res.headers[b'Allow'] = b'POST'
78 78 res.setbodybytes(_('commands require POST requests'))
79 79 return
80 80
81 81 # At some point we'll want to use our own API instead of recycling the
82 82 # behavior of version 1 of the wire protocol...
83 83 # TODO return reasonable responses - not responses that overload the
84 84 # HTTP status line message for error reporting.
85 85 try:
86 86 checkperm(rctx, req, 'pull' if permission == b'ro' else 'push')
87 87 except hgwebcommon.ErrorResponse as e:
88 88 res.status = hgwebcommon.statusmessage(e.code, pycompat.bytestr(e))
89 89 for k, v in e.headers:
90 90 res.headers[k] = v
91 91 res.setbodybytes('permission denied')
92 92 return
93 93
94 94 # We have a special endpoint to reflect the request back at the client.
95 95 if command == b'debugreflect':
96 96 _processhttpv2reflectrequest(rctx.repo.ui, rctx.repo, req, res)
97 97 return
98 98
99 99 # Extra commands that we handle that aren't really wire protocol
100 100 # commands. Think extra hard before making this hackery available to
101 101 # extension.
102 102 extracommands = {'multirequest'}
103 103
104 104 if command not in COMMANDS and command not in extracommands:
105 105 res.status = b'404 Not Found'
106 106 res.headers[b'Content-Type'] = b'text/plain'
107 107 res.setbodybytes(_('unknown wire protocol command: %s\n') % command)
108 108 return
109 109
110 110 repo = rctx.repo
111 111 ui = repo.ui
112 112
113 113 proto = httpv2protocolhandler(req, ui)
114 114
115 115 if (not COMMANDS.commandavailable(command, proto)
116 116 and command not in extracommands):
117 117 res.status = b'404 Not Found'
118 118 res.headers[b'Content-Type'] = b'text/plain'
119 119 res.setbodybytes(_('invalid wire protocol command: %s') % command)
120 120 return
121 121
122 122 # TODO consider cases where proxies may add additional Accept headers.
123 123 if req.headers.get(b'Accept') != FRAMINGTYPE:
124 124 res.status = b'406 Not Acceptable'
125 125 res.headers[b'Content-Type'] = b'text/plain'
126 126 res.setbodybytes(_('client MUST specify Accept header with value: %s\n')
127 127 % FRAMINGTYPE)
128 128 return
129 129
130 130 if req.headers.get(b'Content-Type') != FRAMINGTYPE:
131 131 res.status = b'415 Unsupported Media Type'
132 132 # TODO we should send a response with appropriate media type,
133 133 # since client does Accept it.
134 134 res.headers[b'Content-Type'] = b'text/plain'
135 135 res.setbodybytes(_('client MUST send Content-Type header with '
136 136 'value: %s\n') % FRAMINGTYPE)
137 137 return
138 138
139 139 _processhttpv2request(ui, repo, req, res, permission, command, proto)
140 140
141 141 def _processhttpv2reflectrequest(ui, repo, req, res):
142 142 """Reads unified frame protocol request and dumps out state to client.
143 143
144 144 This special endpoint can be used to help debug the wire protocol.
145 145
146 146 Instead of routing the request through the normal dispatch mechanism,
147 147 we instead read all frames, decode them, and feed them into our state
148 148 tracker. We then dump the log of all that activity back out to the
149 149 client.
150 150 """
151 151 import json
152 152
153 153 # Reflection APIs have a history of being abused, accidentally disclosing
154 154 # sensitive data, etc. So we have a config knob.
155 155 if not ui.configbool('experimental', 'web.api.debugreflect'):
156 156 res.status = b'404 Not Found'
157 157 res.headers[b'Content-Type'] = b'text/plain'
158 158 res.setbodybytes(_('debugreflect service not available'))
159 159 return
160 160
161 161 # We assume we have a unified framing protocol request body.
162 162
163 163 reactor = wireprotoframing.serverreactor(ui)
164 164 states = []
165 165
166 166 while True:
167 167 frame = wireprotoframing.readframe(req.bodyfh)
168 168
169 169 if not frame:
170 170 states.append(b'received: <no frame>')
171 171 break
172 172
173 173 states.append(b'received: %d %d %d %s' % (frame.typeid, frame.flags,
174 174 frame.requestid,
175 175 frame.payload))
176 176
177 177 action, meta = reactor.onframerecv(frame)
178 178 states.append(json.dumps((action, meta), sort_keys=True,
179 179 separators=(', ', ': ')))
180 180
181 181 action, meta = reactor.oninputeof()
182 182 meta['action'] = action
183 183 states.append(json.dumps(meta, sort_keys=True, separators=(', ',': ')))
184 184
185 185 res.status = b'200 OK'
186 186 res.headers[b'Content-Type'] = b'text/plain'
187 187 res.setbodybytes(b'\n'.join(states))
188 188
189 189 def _processhttpv2request(ui, repo, req, res, authedperm, reqcommand, proto):
190 190 """Post-validation handler for HTTPv2 requests.
191 191
192 192 Called when the HTTP request contains unified frame-based protocol
193 193 frames for evaluation.
194 194 """
195 195 # TODO Some HTTP clients are full duplex and can receive data before
196 196 # the entire request is transmitted. Figure out a way to indicate support
197 197 # for that so we can opt into full duplex mode.
198 198 reactor = wireprotoframing.serverreactor(ui, deferoutput=True)
199 199 seencommand = False
200 200
201 201 outstream = None
202 202
203 203 while True:
204 204 frame = wireprotoframing.readframe(req.bodyfh)
205 205 if not frame:
206 206 break
207 207
208 208 action, meta = reactor.onframerecv(frame)
209 209
210 210 if action == 'wantframe':
211 211 # Need more data before we can do anything.
212 212 continue
213 213 elif action == 'runcommand':
214 214 # Defer creating output stream because we need to wait for
215 215 # protocol settings frames so proper encoding can be applied.
216 216 if not outstream:
217 217 outstream = reactor.makeoutputstream()
218 218
219 219 sentoutput = _httpv2runcommand(ui, repo, req, res, authedperm,
220 220 reqcommand, reactor, outstream,
221 221 meta, issubsequent=seencommand)
222 222
223 223 if sentoutput:
224 224 return
225 225
226 226 seencommand = True
227 227
228 228 elif action == 'error':
229 229 # TODO define proper error mechanism.
230 230 res.status = b'200 OK'
231 231 res.headers[b'Content-Type'] = b'text/plain'
232 232 res.setbodybytes(meta['message'] + b'\n')
233 233 return
234 234 else:
235 235 raise error.ProgrammingError(
236 236 'unhandled action from frame processor: %s' % action)
237 237
238 238 action, meta = reactor.oninputeof()
239 239 if action == 'sendframes':
240 240 # We assume we haven't started sending the response yet. If we're
241 241 # wrong, the response type will raise an exception.
242 242 res.status = b'200 OK'
243 243 res.headers[b'Content-Type'] = FRAMINGTYPE
244 244 res.setbodygen(meta['framegen'])
245 245 elif action == 'noop':
246 246 pass
247 247 else:
248 248 raise error.ProgrammingError('unhandled action from frame processor: %s'
249 249 % action)
250 250
251 251 def _httpv2runcommand(ui, repo, req, res, authedperm, reqcommand, reactor,
252 252 outstream, command, issubsequent):
253 253 """Dispatch a wire protocol command made from HTTPv2 requests.
254 254
255 255 The authenticated permission (``authedperm``) along with the original
256 256 command from the URL (``reqcommand``) are passed in.
257 257 """
258 258 # We already validated that the session has permissions to perform the
259 259 # actions in ``authedperm``. In the unified frame protocol, the canonical
260 260 # command to run is expressed in a frame. However, the URL also requested
261 261 # to run a specific command. We need to be careful that the command we
262 262 # run doesn't have permissions requirements greater than what was granted
263 263 # by ``authedperm``.
264 264 #
265 265 # Our rule for this is we only allow one command per HTTP request and
266 266 # that command must match the command in the URL. However, we make
267 267 # an exception for the ``multirequest`` URL. This URL is allowed to
268 268 # execute multiple commands. We double check permissions of each command
269 269 # as it is invoked to ensure there is no privilege escalation.
270 270 # TODO consider allowing multiple commands to regular command URLs
271 271 # iff each command is the same.
272 272
273 273 proto = httpv2protocolhandler(req, ui, args=command['args'])
274 274
275 275 if reqcommand == b'multirequest':
276 276 if not COMMANDS.commandavailable(command['command'], proto):
277 277 # TODO proper error mechanism
278 278 res.status = b'200 OK'
279 279 res.headers[b'Content-Type'] = b'text/plain'
280 280 res.setbodybytes(_('wire protocol command not available: %s') %
281 281 command['command'])
282 282 return True
283 283
284 284 # TODO don't use assert here, since it may be elided by -O.
285 285 assert authedperm in (b'ro', b'rw')
286 286 wirecommand = COMMANDS[command['command']]
287 287 assert wirecommand.permission in ('push', 'pull')
288 288
289 289 if authedperm == b'ro' and wirecommand.permission != 'pull':
290 290 # TODO proper error mechanism
291 291 res.status = b'403 Forbidden'
292 292 res.headers[b'Content-Type'] = b'text/plain'
293 293 res.setbodybytes(_('insufficient permissions to execute '
294 294 'command: %s') % command['command'])
295 295 return True
296 296
297 297 # TODO should we also call checkperm() here? Maybe not if we're going
298 298 # to overhaul that API. The granted scope from the URL check should
299 299 # be good enough.
300 300
301 301 else:
302 302 # Don't allow multiple commands outside of ``multirequest`` URL.
303 303 if issubsequent:
304 304 # TODO proper error mechanism
305 305 res.status = b'200 OK'
306 306 res.headers[b'Content-Type'] = b'text/plain'
307 307 res.setbodybytes(_('multiple commands cannot be issued to this '
308 308 'URL'))
309 309 return True
310 310
311 311 if reqcommand != command['command']:
312 312 # TODO define proper error mechanism
313 313 res.status = b'200 OK'
314 314 res.headers[b'Content-Type'] = b'text/plain'
315 315 res.setbodybytes(_('command in frame must match command in URL'))
316 316 return True
317 317
318 318 res.status = b'200 OK'
319 319 res.headers[b'Content-Type'] = FRAMINGTYPE
320 320
321 321 try:
322 322 objs = dispatch(repo, proto, command['command'], command['redirect'])
323 323
324 324 action, meta = reactor.oncommandresponsereadyobjects(
325 325 outstream, command['requestid'], objs)
326 326
327 327 except error.WireprotoCommandError as e:
328 328 action, meta = reactor.oncommanderror(
329 329 outstream, command['requestid'], e.message, e.messageargs)
330 330
331 331 except Exception as e:
332 332 action, meta = reactor.onservererror(
333 333 outstream, command['requestid'],
334 334 _('exception when invoking command: %s') %
335 335 stringutil.forcebytestr(e))
336 336
337 337 if action == 'sendframes':
338 338 res.setbodygen(meta['framegen'])
339 339 return True
340 340 elif action == 'noop':
341 341 return False
342 342 else:
343 343 raise error.ProgrammingError('unhandled event from reactor: %s' %
344 344 action)
345 345
346 346 def getdispatchrepo(repo, proto, command):
347 347 return repo.filtered('served')
348 348
349 349 def dispatch(repo, proto, command, redirect):
350 350 """Run a wire protocol command.
351 351
352 352 Returns an iterable of objects that will be sent to the client.
353 353 """
354 354 repo = getdispatchrepo(repo, proto, command)
355 355
356 356 entry = COMMANDS[command]
357 357 func = entry.func
358 358 spec = entry.args
359 359
360 360 args = proto.getargs(spec)
361 361
362 362 # There is some duplicate boilerplate code here for calling the command and
363 363 # emitting objects. It is either that or a lot of indented code that looks
364 364 # like a pyramid (since there are a lot of code paths that result in not
365 365 # using the cacher).
366 366 callcommand = lambda: func(repo, proto, **pycompat.strkwargs(args))
367 367
368 368 # Request is not cacheable. Don't bother instantiating a cacher.
369 369 if not entry.cachekeyfn:
370 370 for o in callcommand():
371 371 yield o
372 372 return
373 373
374 374 if redirect:
375 375 redirecttargets = redirect[b'targets']
376 376 redirecthashes = redirect[b'hashes']
377 377 else:
378 378 redirecttargets = []
379 379 redirecthashes = []
380 380
381 381 cacher = makeresponsecacher(repo, proto, command, args,
382 382 cborutil.streamencode,
383 383 redirecttargets=redirecttargets,
384 384 redirecthashes=redirecthashes)
385 385
386 386 # But we have no cacher. Do default handling.
387 387 if not cacher:
388 388 for o in callcommand():
389 389 yield o
390 390 return
391 391
392 392 with cacher:
393 393 cachekey = entry.cachekeyfn(repo, proto, cacher, **args)
394 394
395 395 # No cache key or the cacher doesn't like it. Do default handling.
396 396 if cachekey is None or not cacher.setcachekey(cachekey):
397 397 for o in callcommand():
398 398 yield o
399 399 return
400 400
401 401 # Serve it from the cache, if possible.
402 402 cached = cacher.lookup()
403 403
404 404 if cached:
405 405 for o in cached['objs']:
406 406 yield o
407 407 return
408 408
409 409 # Else call the command and feed its output into the cacher, allowing
410 410 # the cacher to buffer/mutate objects as it desires.
411 411 for o in callcommand():
412 412 for o in cacher.onobject(o):
413 413 yield o
414 414
415 415 for o in cacher.onfinished():
416 416 yield o
417 417
418 418 @interfaceutil.implementer(wireprototypes.baseprotocolhandler)
419 419 class httpv2protocolhandler(object):
420 420 def __init__(self, req, ui, args=None):
421 421 self._req = req
422 422 self._ui = ui
423 423 self._args = args
424 424
425 425 @property
426 426 def name(self):
427 427 return HTTP_WIREPROTO_V2
428 428
429 429 def getargs(self, args):
430 430 # First look for args that were passed but aren't registered on this
431 431 # command.
432 432 extra = set(self._args) - set(args)
433 433 if extra:
434 434 raise error.WireprotoCommandError(
435 435 'unsupported argument to command: %s' %
436 436 ', '.join(sorted(extra)))
437 437
438 438 # And look for required arguments that are missing.
439 439 missing = {a for a in args if args[a]['required']} - set(self._args)
440 440
441 441 if missing:
442 442 raise error.WireprotoCommandError(
443 443 'missing required arguments: %s' % ', '.join(sorted(missing)))
444 444
445 445 # Now derive the arguments to pass to the command, taking into
446 446 # account the arguments specified by the client.
447 447 data = {}
448 448 for k, meta in sorted(args.items()):
449 449 # This argument wasn't passed by the client.
450 450 if k not in self._args:
451 451 data[k] = meta['default']()
452 452 continue
453 453
454 454 v = self._args[k]
455 455
456 456 # Sets may be expressed as lists. Silently normalize.
457 457 if meta['type'] == 'set' and isinstance(v, list):
458 458 v = set(v)
459 459
460 460 # TODO consider more/stronger type validation.
461 461
462 462 data[k] = v
463 463
464 464 return data
465 465
466 466 def getprotocaps(self):
467 467 # Protocol capabilities are currently not implemented for HTTP V2.
468 468 return set()
469 469
470 470 def getpayload(self):
471 471 raise NotImplementedError
472 472
473 473 @contextlib.contextmanager
474 474 def mayberedirectstdio(self):
475 475 raise NotImplementedError
476 476
477 477 def client(self):
478 478 raise NotImplementedError
479 479
480 480 def addcapabilities(self, repo, caps):
481 481 return caps
482 482
483 483 def checkperm(self, perm):
484 484 raise NotImplementedError
485 485
486 486 def httpv2apidescriptor(req, repo):
487 487 proto = httpv2protocolhandler(req, repo.ui)
488 488
489 489 return _capabilitiesv2(repo, proto)
490 490
491 491 def _capabilitiesv2(repo, proto):
492 492 """Obtain the set of capabilities for version 2 transports.
493 493
494 494 These capabilities are distinct from the capabilities for version 1
495 495 transports.
496 496 """
497 497 caps = {
498 498 'commands': {},
499 499 'framingmediatypes': [FRAMINGTYPE],
500 500 'pathfilterprefixes': set(narrowspec.VALID_PREFIXES),
501 501 }
502 502
503 503 for command, entry in COMMANDS.items():
504 504 args = {}
505 505
506 506 for arg, meta in entry.args.items():
507 507 args[arg] = {
508 508 # TODO should this be a normalized type using CBOR's
509 509 # terminology?
510 510 b'type': meta['type'],
511 511 b'required': meta['required'],
512 512 }
513 513
514 514 if not meta['required']:
515 515 args[arg][b'default'] = meta['default']()
516 516
517 517 if meta['validvalues']:
518 518 args[arg][b'validvalues'] = meta['validvalues']
519 519
520 520 # TODO this type of check should be defined in a per-command callback.
521 521 if (command == b'rawstorefiledata'
522 522 and not streamclone.allowservergeneration(repo)):
523 523 continue
524 524
525 525 caps['commands'][command] = {
526 526 'args': args,
527 527 'permissions': [entry.permission],
528 528 }
529 529
530 530 if entry.extracapabilitiesfn:
531 531 extracaps = entry.extracapabilitiesfn(repo, proto)
532 532 caps['commands'][command].update(extracaps)
533 533
534 534 caps['rawrepoformats'] = sorted(repo.requirements &
535 535 repo.supportedformats)
536 536
537 537 targets = getadvertisedredirecttargets(repo, proto)
538 538 if targets:
539 539 caps[b'redirect'] = {
540 540 b'targets': [],
541 541 b'hashes': [b'sha256', b'sha1'],
542 542 }
543 543
544 544 for target in targets:
545 545 entry = {
546 546 b'name': target['name'],
547 547 b'protocol': target['protocol'],
548 548 b'uris': target['uris'],
549 549 }
550 550
551 551 for key in ('snirequired', 'tlsversions'):
552 552 if key in target:
553 553 entry[key] = target[key]
554 554
555 555 caps[b'redirect'][b'targets'].append(entry)
556 556
557 557 return proto.addcapabilities(repo, caps)
558 558
559 559 def getadvertisedredirecttargets(repo, proto):
560 560 """Obtain a list of content redirect targets.
561 561
562 562 Returns a list containing potential redirect targets that will be
563 563 advertised in capabilities data. Each dict MUST have the following
564 564 keys:
565 565
566 566 name
567 567 The name of this redirect target. This is the identifier clients use
568 568 to refer to a target. It is transferred as part of every command
569 569 request.
570 570
571 571 protocol
572 572 Network protocol used by this target. Typically this is the string
573 573 in front of the ``://`` in a URL. e.g. ``https``.
574 574
575 575 uris
576 576 List of representative URIs for this target. Clients can use the
577 577 URIs to test parsing for compatibility or for ordering preference
578 578 for which target to use.
579 579
580 580 The following optional keys are recognized:
581 581
582 582 snirequired
583 583 Bool indicating if Server Name Indication (SNI) is required to
584 584 connect to this target.
585 585
586 586 tlsversions
587 587 List of bytes indicating which TLS versions are supported by this
588 588 target.
589 589
590 590 By default, clients reflect the target order advertised by servers
591 591 and servers will use the first client-advertised target when picking
592 592 a redirect target. So targets should be advertised in the order the
593 593 server prefers they be used.
594 594 """
595 595 return []
596 596
597 597 def wireprotocommand(name, args=None, permission='push', cachekeyfn=None,
598 598 extracapabilitiesfn=None):
599 599 """Decorator to declare a wire protocol command.
600 600
601 601 ``name`` is the name of the wire protocol command being provided.
602 602
603 603 ``args`` is a dict defining arguments accepted by the command. Keys are
604 604 the argument name. Values are dicts with the following keys:
605 605
606 606 ``type``
607 607 The argument data type. Must be one of the following string
608 608 literals: ``bytes``, ``int``, ``list``, ``dict``, ``set``,
609 609 or ``bool``.
610 610
611 611 ``default``
612 612 A callable returning the default value for this argument. If not
613 613 specified, ``None`` will be the default value.
614 614
615 615 ``example``
616 616 An example value for this argument.
617 617
618 618 ``validvalues``
619 619 Set of recognized values for this argument.
620 620
621 621 ``permission`` defines the permission type needed to run this command.
622 622 Can be ``push`` or ``pull``. These roughly map to read-write and read-only,
623 623 respectively. Default is to assume command requires ``push`` permissions
624 624 because otherwise commands not declaring their permissions could modify
625 625 a repository that is supposed to be read-only.
626 626
627 627 ``cachekeyfn`` defines an optional callable that can derive the
628 628 cache key for this request.
629 629
630 630 ``extracapabilitiesfn`` defines an optional callable that defines extra
631 631 command capabilities/parameters that are advertised next to the command
632 632 in the capabilities data structure describing the server. The callable
633 633 receives as arguments the repository and protocol objects. It returns
634 634 a dict of extra fields to add to the command descriptor.
635 635
636 636 Wire protocol commands are generators of objects to be serialized and
637 637 sent to the client.
638 638
639 639 If a command raises an uncaught exception, this will be translated into
640 640 a command error.
641 641
642 642 All commands can opt in to being cacheable by defining a function
643 643 (``cachekeyfn``) that is called to derive a cache key. This function
644 644 receives the same arguments as the command itself plus a ``cacher``
645 645 argument containing the active cacher for the request and returns a bytes
646 646 containing the key in a cache the response to this command may be cached
647 647 under.
648 648 """
649 649 transports = {k for k, v in wireprototypes.TRANSPORTS.items()
650 650 if v['version'] == 2}
651 651
652 652 if permission not in ('push', 'pull'):
653 653 raise error.ProgrammingError('invalid wire protocol permission; '
654 654 'got %s; expected "push" or "pull"' %
655 655 permission)
656 656
657 657 if args is None:
658 658 args = {}
659 659
660 660 if not isinstance(args, dict):
661 661 raise error.ProgrammingError('arguments for version 2 commands '
662 662 'must be declared as dicts')
663 663
664 664 for arg, meta in args.items():
665 665 if arg == '*':
666 666 raise error.ProgrammingError('* argument name not allowed on '
667 667 'version 2 commands')
668 668
669 669 if not isinstance(meta, dict):
670 670 raise error.ProgrammingError('arguments for version 2 commands '
671 671 'must declare metadata as a dict')
672 672
673 673 if 'type' not in meta:
674 674 raise error.ProgrammingError('%s argument for command %s does not '
675 675 'declare type field' % (arg, name))
676 676
677 677 if meta['type'] not in ('bytes', 'int', 'list', 'dict', 'set', 'bool'):
678 678 raise error.ProgrammingError('%s argument for command %s has '
679 679 'illegal type: %s' % (arg, name,
680 680 meta['type']))
681 681
682 682 if 'example' not in meta:
683 683 raise error.ProgrammingError('%s argument for command %s does not '
684 684 'declare example field' % (arg, name))
685 685
686 686 meta['required'] = 'default' not in meta
687 687
688 688 meta.setdefault('default', lambda: None)
689 689 meta.setdefault('validvalues', None)
690 690
691 691 def register(func):
692 692 if name in COMMANDS:
693 693 raise error.ProgrammingError('%s command already registered '
694 694 'for version 2' % name)
695 695
696 696 COMMANDS[name] = wireprototypes.commandentry(
697 697 func, args=args, transports=transports, permission=permission,
698 698 cachekeyfn=cachekeyfn, extracapabilitiesfn=extracapabilitiesfn)
699 699
700 700 return func
701 701
702 702 return register
703 703
704 704 def makecommandcachekeyfn(command, localversion=None, allargs=False):
705 705 """Construct a cache key derivation function with common features.
706 706
707 707 By default, the cache key is a hash of:
708 708
709 709 * The command name.
710 710 * A global cache version number.
711 711 * A local cache version number (passed via ``localversion``).
712 712 * All the arguments passed to the command.
713 713 * The media type used.
714 714 * Wire protocol version string.
715 715 * The repository path.
716 716 """
717 717 if not allargs:
718 718 raise error.ProgrammingError('only allargs=True is currently supported')
719 719
720 720 if localversion is None:
721 721 raise error.ProgrammingError('must set localversion argument value')
722 722
723 723 def cachekeyfn(repo, proto, cacher, **args):
724 724 spec = COMMANDS[command]
725 725
726 726 # Commands that mutate the repo can not be cached.
727 727 if spec.permission == 'push':
728 728 return None
729 729
730 730 # TODO config option to disable caching.
731 731
732 732 # Our key derivation strategy is to construct a data structure
733 733 # holding everything that could influence cacheability and to hash
734 734 # the CBOR representation of that. Using CBOR seems like it might
735 735 # be overkill. However, simpler hashing mechanisms are prone to
736 736 # duplicate input issues. e.g. if you just concatenate two values,
737 737 # "foo"+"bar" is identical to "fo"+"obar". Using CBOR provides
738 738 # "padding" between values and prevents these problems.
739 739
740 740 # Seed the hash with various data.
741 741 state = {
742 742 # To invalidate all cache keys.
743 743 b'globalversion': GLOBAL_CACHE_VERSION,
744 744 # More granular cache key invalidation.
745 745 b'localversion': localversion,
746 746 # Cache keys are segmented by command.
747 747 b'command': pycompat.sysbytes(command),
748 748 # Throw in the media type and API version strings so changes
749 749 # to exchange semantics invalid cache.
750 750 b'mediatype': FRAMINGTYPE,
751 751 b'version': HTTP_WIREPROTO_V2,
752 752 # So same requests for different repos don't share cache keys.
753 753 b'repo': repo.root,
754 754 }
755 755
756 756 # The arguments passed to us will have already been normalized.
757 757 # Default values will be set, etc. This is important because it
758 758 # means that it doesn't matter if clients send an explicit argument
759 759 # or rely on the default value: it will all normalize to the same
760 760 # set of arguments on the server and therefore the same cache key.
761 761 #
762 762 # Arguments by their very nature must support being encoded to CBOR.
763 763 # And the CBOR encoder is deterministic. So we hash the arguments
764 764 # by feeding the CBOR of their representation into the hasher.
765 765 if allargs:
766 766 state[b'args'] = pycompat.byteskwargs(args)
767 767
768 768 cacher.adjustcachekeystate(state)
769 769
770 770 hasher = hashlib.sha1()
771 771 for chunk in cborutil.streamencode(state):
772 772 hasher.update(chunk)
773 773
774 774 return pycompat.sysbytes(hasher.hexdigest())
775 775
776 776 return cachekeyfn
777 777
778 778 def makeresponsecacher(repo, proto, command, args, objencoderfn,
779 779 redirecttargets, redirecthashes):
780 780 """Construct a cacher for a cacheable command.
781 781
782 782 Returns an ``iwireprotocolcommandcacher`` instance.
783 783
784 784 Extensions can monkeypatch this function to provide custom caching
785 785 backends.
786 786 """
787 787 return None
788 788
789 789 def resolvenodes(repo, revisions):
790 790 """Resolve nodes from a revisions specifier data structure."""
791 791 cl = repo.changelog
792 792 clhasnode = cl.hasnode
793 793
794 794 seen = set()
795 795 nodes = []
796 796
797 797 if not isinstance(revisions, list):
798 798 raise error.WireprotoCommandError('revisions must be defined as an '
799 799 'array')
800 800
801 801 for spec in revisions:
802 802 if b'type' not in spec:
803 803 raise error.WireprotoCommandError(
804 804 'type key not present in revision specifier')
805 805
806 806 typ = spec[b'type']
807 807
808 808 if typ == b'changesetexplicit':
809 809 if b'nodes' not in spec:
810 810 raise error.WireprotoCommandError(
811 811 'nodes key not present in changesetexplicit revision '
812 812 'specifier')
813 813
814 814 for node in spec[b'nodes']:
815 815 if node not in seen:
816 816 nodes.append(node)
817 817 seen.add(node)
818 818
819 819 elif typ == b'changesetexplicitdepth':
820 820 for key in (b'nodes', b'depth'):
821 821 if key not in spec:
822 822 raise error.WireprotoCommandError(
823 823 '%s key not present in changesetexplicitdepth revision '
824 824 'specifier', (key,))
825 825
826 826 for rev in repo.revs(b'ancestors(%ln, %d)', spec[b'nodes'],
827 827 spec[b'depth'] - 1):
828 828 node = cl.node(rev)
829 829
830 830 if node not in seen:
831 831 nodes.append(node)
832 832 seen.add(node)
833 833
834 834 elif typ == b'changesetdagrange':
835 835 for key in (b'roots', b'heads'):
836 836 if key not in spec:
837 837 raise error.WireprotoCommandError(
838 838 '%s key not present in changesetdagrange revision '
839 839 'specifier', (key,))
840 840
841 841 if not spec[b'heads']:
842 842 raise error.WireprotoCommandError(
843 843 'heads key in changesetdagrange cannot be empty')
844 844
845 845 if spec[b'roots']:
846 846 common = [n for n in spec[b'roots'] if clhasnode(n)]
847 847 else:
848 848 common = [nullid]
849 849
850 850 for n in discovery.outgoing(repo, common, spec[b'heads']).missing:
851 851 if n not in seen:
852 852 nodes.append(n)
853 853 seen.add(n)
854 854
855 855 else:
856 856 raise error.WireprotoCommandError(
857 857 'unknown revision specifier type: %s', (typ,))
858 858
859 859 return nodes
860 860
861 861 @wireprotocommand('branchmap', permission='pull')
862 862 def branchmapv2(repo, proto):
863 863 yield {encoding.fromlocal(k): v
864 864 for k, v in repo.branchmap().iteritems()}
865 865
866 866 @wireprotocommand('capabilities', permission='pull')
867 867 def capabilitiesv2(repo, proto):
868 868 yield _capabilitiesv2(repo, proto)
869 869
870 870 @wireprotocommand(
871 871 'changesetdata',
872 872 args={
873 873 'revisions': {
874 874 'type': 'list',
875 875 'example': [{
876 876 b'type': b'changesetexplicit',
877 877 b'nodes': [b'abcdef...'],
878 878 }],
879 879 },
880 880 'fields': {
881 881 'type': 'set',
882 882 'default': set,
883 883 'example': {b'parents', b'revision'},
884 884 'validvalues': {b'bookmarks', b'parents', b'phase', b'revision'},
885 885 },
886 886 },
887 887 permission='pull')
888 888 def changesetdata(repo, proto, revisions, fields):
889 889 # TODO look for unknown fields and abort when they can't be serviced.
890 890 # This could probably be validated by dispatcher using validvalues.
891 891
892 892 cl = repo.changelog
893 893 outgoing = resolvenodes(repo, revisions)
894 894 publishing = repo.publishing()
895 895
896 896 if outgoing:
897 897 repo.hook('preoutgoing', throw=True, source='serve')
898 898
899 899 yield {
900 900 b'totalitems': len(outgoing),
901 901 }
902 902
903 903 # The phases of nodes already transferred to the client may have changed
904 904 # since the client last requested data. We send phase-only records
905 905 # for these revisions, if requested.
906 906 # TODO actually do this. We'll probably want to emit phase heads
907 907 # in the ancestry set of the outgoing revisions. This will ensure
908 908 # that phase updates within that set are seen.
909 909 if b'phase' in fields:
910 910 pass
911 911
912 912 nodebookmarks = {}
913 913 for mark, node in repo._bookmarks.items():
914 914 nodebookmarks.setdefault(node, set()).add(mark)
915 915
916 916 # It is already topologically sorted by revision number.
917 917 for node in outgoing:
918 918 d = {
919 919 b'node': node,
920 920 }
921 921
922 922 if b'parents' in fields:
923 923 d[b'parents'] = cl.parents(node)
924 924
925 925 if b'phase' in fields:
926 926 if publishing:
927 927 d[b'phase'] = b'public'
928 928 else:
929 929 ctx = repo[node]
930 930 d[b'phase'] = ctx.phasestr()
931 931
932 932 if b'bookmarks' in fields and node in nodebookmarks:
933 933 d[b'bookmarks'] = sorted(nodebookmarks[node])
934 934 del nodebookmarks[node]
935 935
936 936 followingmeta = []
937 937 followingdata = []
938 938
939 939 if b'revision' in fields:
940 940 revisiondata = cl.revision(node, raw=True)
941 941 followingmeta.append((b'revision', len(revisiondata)))
942 942 followingdata.append(revisiondata)
943 943
944 944 # TODO make it possible for extensions to wrap a function or register
945 945 # a handler to service custom fields.
946 946
947 947 if followingmeta:
948 948 d[b'fieldsfollowing'] = followingmeta
949 949
950 950 yield d
951 951
952 952 for extra in followingdata:
953 953 yield extra
954 954
955 955 # If requested, send bookmarks from nodes that didn't have revision
956 956 # data sent so receiver is aware of any bookmark updates.
957 957 if b'bookmarks' in fields:
958 958 for node, marks in sorted(nodebookmarks.iteritems()):
959 959 yield {
960 960 b'node': node,
961 961 b'bookmarks': sorted(marks),
962 962 }
963 963
964 964 class FileAccessError(Exception):
965 965 """Represents an error accessing a specific file."""
966 966
967 967 def __init__(self, path, msg, args):
968 968 self.path = path
969 969 self.msg = msg
970 970 self.args = args
971 971
972 972 def getfilestore(repo, proto, path):
973 973 """Obtain a file storage object for use with wire protocol.
974 974
975 975 Exists as a standalone function so extensions can monkeypatch to add
976 976 access control.
977 977 """
978 978 # This seems to work even if the file doesn't exist. So catch
979 979 # "empty" files and return an error.
980 980 fl = repo.file(path)
981 981
982 982 if not len(fl):
983 983 raise FileAccessError(path, 'unknown file: %s', (path,))
984 984
985 985 return fl
986 986
987 987 def emitfilerevisions(repo, path, revisions, linknodes, fields):
988 988 for revision in revisions:
989 989 d = {
990 990 b'node': revision.node,
991 991 }
992 992
993 993 if b'parents' in fields:
994 994 d[b'parents'] = [revision.p1node, revision.p2node]
995 995
996 996 if b'linknode' in fields:
997 997 d[b'linknode'] = linknodes[revision.node]
998 998
999 999 followingmeta = []
1000 1000 followingdata = []
1001 1001
1002 1002 if b'revision' in fields:
1003 1003 if revision.revision is not None:
1004 1004 followingmeta.append((b'revision', len(revision.revision)))
1005 1005 followingdata.append(revision.revision)
1006 1006 else:
1007 1007 d[b'deltabasenode'] = revision.basenode
1008 1008 followingmeta.append((b'delta', len(revision.delta)))
1009 1009 followingdata.append(revision.delta)
1010 1010
1011 1011 if followingmeta:
1012 1012 d[b'fieldsfollowing'] = followingmeta
1013 1013
1014 1014 yield d
1015 1015
1016 1016 for extra in followingdata:
1017 1017 yield extra
1018 1018
1019 1019 def makefilematcher(repo, pathfilter):
1020 1020 """Construct a matcher from a path filter dict."""
1021 1021
1022 1022 # Validate values.
1023 1023 if pathfilter:
1024 1024 for key in (b'include', b'exclude'):
1025 1025 for pattern in pathfilter.get(key, []):
1026 1026 if not pattern.startswith((b'path:', b'rootfilesin:')):
1027 1027 raise error.WireprotoCommandError(
1028 1028 '%s pattern must begin with `path:` or `rootfilesin:`; '
1029 1029 'got %s', (key, pattern))
1030 1030
1031 1031 if pathfilter:
1032 1032 matcher = matchmod.match(repo.root, b'',
1033 1033 include=pathfilter.get(b'include', []),
1034 1034 exclude=pathfilter.get(b'exclude', []))
1035 1035 else:
1036 1036 matcher = matchmod.match(repo.root, b'')
1037 1037
1038 1038 # Requested patterns could include files not in the local store. So
1039 1039 # filter those out.
1040 1040 return repo.narrowmatch(matcher)
1041 1041
1042 1042 @wireprotocommand(
1043 1043 'filedata',
1044 1044 args={
1045 1045 'haveparents': {
1046 1046 'type': 'bool',
1047 1047 'default': lambda: False,
1048 1048 'example': True,
1049 1049 },
1050 1050 'nodes': {
1051 1051 'type': 'list',
1052 1052 'example': [b'0123456...'],
1053 1053 },
1054 1054 'fields': {
1055 1055 'type': 'set',
1056 1056 'default': set,
1057 1057 'example': {b'parents', b'revision'},
1058 1058 'validvalues': {b'parents', b'revision', b'linknode'},
1059 1059 },
1060 1060 'path': {
1061 1061 'type': 'bytes',
1062 1062 'example': b'foo.txt',
1063 1063 }
1064 1064 },
1065 1065 permission='pull',
1066 1066 # TODO censoring a file revision won't invalidate the cache.
1067 1067 # Figure out a way to take censoring into account when deriving
1068 1068 # the cache key.
1069 1069 cachekeyfn=makecommandcachekeyfn('filedata', 1, allargs=True))
1070 1070 def filedata(repo, proto, haveparents, nodes, fields, path):
1071 1071 # TODO this API allows access to file revisions that are attached to
1072 1072 # secret changesets. filesdata does not have this problem. Maybe this
1073 1073 # API should be deleted?
1074 1074
1075 1075 try:
1076 1076 # Extensions may wish to access the protocol handler.
1077 1077 store = getfilestore(repo, proto, path)
1078 1078 except FileAccessError as e:
1079 1079 raise error.WireprotoCommandError(e.msg, e.args)
1080 1080
1081 1081 clnode = repo.changelog.node
1082 1082 linknodes = {}
1083 1083
1084 1084 # Validate requested nodes.
1085 1085 for node in nodes:
1086 1086 try:
1087 1087 store.rev(node)
1088 1088 except error.LookupError:
1089 1089 raise error.WireprotoCommandError('unknown file node: %s',
1090 1090 (hex(node),))
1091 1091
1092 1092 # TODO by creating the filectx against a specific file revision
1093 1093 # instead of changeset, linkrev() is always used. This is wrong for
1094 1094 # cases where linkrev() may refer to a hidden changeset. But since this
1095 1095 # API doesn't know anything about changesets, we're not sure how to
1096 1096 # disambiguate the linknode. Perhaps we should delete this API?
1097 1097 fctx = repo.filectx(path, fileid=node)
1098 1098 linknodes[node] = clnode(fctx.introrev())
1099 1099
1100 1100 revisions = store.emitrevisions(nodes,
1101 1101 revisiondata=b'revision' in fields,
1102 1102 assumehaveparentrevisions=haveparents)
1103 1103
1104 1104 yield {
1105 1105 b'totalitems': len(nodes),
1106 1106 }
1107 1107
1108 1108 for o in emitfilerevisions(repo, path, revisions, linknodes, fields):
1109 1109 yield o
1110 1110
1111 1111 def filesdatacapabilities(repo, proto):
1112 1112 batchsize = repo.ui.configint(
1113 1113 b'experimental', b'server.filesdata.recommended-batch-size')
1114 1114 return {
1115 1115 b'recommendedbatchsize': batchsize,
1116 1116 }
1117 1117
1118 1118 @wireprotocommand(
1119 1119 'filesdata',
1120 1120 args={
1121 1121 'haveparents': {
1122 1122 'type': 'bool',
1123 1123 'default': lambda: False,
1124 1124 'example': True,
1125 1125 },
1126 1126 'fields': {
1127 1127 'type': 'set',
1128 1128 'default': set,
1129 1129 'example': {b'parents', b'revision'},
1130 1130 'validvalues': {b'firstchangeset', b'linknode', b'parents',
1131 1131 b'revision'},
1132 1132 },
1133 1133 'pathfilter': {
1134 1134 'type': 'dict',
1135 1135 'default': lambda: None,
1136 1136 'example': {b'include': [b'path:tests']},
1137 1137 },
1138 1138 'revisions': {
1139 1139 'type': 'list',
1140 1140 'example': [{
1141 1141 b'type': b'changesetexplicit',
1142 1142 b'nodes': [b'abcdef...'],
1143 1143 }],
1144 1144 },
1145 1145 },
1146 1146 permission='pull',
1147 1147 # TODO censoring a file revision won't invalidate the cache.
1148 1148 # Figure out a way to take censoring into account when deriving
1149 1149 # the cache key.
1150 1150 cachekeyfn=makecommandcachekeyfn('filesdata', 1, allargs=True),
1151 1151 extracapabilitiesfn=filesdatacapabilities)
1152 1152 def filesdata(repo, proto, haveparents, fields, pathfilter, revisions):
1153 1153 # TODO This should operate on a repo that exposes obsolete changesets. There
1154 1154 # is a race between a client making a push that obsoletes a changeset and
1155 1155 # another client fetching files data for that changeset. If a client has a
1156 1156 # changeset, it should probably be allowed to access files data for that
1157 1157 # changeset.
1158 1158
1159 cl = repo.changelog
1160 clnode = cl.node
1161 1159 outgoing = resolvenodes(repo, revisions)
1162 1160 filematcher = makefilematcher(repo, pathfilter)
1163 1161
1164 # Figure out what needs to be emitted.
1165 changedpaths = set()
1166 1162 # path -> {fnode: linknode}
1167 1163 fnodes = collections.defaultdict(dict)
1168 1164
1165 # We collect the set of relevant file revisions by iterating the changeset
1166 # revisions and either walking the set of files recorded in the changeset
1167 # or by walking the manifest at that revision. There is probably room for a
1168 # storage-level API to request this data, as it can be expensive to compute
1169 # and would benefit from caching or alternate storage from what revlogs
1170 # provide.
1169 1171 for node in outgoing:
1170 1172 ctx = repo[node]
1171 changedpaths.update(ctx.files())
1172
1173 changedpaths = sorted(p for p in changedpaths if filematcher(p))
1173 mctx = ctx.manifestctx()
1174 md = mctx.read()
1174 1175
1175 # If ancestors are known, we send file revisions having a linkrev in the
1176 # outgoing set of changeset revisions.
1177 if haveparents:
1178 outgoingclrevs = set(cl.rev(n) for n in outgoing)
1179
1180 for path in changedpaths:
1181 try:
1182 store = getfilestore(repo, proto, path)
1183 except FileAccessError as e:
1184 raise error.WireprotoCommandError(e.msg, e.args)
1176 if haveparents:
1177 checkpaths = ctx.files()
1178 else:
1179 checkpaths = md.keys()
1185 1180
1186 for rev in store:
1187 linkrev = store.linkrev(rev)
1188
1189 if linkrev in outgoingclrevs:
1190 fnodes[path].setdefault(store.node(rev), clnode(linkrev))
1181 for path in checkpaths:
1182 fnode = md[path]
1191 1183
1192 # If ancestors aren't known, we walk the manifests and send all
1193 # encountered file revisions.
1194 else:
1195 for node in outgoing:
1196 mctx = repo[node].manifestctx()
1184 if path in fnodes and fnode in fnodes[path]:
1185 continue
1197 1186
1198 for path, fnode in mctx.read().items():
1199 if filematcher(path):
1200 fnodes[path].setdefault(fnode, node)
1187 if not filematcher(path):
1188 continue
1189
1190 fnodes[path].setdefault(fnode, node)
1201 1191
1202 1192 yield {
1203 1193 b'totalpaths': len(fnodes),
1204 1194 b'totalitems': sum(len(v) for v in fnodes.values())
1205 1195 }
1206 1196
1207 1197 for path, filenodes in sorted(fnodes.items()):
1208 1198 try:
1209 1199 store = getfilestore(repo, proto, path)
1210 1200 except FileAccessError as e:
1211 1201 raise error.WireprotoCommandError(e.msg, e.args)
1212 1202
1213 1203 yield {
1214 1204 b'path': path,
1215 1205 b'totalitems': len(filenodes),
1216 1206 }
1217 1207
1218 1208 revisions = store.emitrevisions(filenodes.keys(),
1219 1209 revisiondata=b'revision' in fields,
1220 1210 assumehaveparentrevisions=haveparents)
1221 1211
1222 1212 for o in emitfilerevisions(repo, path, revisions, filenodes, fields):
1223 1213 yield o
1224 1214
1225 1215 @wireprotocommand(
1226 1216 'heads',
1227 1217 args={
1228 1218 'publiconly': {
1229 1219 'type': 'bool',
1230 1220 'default': lambda: False,
1231 1221 'example': False,
1232 1222 },
1233 1223 },
1234 1224 permission='pull')
1235 1225 def headsv2(repo, proto, publiconly):
1236 1226 if publiconly:
1237 1227 repo = repo.filtered('immutable')
1238 1228
1239 1229 yield repo.heads()
1240 1230
1241 1231 @wireprotocommand(
1242 1232 'known',
1243 1233 args={
1244 1234 'nodes': {
1245 1235 'type': 'list',
1246 1236 'default': list,
1247 1237 'example': [b'deadbeef'],
1248 1238 },
1249 1239 },
1250 1240 permission='pull')
1251 1241 def knownv2(repo, proto, nodes):
1252 1242 result = b''.join(b'1' if n else b'0' for n in repo.known(nodes))
1253 1243 yield result
1254 1244
1255 1245 @wireprotocommand(
1256 1246 'listkeys',
1257 1247 args={
1258 1248 'namespace': {
1259 1249 'type': 'bytes',
1260 1250 'example': b'ns',
1261 1251 },
1262 1252 },
1263 1253 permission='pull')
1264 1254 def listkeysv2(repo, proto, namespace):
1265 1255 keys = repo.listkeys(encoding.tolocal(namespace))
1266 1256 keys = {encoding.fromlocal(k): encoding.fromlocal(v)
1267 1257 for k, v in keys.iteritems()}
1268 1258
1269 1259 yield keys
1270 1260
1271 1261 @wireprotocommand(
1272 1262 'lookup',
1273 1263 args={
1274 1264 'key': {
1275 1265 'type': 'bytes',
1276 1266 'example': b'foo',
1277 1267 },
1278 1268 },
1279 1269 permission='pull')
1280 1270 def lookupv2(repo, proto, key):
1281 1271 key = encoding.tolocal(key)
1282 1272
1283 1273 # TODO handle exception.
1284 1274 node = repo.lookup(key)
1285 1275
1286 1276 yield node
1287 1277
1288 1278 def manifestdatacapabilities(repo, proto):
1289 1279 batchsize = repo.ui.configint(
1290 1280 b'experimental', b'server.manifestdata.recommended-batch-size')
1291 1281
1292 1282 return {
1293 1283 b'recommendedbatchsize': batchsize,
1294 1284 }
1295 1285
1296 1286 @wireprotocommand(
1297 1287 'manifestdata',
1298 1288 args={
1299 1289 'nodes': {
1300 1290 'type': 'list',
1301 1291 'example': [b'0123456...'],
1302 1292 },
1303 1293 'haveparents': {
1304 1294 'type': 'bool',
1305 1295 'default': lambda: False,
1306 1296 'example': True,
1307 1297 },
1308 1298 'fields': {
1309 1299 'type': 'set',
1310 1300 'default': set,
1311 1301 'example': {b'parents', b'revision'},
1312 1302 'validvalues': {b'parents', b'revision'},
1313 1303 },
1314 1304 'tree': {
1315 1305 'type': 'bytes',
1316 1306 'example': b'',
1317 1307 },
1318 1308 },
1319 1309 permission='pull',
1320 1310 cachekeyfn=makecommandcachekeyfn('manifestdata', 1, allargs=True),
1321 1311 extracapabilitiesfn=manifestdatacapabilities)
1322 1312 def manifestdata(repo, proto, haveparents, nodes, fields, tree):
1323 1313 store = repo.manifestlog.getstorage(tree)
1324 1314
1325 1315 # Validate the node is known and abort on unknown revisions.
1326 1316 for node in nodes:
1327 1317 try:
1328 1318 store.rev(node)
1329 1319 except error.LookupError:
1330 1320 raise error.WireprotoCommandError(
1331 1321 'unknown node: %s', (node,))
1332 1322
1333 1323 revisions = store.emitrevisions(nodes,
1334 1324 revisiondata=b'revision' in fields,
1335 1325 assumehaveparentrevisions=haveparents)
1336 1326
1337 1327 yield {
1338 1328 b'totalitems': len(nodes),
1339 1329 }
1340 1330
1341 1331 for revision in revisions:
1342 1332 d = {
1343 1333 b'node': revision.node,
1344 1334 }
1345 1335
1346 1336 if b'parents' in fields:
1347 1337 d[b'parents'] = [revision.p1node, revision.p2node]
1348 1338
1349 1339 followingmeta = []
1350 1340 followingdata = []
1351 1341
1352 1342 if b'revision' in fields:
1353 1343 if revision.revision is not None:
1354 1344 followingmeta.append((b'revision', len(revision.revision)))
1355 1345 followingdata.append(revision.revision)
1356 1346 else:
1357 1347 d[b'deltabasenode'] = revision.basenode
1358 1348 followingmeta.append((b'delta', len(revision.delta)))
1359 1349 followingdata.append(revision.delta)
1360 1350
1361 1351 if followingmeta:
1362 1352 d[b'fieldsfollowing'] = followingmeta
1363 1353
1364 1354 yield d
1365 1355
1366 1356 for extra in followingdata:
1367 1357 yield extra
1368 1358
1369 1359 @wireprotocommand(
1370 1360 'pushkey',
1371 1361 args={
1372 1362 'namespace': {
1373 1363 'type': 'bytes',
1374 1364 'example': b'ns',
1375 1365 },
1376 1366 'key': {
1377 1367 'type': 'bytes',
1378 1368 'example': b'key',
1379 1369 },
1380 1370 'old': {
1381 1371 'type': 'bytes',
1382 1372 'example': b'old',
1383 1373 },
1384 1374 'new': {
1385 1375 'type': 'bytes',
1386 1376 'example': 'new',
1387 1377 },
1388 1378 },
1389 1379 permission='push')
1390 1380 def pushkeyv2(repo, proto, namespace, key, old, new):
1391 1381 # TODO handle ui output redirection
1392 1382 yield repo.pushkey(encoding.tolocal(namespace),
1393 1383 encoding.tolocal(key),
1394 1384 encoding.tolocal(old),
1395 1385 encoding.tolocal(new))
1396 1386
1397 1387
1398 1388 @wireprotocommand(
1399 1389 'rawstorefiledata',
1400 1390 args={
1401 1391 'files': {
1402 1392 'type': 'list',
1403 1393 'example': [b'changelog', b'manifestlog'],
1404 1394 },
1405 1395 'pathfilter': {
1406 1396 'type': 'list',
1407 1397 'default': lambda: None,
1408 1398 'example': {b'include': [b'path:tests']},
1409 1399 },
1410 1400 },
1411 1401 permission='pull')
1412 1402 def rawstorefiledata(repo, proto, files, pathfilter):
1413 1403 if not streamclone.allowservergeneration(repo):
1414 1404 raise error.WireprotoCommandError(b'stream clone is disabled')
1415 1405
1416 1406 # TODO support dynamically advertising what store files "sets" are
1417 1407 # available. For now, we support changelog, manifestlog, and files.
1418 1408 files = set(files)
1419 1409 allowedfiles = {b'changelog', b'manifestlog'}
1420 1410
1421 1411 unsupported = files - allowedfiles
1422 1412 if unsupported:
1423 1413 raise error.WireprotoCommandError(b'unknown file type: %s',
1424 1414 (b', '.join(sorted(unsupported)),))
1425 1415
1426 1416 with repo.lock():
1427 1417 topfiles = list(repo.store.topfiles())
1428 1418
1429 1419 sendfiles = []
1430 1420 totalsize = 0
1431 1421
1432 1422 # TODO this is a bunch of storage layer interface abstractions because
1433 1423 # it assumes revlogs.
1434 1424 for name, encodedname, size in topfiles:
1435 1425 if b'changelog' in files and name.startswith(b'00changelog'):
1436 1426 pass
1437 1427 elif b'manifestlog' in files and name.startswith(b'00manifest'):
1438 1428 pass
1439 1429 else:
1440 1430 continue
1441 1431
1442 1432 sendfiles.append((b'store', name, size))
1443 1433 totalsize += size
1444 1434
1445 1435 yield {
1446 1436 b'filecount': len(sendfiles),
1447 1437 b'totalsize': totalsize,
1448 1438 }
1449 1439
1450 1440 for location, name, size in sendfiles:
1451 1441 yield {
1452 1442 b'location': location,
1453 1443 b'path': name,
1454 1444 b'size': size,
1455 1445 }
1456 1446
1457 1447 # We have to use a closure for this to ensure the context manager is
1458 1448 # closed only after sending the final chunk.
1459 1449 def getfiledata():
1460 1450 with repo.svfs(name, 'rb', auditpath=False) as fh:
1461 1451 for chunk in util.filechunkiter(fh, limit=size):
1462 1452 yield chunk
1463 1453
1464 1454 yield wireprototypes.indefinitebytestringresponse(
1465 1455 getfiledata())
@@ -1,1292 +1,1298 b''
1 1 $ . $TESTDIR/wireprotohelpers.sh
2 2
3 3 $ hg init server
4 4 $ enablehttpv2 server
5 5 $ cd server
6 6 $ cat > a << EOF
7 7 > a0
8 8 > 00000000000000000000000000000000000000
9 9 > 11111111111111111111111111111111111111
10 10 > EOF
11 11 $ cat > b << EOF
12 12 > b0
13 13 > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
14 14 > bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
15 15 > EOF
16 16 $ mkdir -p dir0/child0 dir0/child1 dir1
17 17 $ echo c0 > dir0/c
18 18 $ echo d0 > dir0/d
19 19 $ echo e0 > dir0/child0/e
20 20 $ echo f0 > dir0/child1/f
21 21 $ hg -q commit -A -m 'commit 0'
22 22
23 23 $ echo a1 >> a
24 24 $ echo d1 > dir0/d
25 25 $ echo g0 > g
26 26 $ echo h0 > h
27 27 $ hg -q commit -A -m 'commit 1'
28 28 $ echo f1 > dir0/child1/f
29 29 $ echo i0 > dir0/i
30 30 $ hg -q commit -A -m 'commit 2'
31 31
32 32 $ hg -q up -r 0
33 33 $ echo a2 >> a
34 34 $ hg commit -m 'commit 3'
35 35 created new head
36 36
37 37 Create multiple heads introducing the same file nodefile node
38 38
39 39 $ hg -q up -r 0
40 40 $ echo foo > dupe-file
41 41 $ hg commit -Am 'dupe 1'
42 42 adding dupe-file
43 43 created new head
44 44 $ hg -q up -r 0
45 45 $ echo foo > dupe-file
46 46 $ hg commit -Am 'dupe 2'
47 47 adding dupe-file
48 48 created new head
49 49
50 50 $ hg log -G -T '{rev}:{node} {desc}\n'
51 51 @ 5:47fc30580911232cb264675b402819deddf6c6f0 dupe 2
52 52 |
53 53 | o 4:b16cce2967c1749ef4f4e3086a806cfbad8a3af7 dupe 1
54 54 |/
55 55 | o 3:476fbf122cd82f6726f0191ff146f67140946abc commit 3
56 56 |/
57 57 | o 2:b91c03cbba3519ab149b6cd0a0afbdb5cf1b5c8a commit 2
58 58 | |
59 59 | o 1:5b0b1a23577e205ea240e39c9704e28d7697cbd8 commit 1
60 60 |/
61 61 o 0:6e875ff18c227659ad6143bb3580c65700734884 commit 0
62 62
63 63
64 64 $ hg serve -p $HGPORT -d --pid-file hg.pid -E error.log
65 65 $ cat hg.pid > $DAEMON_PIDS
66 66
67 67 Missing arguments is an error
68 68
69 69 $ sendhttpv2peer << EOF
70 70 > command filesdata
71 71 > EOF
72 72 creating http peer for wire protocol version 2
73 73 sending filesdata command
74 74 abort: missing required arguments: revisions!
75 75 [255]
76 76
77 77 Bad pattern to pathfilter is rejected
78 78
79 79 $ sendhttpv2peer << EOF
80 80 > command filesdata
81 81 > revisions eval:[{
82 82 > b'type': b'changesetexplicit',
83 83 > b'nodes': [
84 84 > b'\x5b\x0b\x1a\x23\x57\x7e\x20\x5e\xa2\x40\xe3\x9c\x97\x04\xe2\x8d\x76\x97\xcb\xd8',
85 85 > ]}]
86 86 > pathfilter eval:{b'include': [b'bad:foo']}
87 87 > EOF
88 88 creating http peer for wire protocol version 2
89 89 sending filesdata command
90 90 abort: include pattern must begin with `path:` or `rootfilesin:`; got bad:foo!
91 91 [255]
92 92
93 93 $ sendhttpv2peer << EOF
94 94 > command filesdata
95 95 > revisions eval:[{
96 96 > b'type': b'changesetexplicit',
97 97 > b'nodes': [
98 98 > b'\x5b\x0b\x1a\x23\x57\x7e\x20\x5e\xa2\x40\xe3\x9c\x97\x04\xe2\x8d\x76\x97\xcb\xd8',
99 99 > ]}]
100 100 > pathfilter eval:{b'exclude': [b'glob:foo']}
101 101 > EOF
102 102 creating http peer for wire protocol version 2
103 103 sending filesdata command
104 104 abort: exclude pattern must begin with `path:` or `rootfilesin:`; got glob:foo!
105 105 [255]
106 106
107 107 Fetching a single changeset without parents fetches all files
108 108
109 109 $ sendhttpv2peer << EOF
110 110 > command filesdata
111 111 > revisions eval:[{
112 112 > b'type': b'changesetexplicit',
113 113 > b'nodes': [
114 114 > b'\x5b\x0b\x1a\x23\x57\x7e\x20\x5e\xa2\x40\xe3\x9c\x97\x04\xe2\x8d\x76\x97\xcb\xd8',
115 115 > ]}]
116 116 > EOF
117 117 creating http peer for wire protocol version 2
118 118 sending filesdata command
119 119 response: gen[
120 120 {
121 121 b'totalitems': 8,
122 122 b'totalpaths': 8
123 123 },
124 124 {
125 125 b'path': b'a',
126 126 b'totalitems': 1
127 127 },
128 128 {
129 129 b'node': b'\n\x862\x1f\x13y\xd1\xa9\xec\xd0W\x9a"\x97z\xf7\xa5\xac\xaf\x11'
130 130 },
131 131 {
132 132 b'path': b'b',
133 133 b'totalitems': 1
134 134 },
135 135 {
136 136 b'node': b'\x88\xbac\xb8\xd8\xc6 :\xc6z\xc9\x98\xac\xd9\x17K\xf7\x05!\xb2'
137 137 },
138 138 {
139 139 b'path': b'dir0/c',
140 140 b'totalitems': 1
141 141 },
142 142 {
143 143 b'node': b'\x91DE4j\x0c\xa0b\x9b\xd4|\xeb]\xfe\x07\xe4\xd4\xcf%\x01'
144 144 },
145 145 {
146 146 b'path': b'dir0/child0/e',
147 147 b'totalitems': 1
148 148 },
149 149 {
150 150 b'node': b'\xbb\xbal\x06\xb3\x0fD=4\xff\x84\x1b\xc9\x85\xc4\xd0\x82|k\xe4'
151 151 },
152 152 {
153 153 b'path': b'dir0/child1/f',
154 154 b'totalitems': 1
155 155 },
156 156 {
157 157 b'node': b'\x12\xfc}\xcdw;Z\n\x92\x9c\xe1\x95"\x80\x83\xc6\xdd\xc9\xce\xc4'
158 158 },
159 159 {
160 160 b'path': b'dir0/d',
161 161 b'totalitems': 1
162 162 },
163 163 {
164 164 b'node': b'\x93\x88)\xad\x01R}2\xba\x06_\x81#6\xfe\xc7\x9d\xdd9G'
165 165 },
166 166 {
167 167 b'path': b'g',
168 168 b'totalitems': 1
169 169 },
170 170 {
171 171 b'node': b'\xde\xca\xba5DFjI\x95r\xe9\x0f\xac\xe6\xfa\x0c!k\xba\x8c'
172 172 },
173 173 {
174 174 b'path': b'h',
175 175 b'totalitems': 1
176 176 },
177 177 {
178 178 b'node': b'\x03A\xfc\x84\x1b\xb5\xb4\xba\x93\xb2mM\xdaa\xf7y6]\xb3K'
179 179 }
180 180 ]
181 181
182 182 Fetching a single changeset saying parents data is available fetches just new files
183 183
184 184 $ sendhttpv2peer << EOF
185 185 > command filesdata
186 186 > revisions eval:[{
187 187 > b'type': b'changesetexplicit',
188 188 > b'nodes': [
189 189 > b'\x5b\x0b\x1a\x23\x57\x7e\x20\x5e\xa2\x40\xe3\x9c\x97\x04\xe2\x8d\x76\x97\xcb\xd8',
190 190 > ]}]
191 191 > haveparents eval:True
192 192 > EOF
193 193 creating http peer for wire protocol version 2
194 194 sending filesdata command
195 195 response: gen[
196 196 {
197 197 b'totalitems': 4,
198 198 b'totalpaths': 4
199 199 },
200 200 {
201 201 b'path': b'a',
202 202 b'totalitems': 1
203 203 },
204 204 {
205 205 b'node': b'\n\x862\x1f\x13y\xd1\xa9\xec\xd0W\x9a"\x97z\xf7\xa5\xac\xaf\x11'
206 206 },
207 207 {
208 208 b'path': b'dir0/d',
209 209 b'totalitems': 1
210 210 },
211 211 {
212 212 b'node': b'\x93\x88)\xad\x01R}2\xba\x06_\x81#6\xfe\xc7\x9d\xdd9G'
213 213 },
214 214 {
215 215 b'path': b'g',
216 216 b'totalitems': 1
217 217 },
218 218 {
219 219 b'node': b'\xde\xca\xba5DFjI\x95r\xe9\x0f\xac\xe6\xfa\x0c!k\xba\x8c'
220 220 },
221 221 {
222 222 b'path': b'h',
223 223 b'totalitems': 1
224 224 },
225 225 {
226 226 b'node': b'\x03A\xfc\x84\x1b\xb5\xb4\xba\x93\xb2mM\xdaa\xf7y6]\xb3K'
227 227 }
228 228 ]
229 229
230 230 A path filter for a sub-directory is honored
231 231
232 232 $ sendhttpv2peer << EOF
233 233 > command filesdata
234 234 > revisions eval:[{
235 235 > b'type': b'changesetexplicit',
236 236 > b'nodes': [
237 237 > b'\x5b\x0b\x1a\x23\x57\x7e\x20\x5e\xa2\x40\xe3\x9c\x97\x04\xe2\x8d\x76\x97\xcb\xd8',
238 238 > ]}]
239 239 > haveparents eval:True
240 240 > pathfilter eval:{b'include': [b'path:dir0']}
241 241 > EOF
242 242 creating http peer for wire protocol version 2
243 243 sending filesdata command
244 244 response: gen[
245 245 {
246 246 b'totalitems': 1,
247 247 b'totalpaths': 1
248 248 },
249 249 {
250 250 b'path': b'dir0/d',
251 251 b'totalitems': 1
252 252 },
253 253 {
254 254 b'node': b'\x93\x88)\xad\x01R}2\xba\x06_\x81#6\xfe\xc7\x9d\xdd9G'
255 255 }
256 256 ]
257 257
258 258 $ sendhttpv2peer << EOF
259 259 > command filesdata
260 260 > revisions eval:[{
261 261 > b'type': b'changesetexplicit',
262 262 > b'nodes': [
263 263 > b'\x5b\x0b\x1a\x23\x57\x7e\x20\x5e\xa2\x40\xe3\x9c\x97\x04\xe2\x8d\x76\x97\xcb\xd8',
264 264 > ]}]
265 265 > haveparents eval:True
266 266 > pathfilter eval:{b'exclude': [b'path:a', b'path:g']}
267 267 > EOF
268 268 creating http peer for wire protocol version 2
269 269 sending filesdata command
270 270 response: gen[
271 271 {
272 272 b'totalitems': 2,
273 273 b'totalpaths': 2
274 274 },
275 275 {
276 276 b'path': b'dir0/d',
277 277 b'totalitems': 1
278 278 },
279 279 {
280 280 b'node': b'\x93\x88)\xad\x01R}2\xba\x06_\x81#6\xfe\xc7\x9d\xdd9G'
281 281 },
282 282 {
283 283 b'path': b'h',
284 284 b'totalitems': 1
285 285 },
286 286 {
287 287 b'node': b'\x03A\xfc\x84\x1b\xb5\xb4\xba\x93\xb2mM\xdaa\xf7y6]\xb3K'
288 288 }
289 289 ]
290 290
291 291 Requesting multiple changeset nodes without haveparents sends all data for both
292 292
293 293 $ sendhttpv2peer << EOF
294 294 > command filesdata
295 295 > revisions eval:[{
296 296 > b'type': b'changesetexplicit',
297 297 > b'nodes': [
298 298 > b'\x5b\x0b\x1a\x23\x57\x7e\x20\x5e\xa2\x40\xe3\x9c\x97\x04\xe2\x8d\x76\x97\xcb\xd8',
299 299 > b'\xb9\x1c\x03\xcb\xba\x35\x19\xab\x14\x9b\x6c\xd0\xa0\xaf\xbd\xb5\xcf\x1b\x5c\x8a',
300 300 > ]}]
301 301 > EOF
302 302 creating http peer for wire protocol version 2
303 303 sending filesdata command
304 304 response: gen[
305 305 {
306 306 b'totalitems': 10,
307 307 b'totalpaths': 9
308 308 },
309 309 {
310 310 b'path': b'a',
311 311 b'totalitems': 1
312 312 },
313 313 {
314 314 b'node': b'\n\x862\x1f\x13y\xd1\xa9\xec\xd0W\x9a"\x97z\xf7\xa5\xac\xaf\x11'
315 315 },
316 316 {
317 317 b'path': b'b',
318 318 b'totalitems': 1
319 319 },
320 320 {
321 321 b'node': b'\x88\xbac\xb8\xd8\xc6 :\xc6z\xc9\x98\xac\xd9\x17K\xf7\x05!\xb2'
322 322 },
323 323 {
324 324 b'path': b'dir0/c',
325 325 b'totalitems': 1
326 326 },
327 327 {
328 328 b'node': b'\x91DE4j\x0c\xa0b\x9b\xd4|\xeb]\xfe\x07\xe4\xd4\xcf%\x01'
329 329 },
330 330 {
331 331 b'path': b'dir0/child0/e',
332 332 b'totalitems': 1
333 333 },
334 334 {
335 335 b'node': b'\xbb\xbal\x06\xb3\x0fD=4\xff\x84\x1b\xc9\x85\xc4\xd0\x82|k\xe4'
336 336 },
337 337 {
338 338 b'path': b'dir0/child1/f',
339 339 b'totalitems': 2
340 340 },
341 341 {
342 342 b'node': b'\x12\xfc}\xcdw;Z\n\x92\x9c\xe1\x95"\x80\x83\xc6\xdd\xc9\xce\xc4'
343 343 },
344 344 {
345 345 b'node': b'(\xc7v\xae\x08\xd0\xd5^\xb4\x06H\xb4\x01\xb9\x0f\xf5DH4\x8e'
346 346 },
347 347 {
348 348 b'path': b'dir0/d',
349 349 b'totalitems': 1
350 350 },
351 351 {
352 352 b'node': b'\x93\x88)\xad\x01R}2\xba\x06_\x81#6\xfe\xc7\x9d\xdd9G'
353 353 },
354 354 {
355 355 b'path': b'dir0/i',
356 356 b'totalitems': 1
357 357 },
358 358 {
359 359 b'node': b'\xd7t\xb5\x80Jq\xfd1\xe1\xae\x05\xea\x8e2\xdd\x9b\xa3\xd8S\xd7'
360 360 },
361 361 {
362 362 b'path': b'g',
363 363 b'totalitems': 1
364 364 },
365 365 {
366 366 b'node': b'\xde\xca\xba5DFjI\x95r\xe9\x0f\xac\xe6\xfa\x0c!k\xba\x8c'
367 367 },
368 368 {
369 369 b'path': b'h',
370 370 b'totalitems': 1
371 371 },
372 372 {
373 373 b'node': b'\x03A\xfc\x84\x1b\xb5\xb4\xba\x93\xb2mM\xdaa\xf7y6]\xb3K'
374 374 }
375 375 ]
376 376
377 377 Requesting multiple changeset nodes with haveparents sends incremental data for both
378 378
379 379 $ sendhttpv2peer << EOF
380 380 > command filesdata
381 381 > revisions eval:[{
382 382 > b'type': b'changesetexplicit',
383 383 > b'nodes': [
384 384 > b'\x5b\x0b\x1a\x23\x57\x7e\x20\x5e\xa2\x40\xe3\x9c\x97\x04\xe2\x8d\x76\x97\xcb\xd8',
385 385 > b'\xb9\x1c\x03\xcb\xba\x35\x19\xab\x14\x9b\x6c\xd0\xa0\xaf\xbd\xb5\xcf\x1b\x5c\x8a',
386 386 > ]}]
387 387 > haveparents eval:True
388 388 > EOF
389 389 creating http peer for wire protocol version 2
390 390 sending filesdata command
391 391 response: gen[
392 392 {
393 393 b'totalitems': 6,
394 394 b'totalpaths': 6
395 395 },
396 396 {
397 397 b'path': b'a',
398 398 b'totalitems': 1
399 399 },
400 400 {
401 401 b'node': b'\n\x862\x1f\x13y\xd1\xa9\xec\xd0W\x9a"\x97z\xf7\xa5\xac\xaf\x11'
402 402 },
403 403 {
404 404 b'path': b'dir0/child1/f',
405 405 b'totalitems': 1
406 406 },
407 407 {
408 408 b'node': b'(\xc7v\xae\x08\xd0\xd5^\xb4\x06H\xb4\x01\xb9\x0f\xf5DH4\x8e'
409 409 },
410 410 {
411 411 b'path': b'dir0/d',
412 412 b'totalitems': 1
413 413 },
414 414 {
415 415 b'node': b'\x93\x88)\xad\x01R}2\xba\x06_\x81#6\xfe\xc7\x9d\xdd9G'
416 416 },
417 417 {
418 418 b'path': b'dir0/i',
419 419 b'totalitems': 1
420 420 },
421 421 {
422 422 b'node': b'\xd7t\xb5\x80Jq\xfd1\xe1\xae\x05\xea\x8e2\xdd\x9b\xa3\xd8S\xd7'
423 423 },
424 424 {
425 425 b'path': b'g',
426 426 b'totalitems': 1
427 427 },
428 428 {
429 429 b'node': b'\xde\xca\xba5DFjI\x95r\xe9\x0f\xac\xe6\xfa\x0c!k\xba\x8c'
430 430 },
431 431 {
432 432 b'path': b'h',
433 433 b'totalitems': 1
434 434 },
435 435 {
436 436 b'node': b'\x03A\xfc\x84\x1b\xb5\xb4\xba\x93\xb2mM\xdaa\xf7y6]\xb3K'
437 437 }
438 438 ]
439 439
440 440 Requesting parents works
441 441
442 442 $ sendhttpv2peer << EOF
443 443 > command filesdata
444 444 > revisions eval:[{
445 445 > b'type': b'changesetexplicit',
446 446 > b'nodes': [
447 447 > b'\x5b\x0b\x1a\x23\x57\x7e\x20\x5e\xa2\x40\xe3\x9c\x97\x04\xe2\x8d\x76\x97\xcb\xd8',
448 448 > ]}]
449 449 > fields eval:[b'parents']
450 450 > EOF
451 451 creating http peer for wire protocol version 2
452 452 sending filesdata command
453 453 response: gen[
454 454 {
455 455 b'totalitems': 8,
456 456 b'totalpaths': 8
457 457 },
458 458 {
459 459 b'path': b'a',
460 460 b'totalitems': 1
461 461 },
462 462 {
463 463 b'node': b'\n\x862\x1f\x13y\xd1\xa9\xec\xd0W\x9a"\x97z\xf7\xa5\xac\xaf\x11',
464 464 b'parents': [
465 465 b'd\x9d\x14\x9d\xf4=\x83\x88%#\xb7\xfb\x1ej:\xf6\xf1\x90{9',
466 466 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
467 467 ]
468 468 },
469 469 {
470 470 b'path': b'b',
471 471 b'totalitems': 1
472 472 },
473 473 {
474 474 b'node': b'\x88\xbac\xb8\xd8\xc6 :\xc6z\xc9\x98\xac\xd9\x17K\xf7\x05!\xb2',
475 475 b'parents': [
476 476 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
477 477 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
478 478 ]
479 479 },
480 480 {
481 481 b'path': b'dir0/c',
482 482 b'totalitems': 1
483 483 },
484 484 {
485 485 b'node': b'\x91DE4j\x0c\xa0b\x9b\xd4|\xeb]\xfe\x07\xe4\xd4\xcf%\x01',
486 486 b'parents': [
487 487 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
488 488 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
489 489 ]
490 490 },
491 491 {
492 492 b'path': b'dir0/child0/e',
493 493 b'totalitems': 1
494 494 },
495 495 {
496 496 b'node': b'\xbb\xbal\x06\xb3\x0fD=4\xff\x84\x1b\xc9\x85\xc4\xd0\x82|k\xe4',
497 497 b'parents': [
498 498 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
499 499 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
500 500 ]
501 501 },
502 502 {
503 503 b'path': b'dir0/child1/f',
504 504 b'totalitems': 1
505 505 },
506 506 {
507 507 b'node': b'\x12\xfc}\xcdw;Z\n\x92\x9c\xe1\x95"\x80\x83\xc6\xdd\xc9\xce\xc4',
508 508 b'parents': [
509 509 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
510 510 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
511 511 ]
512 512 },
513 513 {
514 514 b'path': b'dir0/d',
515 515 b'totalitems': 1
516 516 },
517 517 {
518 518 b'node': b'\x93\x88)\xad\x01R}2\xba\x06_\x81#6\xfe\xc7\x9d\xdd9G',
519 519 b'parents': [
520 520 b'S\x82\x06\xdc\x97\x1eR\x15@\xd6\x84:\xbf\xe6\xd1`2\xf6\xd4&',
521 521 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
522 522 ]
523 523 },
524 524 {
525 525 b'path': b'g',
526 526 b'totalitems': 1
527 527 },
528 528 {
529 529 b'node': b'\xde\xca\xba5DFjI\x95r\xe9\x0f\xac\xe6\xfa\x0c!k\xba\x8c',
530 530 b'parents': [
531 531 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
532 532 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
533 533 ]
534 534 },
535 535 {
536 536 b'path': b'h',
537 537 b'totalitems': 1
538 538 },
539 539 {
540 540 b'node': b'\x03A\xfc\x84\x1b\xb5\xb4\xba\x93\xb2mM\xdaa\xf7y6]\xb3K',
541 541 b'parents': [
542 542 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
543 543 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
544 544 ]
545 545 }
546 546 ]
547 547
548 548 Requesting revision data works
549 549 (haveparents defaults to False, so fulltext is emitted)
550 550
551 551 $ sendhttpv2peer << EOF
552 552 > command filesdata
553 553 > revisions eval:[{
554 554 > b'type': b'changesetexplicit',
555 555 > b'nodes': [
556 556 > b'\x5b\x0b\x1a\x23\x57\x7e\x20\x5e\xa2\x40\xe3\x9c\x97\x04\xe2\x8d\x76\x97\xcb\xd8',
557 557 > ]}]
558 558 > fields eval:[b'revision']
559 559 > EOF
560 560 creating http peer for wire protocol version 2
561 561 sending filesdata command
562 562 response: gen[
563 563 {
564 564 b'totalitems': 8,
565 565 b'totalpaths': 8
566 566 },
567 567 {
568 568 b'path': b'a',
569 569 b'totalitems': 1
570 570 },
571 571 {
572 572 b'fieldsfollowing': [
573 573 [
574 574 b'revision',
575 575 84
576 576 ]
577 577 ],
578 578 b'node': b'\n\x862\x1f\x13y\xd1\xa9\xec\xd0W\x9a"\x97z\xf7\xa5\xac\xaf\x11'
579 579 },
580 580 b'a0\n00000000000000000000000000000000000000\n11111111111111111111111111111111111111\na1\n',
581 581 {
582 582 b'path': b'b',
583 583 b'totalitems': 1
584 584 },
585 585 {
586 586 b'fieldsfollowing': [
587 587 [
588 588 b'revision',
589 589 81
590 590 ]
591 591 ],
592 592 b'node': b'\x88\xbac\xb8\xd8\xc6 :\xc6z\xc9\x98\xac\xd9\x17K\xf7\x05!\xb2'
593 593 },
594 594 b'b0\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n',
595 595 {
596 596 b'path': b'dir0/c',
597 597 b'totalitems': 1
598 598 },
599 599 {
600 600 b'fieldsfollowing': [
601 601 [
602 602 b'revision',
603 603 3
604 604 ]
605 605 ],
606 606 b'node': b'\x91DE4j\x0c\xa0b\x9b\xd4|\xeb]\xfe\x07\xe4\xd4\xcf%\x01'
607 607 },
608 608 b'c0\n',
609 609 {
610 610 b'path': b'dir0/child0/e',
611 611 b'totalitems': 1
612 612 },
613 613 {
614 614 b'fieldsfollowing': [
615 615 [
616 616 b'revision',
617 617 3
618 618 ]
619 619 ],
620 620 b'node': b'\xbb\xbal\x06\xb3\x0fD=4\xff\x84\x1b\xc9\x85\xc4\xd0\x82|k\xe4'
621 621 },
622 622 b'e0\n',
623 623 {
624 624 b'path': b'dir0/child1/f',
625 625 b'totalitems': 1
626 626 },
627 627 {
628 628 b'fieldsfollowing': [
629 629 [
630 630 b'revision',
631 631 3
632 632 ]
633 633 ],
634 634 b'node': b'\x12\xfc}\xcdw;Z\n\x92\x9c\xe1\x95"\x80\x83\xc6\xdd\xc9\xce\xc4'
635 635 },
636 636 b'f0\n',
637 637 {
638 638 b'path': b'dir0/d',
639 639 b'totalitems': 1
640 640 },
641 641 {
642 642 b'fieldsfollowing': [
643 643 [
644 644 b'revision',
645 645 3
646 646 ]
647 647 ],
648 648 b'node': b'\x93\x88)\xad\x01R}2\xba\x06_\x81#6\xfe\xc7\x9d\xdd9G'
649 649 },
650 650 b'd1\n',
651 651 {
652 652 b'path': b'g',
653 653 b'totalitems': 1
654 654 },
655 655 {
656 656 b'fieldsfollowing': [
657 657 [
658 658 b'revision',
659 659 3
660 660 ]
661 661 ],
662 662 b'node': b'\xde\xca\xba5DFjI\x95r\xe9\x0f\xac\xe6\xfa\x0c!k\xba\x8c'
663 663 },
664 664 b'g0\n',
665 665 {
666 666 b'path': b'h',
667 667 b'totalitems': 1
668 668 },
669 669 {
670 670 b'fieldsfollowing': [
671 671 [
672 672 b'revision',
673 673 3
674 674 ]
675 675 ],
676 676 b'node': b'\x03A\xfc\x84\x1b\xb5\xb4\xba\x93\xb2mM\xdaa\xf7y6]\xb3K'
677 677 },
678 678 b'h0\n'
679 679 ]
680 680
681 681 haveparents=False should be same as above
682 682
683 683 $ sendhttpv2peer << EOF
684 684 > command filesdata
685 685 > revisions eval:[{
686 686 > b'type': b'changesetexplicit',
687 687 > b'nodes': [
688 688 > b'\x5b\x0b\x1a\x23\x57\x7e\x20\x5e\xa2\x40\xe3\x9c\x97\x04\xe2\x8d\x76\x97\xcb\xd8',
689 689 > ]}]
690 690 > fields eval:[b'revision']
691 691 > haveparents eval:False
692 692 > EOF
693 693 creating http peer for wire protocol version 2
694 694 sending filesdata command
695 695 response: gen[
696 696 {
697 697 b'totalitems': 8,
698 698 b'totalpaths': 8
699 699 },
700 700 {
701 701 b'path': b'a',
702 702 b'totalitems': 1
703 703 },
704 704 {
705 705 b'fieldsfollowing': [
706 706 [
707 707 b'revision',
708 708 84
709 709 ]
710 710 ],
711 711 b'node': b'\n\x862\x1f\x13y\xd1\xa9\xec\xd0W\x9a"\x97z\xf7\xa5\xac\xaf\x11'
712 712 },
713 713 b'a0\n00000000000000000000000000000000000000\n11111111111111111111111111111111111111\na1\n',
714 714 {
715 715 b'path': b'b',
716 716 b'totalitems': 1
717 717 },
718 718 {
719 719 b'fieldsfollowing': [
720 720 [
721 721 b'revision',
722 722 81
723 723 ]
724 724 ],
725 725 b'node': b'\x88\xbac\xb8\xd8\xc6 :\xc6z\xc9\x98\xac\xd9\x17K\xf7\x05!\xb2'
726 726 },
727 727 b'b0\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n',
728 728 {
729 729 b'path': b'dir0/c',
730 730 b'totalitems': 1
731 731 },
732 732 {
733 733 b'fieldsfollowing': [
734 734 [
735 735 b'revision',
736 736 3
737 737 ]
738 738 ],
739 739 b'node': b'\x91DE4j\x0c\xa0b\x9b\xd4|\xeb]\xfe\x07\xe4\xd4\xcf%\x01'
740 740 },
741 741 b'c0\n',
742 742 {
743 743 b'path': b'dir0/child0/e',
744 744 b'totalitems': 1
745 745 },
746 746 {
747 747 b'fieldsfollowing': [
748 748 [
749 749 b'revision',
750 750 3
751 751 ]
752 752 ],
753 753 b'node': b'\xbb\xbal\x06\xb3\x0fD=4\xff\x84\x1b\xc9\x85\xc4\xd0\x82|k\xe4'
754 754 },
755 755 b'e0\n',
756 756 {
757 757 b'path': b'dir0/child1/f',
758 758 b'totalitems': 1
759 759 },
760 760 {
761 761 b'fieldsfollowing': [
762 762 [
763 763 b'revision',
764 764 3
765 765 ]
766 766 ],
767 767 b'node': b'\x12\xfc}\xcdw;Z\n\x92\x9c\xe1\x95"\x80\x83\xc6\xdd\xc9\xce\xc4'
768 768 },
769 769 b'f0\n',
770 770 {
771 771 b'path': b'dir0/d',
772 772 b'totalitems': 1
773 773 },
774 774 {
775 775 b'fieldsfollowing': [
776 776 [
777 777 b'revision',
778 778 3
779 779 ]
780 780 ],
781 781 b'node': b'\x93\x88)\xad\x01R}2\xba\x06_\x81#6\xfe\xc7\x9d\xdd9G'
782 782 },
783 783 b'd1\n',
784 784 {
785 785 b'path': b'g',
786 786 b'totalitems': 1
787 787 },
788 788 {
789 789 b'fieldsfollowing': [
790 790 [
791 791 b'revision',
792 792 3
793 793 ]
794 794 ],
795 795 b'node': b'\xde\xca\xba5DFjI\x95r\xe9\x0f\xac\xe6\xfa\x0c!k\xba\x8c'
796 796 },
797 797 b'g0\n',
798 798 {
799 799 b'path': b'h',
800 800 b'totalitems': 1
801 801 },
802 802 {
803 803 b'fieldsfollowing': [
804 804 [
805 805 b'revision',
806 806 3
807 807 ]
808 808 ],
809 809 b'node': b'\x03A\xfc\x84\x1b\xb5\xb4\xba\x93\xb2mM\xdaa\xf7y6]\xb3K'
810 810 },
811 811 b'h0\n'
812 812 ]
813 813
814 814 haveparents=True should emit a delta
815 815
816 816 $ sendhttpv2peer << EOF
817 817 > command filesdata
818 818 > revisions eval:[{
819 819 > b'type': b'changesetexplicit',
820 820 > b'nodes': [
821 821 > b'\x5b\x0b\x1a\x23\x57\x7e\x20\x5e\xa2\x40\xe3\x9c\x97\x04\xe2\x8d\x76\x97\xcb\xd8',
822 822 > ]}]
823 823 > fields eval:[b'revision']
824 824 > haveparents eval:True
825 825 > EOF
826 826 creating http peer for wire protocol version 2
827 827 sending filesdata command
828 828 response: gen[
829 829 {
830 830 b'totalitems': 4,
831 831 b'totalpaths': 4
832 832 },
833 833 {
834 834 b'path': b'a',
835 835 b'totalitems': 1
836 836 },
837 837 {
838 838 b'deltabasenode': b'd\x9d\x14\x9d\xf4=\x83\x88%#\xb7\xfb\x1ej:\xf6\xf1\x90{9',
839 839 b'fieldsfollowing': [
840 840 [
841 841 b'delta',
842 842 15
843 843 ]
844 844 ],
845 845 b'node': b'\n\x862\x1f\x13y\xd1\xa9\xec\xd0W\x9a"\x97z\xf7\xa5\xac\xaf\x11'
846 846 },
847 847 b'\x00\x00\x00Q\x00\x00\x00Q\x00\x00\x00\x03a1\n',
848 848 {
849 849 b'path': b'dir0/d',
850 850 b'totalitems': 1
851 851 },
852 852 {
853 853 b'deltabasenode': b'S\x82\x06\xdc\x97\x1eR\x15@\xd6\x84:\xbf\xe6\xd1`2\xf6\xd4&',
854 854 b'fieldsfollowing': [
855 855 [
856 856 b'delta',
857 857 15
858 858 ]
859 859 ],
860 860 b'node': b'\x93\x88)\xad\x01R}2\xba\x06_\x81#6\xfe\xc7\x9d\xdd9G'
861 861 },
862 862 b'\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x03d1\n',
863 863 {
864 864 b'path': b'g',
865 865 b'totalitems': 1
866 866 },
867 867 {
868 868 b'fieldsfollowing': [
869 869 [
870 870 b'revision',
871 871 3
872 872 ]
873 873 ],
874 874 b'node': b'\xde\xca\xba5DFjI\x95r\xe9\x0f\xac\xe6\xfa\x0c!k\xba\x8c'
875 875 },
876 876 b'g0\n',
877 877 {
878 878 b'path': b'h',
879 879 b'totalitems': 1
880 880 },
881 881 {
882 882 b'fieldsfollowing': [
883 883 [
884 884 b'revision',
885 885 3
886 886 ]
887 887 ],
888 888 b'node': b'\x03A\xfc\x84\x1b\xb5\xb4\xba\x93\xb2mM\xdaa\xf7y6]\xb3K'
889 889 },
890 890 b'h0\n'
891 891 ]
892 892
893 893 Requesting multiple revisions works
894 894 (first revision is a fulltext since haveparents=False by default)
895 895
896 896 $ sendhttpv2peer << EOF
897 897 > command filesdata
898 898 > revisions eval:[{
899 899 > b'type': b'changesetexplicit',
900 900 > b'nodes': [
901 901 > b'\x6e\x87\x5f\xf1\x8c\x22\x76\x59\xad\x61\x43\xbb\x35\x80\xc6\x57\x00\x73\x48\x84',
902 902 > b'\x5b\x0b\x1a\x23\x57\x7e\x20\x5e\xa2\x40\xe3\x9c\x97\x04\xe2\x8d\x76\x97\xcb\xd8',
903 903 > b'\xb9\x1c\x03\xcb\xba\x35\x19\xab\x14\x9b\x6c\xd0\xa0\xaf\xbd\xb5\xcf\x1b\x5c\x8a',
904 904 > ]}]
905 905 > fields eval:[b'revision']
906 906 > EOF
907 907 creating http peer for wire protocol version 2
908 908 sending filesdata command
909 909 response: gen[
910 910 {
911 911 b'totalitems': 12,
912 912 b'totalpaths': 9
913 913 },
914 914 {
915 915 b'path': b'a',
916 916 b'totalitems': 2
917 917 },
918 918 {
919 919 b'fieldsfollowing': [
920 920 [
921 921 b'revision',
922 922 81
923 923 ]
924 924 ],
925 925 b'node': b'd\x9d\x14\x9d\xf4=\x83\x88%#\xb7\xfb\x1ej:\xf6\xf1\x90{9'
926 926 },
927 927 b'a0\n00000000000000000000000000000000000000\n11111111111111111111111111111111111111\n',
928 928 {
929 929 b'deltabasenode': b'd\x9d\x14\x9d\xf4=\x83\x88%#\xb7\xfb\x1ej:\xf6\xf1\x90{9',
930 930 b'fieldsfollowing': [
931 931 [
932 932 b'delta',
933 933 15
934 934 ]
935 935 ],
936 936 b'node': b'\n\x862\x1f\x13y\xd1\xa9\xec\xd0W\x9a"\x97z\xf7\xa5\xac\xaf\x11'
937 937 },
938 938 b'\x00\x00\x00Q\x00\x00\x00Q\x00\x00\x00\x03a1\n',
939 939 {
940 940 b'path': b'b',
941 941 b'totalitems': 1
942 942 },
943 943 {
944 944 b'fieldsfollowing': [
945 945 [
946 946 b'revision',
947 947 81
948 948 ]
949 949 ],
950 950 b'node': b'\x88\xbac\xb8\xd8\xc6 :\xc6z\xc9\x98\xac\xd9\x17K\xf7\x05!\xb2'
951 951 },
952 952 b'b0\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n',
953 953 {
954 954 b'path': b'dir0/c',
955 955 b'totalitems': 1
956 956 },
957 957 {
958 958 b'fieldsfollowing': [
959 959 [
960 960 b'revision',
961 961 3
962 962 ]
963 963 ],
964 964 b'node': b'\x91DE4j\x0c\xa0b\x9b\xd4|\xeb]\xfe\x07\xe4\xd4\xcf%\x01'
965 965 },
966 966 b'c0\n',
967 967 {
968 968 b'path': b'dir0/child0/e',
969 969 b'totalitems': 1
970 970 },
971 971 {
972 972 b'fieldsfollowing': [
973 973 [
974 974 b'revision',
975 975 3
976 976 ]
977 977 ],
978 978 b'node': b'\xbb\xbal\x06\xb3\x0fD=4\xff\x84\x1b\xc9\x85\xc4\xd0\x82|k\xe4'
979 979 },
980 980 b'e0\n',
981 981 {
982 982 b'path': b'dir0/child1/f',
983 983 b'totalitems': 2
984 984 },
985 985 {
986 986 b'fieldsfollowing': [
987 987 [
988 988 b'revision',
989 989 3
990 990 ]
991 991 ],
992 992 b'node': b'\x12\xfc}\xcdw;Z\n\x92\x9c\xe1\x95"\x80\x83\xc6\xdd\xc9\xce\xc4'
993 993 },
994 994 b'f0\n',
995 995 {
996 996 b'deltabasenode': b'\x12\xfc}\xcdw;Z\n\x92\x9c\xe1\x95"\x80\x83\xc6\xdd\xc9\xce\xc4',
997 997 b'fieldsfollowing': [
998 998 [
999 999 b'delta',
1000 1000 15
1001 1001 ]
1002 1002 ],
1003 1003 b'node': b'(\xc7v\xae\x08\xd0\xd5^\xb4\x06H\xb4\x01\xb9\x0f\xf5DH4\x8e'
1004 1004 },
1005 1005 b'\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x03f1\n',
1006 1006 {
1007 1007 b'path': b'dir0/d',
1008 1008 b'totalitems': 2
1009 1009 },
1010 1010 {
1011 1011 b'fieldsfollowing': [
1012 1012 [
1013 1013 b'revision',
1014 1014 3
1015 1015 ]
1016 1016 ],
1017 1017 b'node': b'S\x82\x06\xdc\x97\x1eR\x15@\xd6\x84:\xbf\xe6\xd1`2\xf6\xd4&'
1018 1018 },
1019 1019 b'd0\n',
1020 1020 {
1021 1021 b'deltabasenode': b'S\x82\x06\xdc\x97\x1eR\x15@\xd6\x84:\xbf\xe6\xd1`2\xf6\xd4&',
1022 1022 b'fieldsfollowing': [
1023 1023 [
1024 1024 b'delta',
1025 1025 15
1026 1026 ]
1027 1027 ],
1028 1028 b'node': b'\x93\x88)\xad\x01R}2\xba\x06_\x81#6\xfe\xc7\x9d\xdd9G'
1029 1029 },
1030 1030 b'\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x03d1\n',
1031 1031 {
1032 1032 b'path': b'dir0/i',
1033 1033 b'totalitems': 1
1034 1034 },
1035 1035 {
1036 1036 b'fieldsfollowing': [
1037 1037 [
1038 1038 b'revision',
1039 1039 3
1040 1040 ]
1041 1041 ],
1042 1042 b'node': b'\xd7t\xb5\x80Jq\xfd1\xe1\xae\x05\xea\x8e2\xdd\x9b\xa3\xd8S\xd7'
1043 1043 },
1044 1044 b'i0\n',
1045 1045 {
1046 1046 b'path': b'g',
1047 1047 b'totalitems': 1
1048 1048 },
1049 1049 {
1050 1050 b'fieldsfollowing': [
1051 1051 [
1052 1052 b'revision',
1053 1053 3
1054 1054 ]
1055 1055 ],
1056 1056 b'node': b'\xde\xca\xba5DFjI\x95r\xe9\x0f\xac\xe6\xfa\x0c!k\xba\x8c'
1057 1057 },
1058 1058 b'g0\n',
1059 1059 {
1060 1060 b'path': b'h',
1061 1061 b'totalitems': 1
1062 1062 },
1063 1063 {
1064 1064 b'fieldsfollowing': [
1065 1065 [
1066 1066 b'revision',
1067 1067 3
1068 1068 ]
1069 1069 ],
1070 1070 b'node': b'\x03A\xfc\x84\x1b\xb5\xb4\xba\x93\xb2mM\xdaa\xf7y6]\xb3K'
1071 1071 },
1072 1072 b'h0\n'
1073 1073 ]
1074 1074
1075 1075 Requesting linknode field works
1076 1076
1077 1077 $ sendhttpv2peer << EOF
1078 1078 > command filesdata
1079 1079 > revisions eval:[{
1080 1080 > b'type': b'changesetexplicit',
1081 1081 > b'nodes': [
1082 1082 > b'\x6e\x87\x5f\xf1\x8c\x22\x76\x59\xad\x61\x43\xbb\x35\x80\xc6\x57\x00\x73\x48\x84',
1083 1083 > b'\x5b\x0b\x1a\x23\x57\x7e\x20\x5e\xa2\x40\xe3\x9c\x97\x04\xe2\x8d\x76\x97\xcb\xd8',
1084 1084 > b'\xb9\x1c\x03\xcb\xba\x35\x19\xab\x14\x9b\x6c\xd0\xa0\xaf\xbd\xb5\xcf\x1b\x5c\x8a',
1085 1085 > ]}]
1086 1086 > fields eval:[b'linknode']
1087 1087 > EOF
1088 1088 creating http peer for wire protocol version 2
1089 1089 sending filesdata command
1090 1090 response: gen[
1091 1091 {
1092 1092 b'totalitems': 12,
1093 1093 b'totalpaths': 9
1094 1094 },
1095 1095 {
1096 1096 b'path': b'a',
1097 1097 b'totalitems': 2
1098 1098 },
1099 1099 {
1100 1100 b'linknode': b'n\x87_\xf1\x8c"vY\xadaC\xbb5\x80\xc6W\x00sH\x84',
1101 1101 b'node': b'd\x9d\x14\x9d\xf4=\x83\x88%#\xb7\xfb\x1ej:\xf6\xf1\x90{9'
1102 1102 },
1103 1103 {
1104 1104 b'linknode': b'[\x0b\x1a#W~ ^\xa2@\xe3\x9c\x97\x04\xe2\x8dv\x97\xcb\xd8',
1105 1105 b'node': b'\n\x862\x1f\x13y\xd1\xa9\xec\xd0W\x9a"\x97z\xf7\xa5\xac\xaf\x11'
1106 1106 },
1107 1107 {
1108 1108 b'path': b'b',
1109 1109 b'totalitems': 1
1110 1110 },
1111 1111 {
1112 1112 b'linknode': b'n\x87_\xf1\x8c"vY\xadaC\xbb5\x80\xc6W\x00sH\x84',
1113 1113 b'node': b'\x88\xbac\xb8\xd8\xc6 :\xc6z\xc9\x98\xac\xd9\x17K\xf7\x05!\xb2'
1114 1114 },
1115 1115 {
1116 1116 b'path': b'dir0/c',
1117 1117 b'totalitems': 1
1118 1118 },
1119 1119 {
1120 1120 b'linknode': b'n\x87_\xf1\x8c"vY\xadaC\xbb5\x80\xc6W\x00sH\x84',
1121 1121 b'node': b'\x91DE4j\x0c\xa0b\x9b\xd4|\xeb]\xfe\x07\xe4\xd4\xcf%\x01'
1122 1122 },
1123 1123 {
1124 1124 b'path': b'dir0/child0/e',
1125 1125 b'totalitems': 1
1126 1126 },
1127 1127 {
1128 1128 b'linknode': b'n\x87_\xf1\x8c"vY\xadaC\xbb5\x80\xc6W\x00sH\x84',
1129 1129 b'node': b'\xbb\xbal\x06\xb3\x0fD=4\xff\x84\x1b\xc9\x85\xc4\xd0\x82|k\xe4'
1130 1130 },
1131 1131 {
1132 1132 b'path': b'dir0/child1/f',
1133 1133 b'totalitems': 2
1134 1134 },
1135 1135 {
1136 1136 b'linknode': b'n\x87_\xf1\x8c"vY\xadaC\xbb5\x80\xc6W\x00sH\x84',
1137 1137 b'node': b'\x12\xfc}\xcdw;Z\n\x92\x9c\xe1\x95"\x80\x83\xc6\xdd\xc9\xce\xc4'
1138 1138 },
1139 1139 {
1140 1140 b'linknode': b'\xb9\x1c\x03\xcb\xba5\x19\xab\x14\x9bl\xd0\xa0\xaf\xbd\xb5\xcf\x1b\\\x8a',
1141 1141 b'node': b'(\xc7v\xae\x08\xd0\xd5^\xb4\x06H\xb4\x01\xb9\x0f\xf5DH4\x8e'
1142 1142 },
1143 1143 {
1144 1144 b'path': b'dir0/d',
1145 1145 b'totalitems': 2
1146 1146 },
1147 1147 {
1148 1148 b'linknode': b'n\x87_\xf1\x8c"vY\xadaC\xbb5\x80\xc6W\x00sH\x84',
1149 1149 b'node': b'S\x82\x06\xdc\x97\x1eR\x15@\xd6\x84:\xbf\xe6\xd1`2\xf6\xd4&'
1150 1150 },
1151 1151 {
1152 1152 b'linknode': b'[\x0b\x1a#W~ ^\xa2@\xe3\x9c\x97\x04\xe2\x8dv\x97\xcb\xd8',
1153 1153 b'node': b'\x93\x88)\xad\x01R}2\xba\x06_\x81#6\xfe\xc7\x9d\xdd9G'
1154 1154 },
1155 1155 {
1156 1156 b'path': b'dir0/i',
1157 1157 b'totalitems': 1
1158 1158 },
1159 1159 {
1160 1160 b'linknode': b'\xb9\x1c\x03\xcb\xba5\x19\xab\x14\x9bl\xd0\xa0\xaf\xbd\xb5\xcf\x1b\\\x8a',
1161 1161 b'node': b'\xd7t\xb5\x80Jq\xfd1\xe1\xae\x05\xea\x8e2\xdd\x9b\xa3\xd8S\xd7'
1162 1162 },
1163 1163 {
1164 1164 b'path': b'g',
1165 1165 b'totalitems': 1
1166 1166 },
1167 1167 {
1168 1168 b'linknode': b'[\x0b\x1a#W~ ^\xa2@\xe3\x9c\x97\x04\xe2\x8dv\x97\xcb\xd8',
1169 1169 b'node': b'\xde\xca\xba5DFjI\x95r\xe9\x0f\xac\xe6\xfa\x0c!k\xba\x8c'
1170 1170 },
1171 1171 {
1172 1172 b'path': b'h',
1173 1173 b'totalitems': 1
1174 1174 },
1175 1175 {
1176 1176 b'linknode': b'[\x0b\x1a#W~ ^\xa2@\xe3\x9c\x97\x04\xe2\x8dv\x97\xcb\xd8',
1177 1177 b'node': b'\x03A\xfc\x84\x1b\xb5\xb4\xba\x93\xb2mM\xdaa\xf7y6]\xb3K'
1178 1178 }
1179 1179 ]
1180 1180
1181 1181 Test behavior where a file node is introduced in 2 DAG heads
1182 1182
1183 1183 Request for changeset introducing filenode returns linknode as self
1184 1184
1185 1185 $ sendhttpv2peer << EOF
1186 1186 > command filesdata
1187 1187 > revisions eval:[{
1188 1188 > b'type': b'changesetexplicit',
1189 1189 > b'nodes': [
1190 1190 > b'\xb1\x6c\xce\x29\x67\xc1\x74\x9e\xf4\xf4\xe3\x08\x6a\x80\x6c\xfb\xad\x8a\x3a\xf7',
1191 1191 > ]}]
1192 1192 > fields eval:[b'linknode']
1193 1193 > pathfilter eval:{b'include': [b'path:dupe-file']}
1194 1194 > EOF
1195 1195 creating http peer for wire protocol version 2
1196 1196 sending filesdata command
1197 1197 response: gen[
1198 1198 {
1199 1199 b'totalitems': 1,
1200 1200 b'totalpaths': 1
1201 1201 },
1202 1202 {
1203 1203 b'path': b'dupe-file',
1204 1204 b'totalitems': 1
1205 1205 },
1206 1206 {
1207 1207 b'linknode': b'\xb1l\xce)g\xc1t\x9e\xf4\xf4\xe3\x08j\x80l\xfb\xad\x8a:\xf7',
1208 1208 b'node': b'.\xd2\xa3\x91*\x0b$P C\xea\xe8N\xe4\xb2y\xc1\x8b\x90\xdd'
1209 1209 }
1210 1210 ]
1211 1211
1212 1212 $ sendhttpv2peer << EOF
1213 1213 > command filesdata
1214 1214 > revisions eval:[{
1215 1215 > b'type': b'changesetexplicit',
1216 1216 > b'nodes': [
1217 1217 > b'\xb1\x6c\xce\x29\x67\xc1\x74\x9e\xf4\xf4\xe3\x08\x6a\x80\x6c\xfb\xad\x8a\x3a\xf7',
1218 1218 > ]}]
1219 1219 > fields eval:[b'linknode']
1220 1220 > haveparents eval:True
1221 1221 > pathfilter eval:{b'include': [b'path:dupe-file']}
1222 1222 > EOF
1223 1223 creating http peer for wire protocol version 2
1224 1224 sending filesdata command
1225 1225 response: gen[
1226 1226 {
1227 1227 b'totalitems': 1,
1228 1228 b'totalpaths': 1
1229 1229 },
1230 1230 {
1231 1231 b'path': b'dupe-file',
1232 1232 b'totalitems': 1
1233 1233 },
1234 1234 {
1235 1235 b'linknode': b'\xb1l\xce)g\xc1t\x9e\xf4\xf4\xe3\x08j\x80l\xfb\xad\x8a:\xf7',
1236 1236 b'node': b'.\xd2\xa3\x91*\x0b$P C\xea\xe8N\xe4\xb2y\xc1\x8b\x90\xdd'
1237 1237 }
1238 1238 ]
1239 1239
1240 1240 Request for changeset where recorded linknode isn't in DAG ancestry will get
1241 1241 rewritten accordingly
1242 1242
1243 1243 $ sendhttpv2peer << EOF
1244 1244 > command filesdata
1245 1245 > revisions eval:[{
1246 1246 > b'type': b'changesetexplicit',
1247 1247 > b'nodes': [
1248 1248 > b'\x47\xfc\x30\x58\x09\x11\x23\x2c\xb2\x64\x67\x5b\x40\x28\x19\xde\xdd\xf6\xc6\xf0',
1249 1249 > ]}]
1250 1250 > fields eval:[b'linknode']
1251 1251 > pathfilter eval:{b'include': [b'path:dupe-file']}
1252 1252 > EOF
1253 1253 creating http peer for wire protocol version 2
1254 1254 sending filesdata command
1255 1255 response: gen[
1256 1256 {
1257 1257 b'totalitems': 1,
1258 1258 b'totalpaths': 1
1259 1259 },
1260 1260 {
1261 1261 b'path': b'dupe-file',
1262 1262 b'totalitems': 1
1263 1263 },
1264 1264 {
1265 1265 b'linknode': b'G\xfc0X\t\x11#,\xb2dg[@(\x19\xde\xdd\xf6\xc6\xf0',
1266 1266 b'node': b'.\xd2\xa3\x91*\x0b$P C\xea\xe8N\xe4\xb2y\xc1\x8b\x90\xdd'
1267 1267 }
1268 1268 ]
1269 1269
1270 TODO this is buggy
1271
1272 1270 $ sendhttpv2peer << EOF
1273 1271 > command filesdata
1274 1272 > revisions eval:[{
1275 1273 > b'type': b'changesetexplicit',
1276 1274 > b'nodes': [
1277 1275 > b'\x47\xfc\x30\x58\x09\x11\x23\x2c\xb2\x64\x67\x5b\x40\x28\x19\xde\xdd\xf6\xc6\xf0',
1278 1276 > ]}]
1279 1277 > fields eval:[b'linknode']
1280 1278 > haveparents eval:True
1281 1279 > pathfilter eval:{b'include': [b'path:dupe-file']}
1282 1280 > EOF
1283 1281 creating http peer for wire protocol version 2
1284 1282 sending filesdata command
1285 1283 response: gen[
1286 1284 {
1287 b'totalitems': 0,
1288 b'totalpaths': 0
1285 b'totalitems': 1,
1286 b'totalpaths': 1
1287 },
1288 {
1289 b'path': b'dupe-file',
1290 b'totalitems': 1
1291 },
1292 {
1293 b'linknode': b'G\xfc0X\t\x11#,\xb2dg[@(\x19\xde\xdd\xf6\xc6\xf0',
1294 b'node': b'.\xd2\xa3\x91*\x0b$P C\xea\xe8N\xe4\xb2y\xc1\x8b\x90\xdd'
1289 1295 }
1290 1296 ]
1291 1297
1292 1298 $ cat error.log
@@ -1,1320 +1,1305 b''
1 1 Tests for wire protocol version 2 exchange.
2 2 Tests in this file should be folded into existing tests once protocol
3 3 v2 has enough features that it can be enabled via #testcase in existing
4 4 tests.
5 5
6 6 $ . $TESTDIR/wireprotohelpers.sh
7 7 $ enablehttpv2client
8 8
9 9 $ hg init server-simple
10 10 $ enablehttpv2 server-simple
11 11 $ cd server-simple
12 12 $ cat >> .hg/hgrc << EOF
13 13 > [phases]
14 14 > publish = false
15 15 > EOF
16 16 $ echo a0 > a
17 17 $ echo b0 > b
18 18 $ hg -q commit -A -m 'commit 0'
19 19
20 20 $ echo a1 > a
21 21 $ hg commit -m 'commit 1'
22 22 $ hg phase --public -r .
23 23 $ echo a2 > a
24 24 $ hg commit -m 'commit 2'
25 25
26 26 $ hg -q up -r 0
27 27 $ echo b1 > b
28 28 $ hg -q commit -m 'head 2 commit 1'
29 29 $ echo b2 > b
30 30 $ hg -q commit -m 'head 2 commit 2'
31 31
32 32 $ hg serve -p $HGPORT -d --pid-file hg.pid -E error.log
33 33 $ cat hg.pid > $DAEMON_PIDS
34 34
35 35 $ cd ..
36 36
37 37 Test basic clone
38 38
39 39 $ hg --debug clone -U http://localhost:$HGPORT client-simple
40 40 using http://localhost:$HGPORT/
41 41 sending capabilities command
42 42 query 1; heads
43 43 sending 2 commands
44 44 sending command heads: {}
45 45 sending command known: {
46 46 'nodes': []
47 47 }
48 48 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
49 49 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
50 50 received frame(size=43; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
51 51 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
52 52 received frame(size=11; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
53 53 received frame(size=1; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
54 54 received frame(size=0; request=3; stream=2; streamflags=; type=command-response; flags=eos)
55 55 sending 1 commands
56 56 sending command changesetdata: {
57 57 'fields': set([
58 58 'bookmarks',
59 59 'parents',
60 60 'phase',
61 61 'revision'
62 62 ]),
63 63 'revisions': [
64 64 {
65 65 'heads': [
66 66 '\xca\xa2\xa4eE\x1d\xd1\xfa\xcd\xa0\xf5\xb1#\x12\xc3UXA\x88\xa1',
67 67 '\xcd%4vk\xec\xe18\xc7\xc1\xaf\xdch%0/\x0fb\xd8\x1f'
68 68 ],
69 69 'roots': [],
70 70 'type': 'changesetdagrange'
71 71 }
72 72 ]
73 73 }
74 74 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
75 75 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
76 76 received frame(size=941; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
77 77 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
78 78 add changeset 3390ef850073
79 79 add changeset 4432d83626e8
80 80 add changeset cd2534766bec
81 81 add changeset e96ae20f4188
82 82 add changeset caa2a465451d
83 83 checking for updated bookmarks
84 84 sending 1 commands
85 85 sending command manifestdata: {
86 86 'fields': set([
87 87 'parents',
88 88 'revision'
89 89 ]),
90 90 'haveparents': True,
91 91 'nodes': [
92 92 '\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A',
93 93 '\xa9\x88\xfbCX>\x87\x1d\x1e\xd5u\x0e\xe0t\xc6\xd8@\xbb\xbf\xc8',
94 94 '\xec\x80NH\x8c \x88\xc25\t\x9a\x10 u\x13\xbe\xcd\xc3\xdd\xa5',
95 95 '\x04\\\x7f9\'\xda\x13\xe7Z\xf8\xf0\xe4\xf0HI\xe4a\xa9x\x0f',
96 96 '7\x9c\xb0\xc2\xe6d\\y\xdd\xc5\x9a\x1dG\'\xa9\xfb\x83\n\xeb&'
97 97 ],
98 98 'tree': ''
99 99 }
100 100 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
101 101 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
102 102 received frame(size=992; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
103 103 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
104 104 sending 1 commands
105 105 sending command filesdata: {
106 106 'fields': set([
107 107 'parents',
108 108 'revision'
109 109 ]),
110 110 'haveparents': True,
111 111 'revisions': [
112 112 {
113 113 'nodes': [
114 114 '3\x90\xef\x85\x00s\xfb\xc2\xf0\xdf\xff"D4,\x8e\x92)\x01:',
115 115 'D2\xd86&\xe8\xa9\x86U\xf0b\xec\x1f*C\xb0\x7f\x7f\xbb\xb0',
116 116 '\xcd%4vk\xec\xe18\xc7\xc1\xaf\xdch%0/\x0fb\xd8\x1f',
117 117 '\xe9j\xe2\x0fA\x88H{\x9a\xe4\xef9A\xc2|\x81\x141F\xe5',
118 118 '\xca\xa2\xa4eE\x1d\xd1\xfa\xcd\xa0\xf5\xb1#\x12\xc3UXA\x88\xa1'
119 119 ],
120 120 'type': 'changesetexplicit'
121 121 }
122 122 ]
123 123 }
124 124 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
125 125 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
126 126 received frame(size=901; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
127 127 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
128 128 updating the branch cache
129 129 new changesets 3390ef850073:caa2a465451d (3 drafts)
130 130 (sent 5 HTTP requests and * bytes; received * bytes in responses) (glob)
131 131
132 132 All changesets should have been transferred
133 133
134 134 $ hg -R client-simple debugindex -c
135 135 rev linkrev nodeid p1 p2
136 136 0 0 3390ef850073 000000000000 000000000000
137 137 1 1 4432d83626e8 3390ef850073 000000000000
138 138 2 2 cd2534766bec 4432d83626e8 000000000000
139 139 3 3 e96ae20f4188 3390ef850073 000000000000
140 140 4 4 caa2a465451d e96ae20f4188 000000000000
141 141
142 142 $ hg -R client-simple log -G -T '{rev} {node} {phase}\n'
143 143 o 4 caa2a465451dd1facda0f5b12312c355584188a1 draft
144 144 |
145 145 o 3 e96ae20f4188487b9ae4ef3941c27c81143146e5 draft
146 146 |
147 147 | o 2 cd2534766bece138c7c1afdc6825302f0f62d81f draft
148 148 | |
149 149 | o 1 4432d83626e8a98655f062ec1f2a43b07f7fbbb0 public
150 150 |/
151 151 o 0 3390ef850073fbc2f0dfff2244342c8e9229013a public
152 152
153 153
154 154 All manifests should have been transferred
155 155
156 156 $ hg -R client-simple debugindex -m
157 157 rev linkrev nodeid p1 p2
158 158 0 0 992f4779029a 000000000000 000000000000
159 159 1 1 a988fb43583e 992f4779029a 000000000000
160 160 2 2 ec804e488c20 a988fb43583e 000000000000
161 161 3 3 045c7f3927da 992f4779029a 000000000000
162 162 4 4 379cb0c2e664 045c7f3927da 000000000000
163 163
164 164 Cloning only a specific revision works
165 165
166 166 $ hg --debug clone -U -r 4432d83626e8 http://localhost:$HGPORT client-singlehead
167 167 using http://localhost:$HGPORT/
168 168 sending capabilities command
169 169 sending 1 commands
170 170 sending command lookup: {
171 171 'key': '4432d83626e8'
172 172 }
173 173 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
174 174 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
175 175 received frame(size=21; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
176 176 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
177 177 query 1; heads
178 178 sending 2 commands
179 179 sending command heads: {}
180 180 sending command known: {
181 181 'nodes': []
182 182 }
183 183 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
184 184 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
185 185 received frame(size=43; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
186 186 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
187 187 received frame(size=11; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
188 188 received frame(size=1; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
189 189 received frame(size=0; request=3; stream=2; streamflags=; type=command-response; flags=eos)
190 190 sending 1 commands
191 191 sending command changesetdata: {
192 192 'fields': set([
193 193 'bookmarks',
194 194 'parents',
195 195 'phase',
196 196 'revision'
197 197 ]),
198 198 'revisions': [
199 199 {
200 200 'heads': [
201 201 'D2\xd86&\xe8\xa9\x86U\xf0b\xec\x1f*C\xb0\x7f\x7f\xbb\xb0'
202 202 ],
203 203 'roots': [],
204 204 'type': 'changesetdagrange'
205 205 }
206 206 ]
207 207 }
208 208 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
209 209 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
210 210 received frame(size=381; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
211 211 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
212 212 add changeset 3390ef850073
213 213 add changeset 4432d83626e8
214 214 checking for updated bookmarks
215 215 sending 1 commands
216 216 sending command manifestdata: {
217 217 'fields': set([
218 218 'parents',
219 219 'revision'
220 220 ]),
221 221 'haveparents': True,
222 222 'nodes': [
223 223 '\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A',
224 224 '\xa9\x88\xfbCX>\x87\x1d\x1e\xd5u\x0e\xe0t\xc6\xd8@\xbb\xbf\xc8'
225 225 ],
226 226 'tree': ''
227 227 }
228 228 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
229 229 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
230 230 received frame(size=404; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
231 231 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
232 232 sending 1 commands
233 233 sending command filesdata: {
234 234 'fields': set([
235 235 'parents',
236 236 'revision'
237 237 ]),
238 238 'haveparents': True,
239 239 'revisions': [
240 240 {
241 241 'nodes': [
242 242 '3\x90\xef\x85\x00s\xfb\xc2\xf0\xdf\xff"D4,\x8e\x92)\x01:',
243 243 'D2\xd86&\xe8\xa9\x86U\xf0b\xec\x1f*C\xb0\x7f\x7f\xbb\xb0'
244 244 ],
245 245 'type': 'changesetexplicit'
246 246 }
247 247 ]
248 248 }
249 249 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
250 250 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
251 251 received frame(size=439; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
252 252 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
253 253 updating the branch cache
254 254 new changesets 3390ef850073:4432d83626e8
255 255 (sent 6 HTTP requests and * bytes; received * bytes in responses) (glob)
256 256
257 257 $ cd client-singlehead
258 258
259 259 $ hg log -G -T '{rev} {node} {phase}\n'
260 260 o 1 4432d83626e8a98655f062ec1f2a43b07f7fbbb0 public
261 261 |
262 262 o 0 3390ef850073fbc2f0dfff2244342c8e9229013a public
263 263
264 264
265 265 $ hg debugindex -m
266 266 rev linkrev nodeid p1 p2
267 267 0 0 992f4779029a 000000000000 000000000000
268 268 1 1 a988fb43583e 992f4779029a 000000000000
269 269
270 270 Incremental pull works
271 271
272 272 $ hg --debug pull
273 273 pulling from http://localhost:$HGPORT/
274 274 using http://localhost:$HGPORT/
275 275 sending capabilities command
276 276 query 1; heads
277 277 sending 2 commands
278 278 sending command heads: {}
279 279 sending command known: {
280 280 'nodes': [
281 281 'D2\xd86&\xe8\xa9\x86U\xf0b\xec\x1f*C\xb0\x7f\x7f\xbb\xb0'
282 282 ]
283 283 }
284 284 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
285 285 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
286 286 received frame(size=43; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
287 287 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
288 288 received frame(size=11; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
289 289 received frame(size=2; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
290 290 received frame(size=0; request=3; stream=2; streamflags=; type=command-response; flags=eos)
291 291 searching for changes
292 292 all local heads known remotely
293 293 sending 1 commands
294 294 sending command changesetdata: {
295 295 'fields': set([
296 296 'bookmarks',
297 297 'parents',
298 298 'phase',
299 299 'revision'
300 300 ]),
301 301 'revisions': [
302 302 {
303 303 'heads': [
304 304 '\xca\xa2\xa4eE\x1d\xd1\xfa\xcd\xa0\xf5\xb1#\x12\xc3UXA\x88\xa1',
305 305 '\xcd%4vk\xec\xe18\xc7\xc1\xaf\xdch%0/\x0fb\xd8\x1f'
306 306 ],
307 307 'roots': [
308 308 'D2\xd86&\xe8\xa9\x86U\xf0b\xec\x1f*C\xb0\x7f\x7f\xbb\xb0'
309 309 ],
310 310 'type': 'changesetdagrange'
311 311 }
312 312 ]
313 313 }
314 314 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
315 315 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
316 316 received frame(size=573; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
317 317 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
318 318 add changeset cd2534766bec
319 319 add changeset e96ae20f4188
320 320 add changeset caa2a465451d
321 321 checking for updated bookmarks
322 322 sending 1 commands
323 323 sending command manifestdata: {
324 324 'fields': set([
325 325 'parents',
326 326 'revision'
327 327 ]),
328 328 'haveparents': True,
329 329 'nodes': [
330 330 '\xec\x80NH\x8c \x88\xc25\t\x9a\x10 u\x13\xbe\xcd\xc3\xdd\xa5',
331 331 '\x04\\\x7f9\'\xda\x13\xe7Z\xf8\xf0\xe4\xf0HI\xe4a\xa9x\x0f',
332 332 '7\x9c\xb0\xc2\xe6d\\y\xdd\xc5\x9a\x1dG\'\xa9\xfb\x83\n\xeb&'
333 333 ],
334 334 'tree': ''
335 335 }
336 336 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
337 337 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
338 338 received frame(size=601; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
339 339 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
340 340 sending 1 commands
341 341 sending command filesdata: {
342 342 'fields': set([
343 343 'parents',
344 344 'revision'
345 345 ]),
346 346 'haveparents': True,
347 347 'revisions': [
348 348 {
349 349 'nodes': [
350 350 '\xcd%4vk\xec\xe18\xc7\xc1\xaf\xdch%0/\x0fb\xd8\x1f',
351 351 '\xe9j\xe2\x0fA\x88H{\x9a\xe4\xef9A\xc2|\x81\x141F\xe5',
352 352 '\xca\xa2\xa4eE\x1d\xd1\xfa\xcd\xa0\xf5\xb1#\x12\xc3UXA\x88\xa1'
353 353 ],
354 354 'type': 'changesetexplicit'
355 355 }
356 356 ]
357 357 }
358 358 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
359 359 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
360 360 received frame(size=527; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
361 361 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
362 362 updating the branch cache
363 363 new changesets cd2534766bec:caa2a465451d (3 drafts)
364 364 (run 'hg update' to get a working copy)
365 365 (sent 5 HTTP requests and * bytes; received * bytes in responses) (glob)
366 366
367 367 $ hg log -G -T '{rev} {node} {phase}\n'
368 368 o 4 caa2a465451dd1facda0f5b12312c355584188a1 draft
369 369 |
370 370 o 3 e96ae20f4188487b9ae4ef3941c27c81143146e5 draft
371 371 |
372 372 | o 2 cd2534766bece138c7c1afdc6825302f0f62d81f draft
373 373 | |
374 374 | o 1 4432d83626e8a98655f062ec1f2a43b07f7fbbb0 public
375 375 |/
376 376 o 0 3390ef850073fbc2f0dfff2244342c8e9229013a public
377 377
378 378
379 379 $ hg debugindex -m
380 380 rev linkrev nodeid p1 p2
381 381 0 0 992f4779029a 000000000000 000000000000
382 382 1 1 a988fb43583e 992f4779029a 000000000000
383 383 2 2 ec804e488c20 a988fb43583e 000000000000
384 384 3 3 045c7f3927da 992f4779029a 000000000000
385 385 4 4 379cb0c2e664 045c7f3927da 000000000000
386 386
387 387 Phase-only update works
388 388 TODO this doesn't work
389 389
390 390 $ hg -R ../server-simple phase --public -r caa2a465451dd
391 391 $ hg --debug pull
392 392 pulling from http://localhost:$HGPORT/
393 393 using http://localhost:$HGPORT/
394 394 sending capabilities command
395 395 query 1; heads
396 396 sending 2 commands
397 397 sending command heads: {}
398 398 sending command known: {
399 399 'nodes': [
400 400 '\xcd%4vk\xec\xe18\xc7\xc1\xaf\xdch%0/\x0fb\xd8\x1f',
401 401 '\xca\xa2\xa4eE\x1d\xd1\xfa\xcd\xa0\xf5\xb1#\x12\xc3UXA\x88\xa1'
402 402 ]
403 403 }
404 404 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
405 405 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
406 406 received frame(size=43; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
407 407 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
408 408 received frame(size=11; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
409 409 received frame(size=3; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
410 410 received frame(size=0; request=3; stream=2; streamflags=; type=command-response; flags=eos)
411 411 searching for changes
412 412 all remote heads known locally
413 413 sending 1 commands
414 414 sending command changesetdata: {
415 415 'fields': set([
416 416 'bookmarks',
417 417 'parents',
418 418 'phase',
419 419 'revision'
420 420 ]),
421 421 'revisions': [
422 422 {
423 423 'heads': [
424 424 '\xca\xa2\xa4eE\x1d\xd1\xfa\xcd\xa0\xf5\xb1#\x12\xc3UXA\x88\xa1',
425 425 '\xcd%4vk\xec\xe18\xc7\xc1\xaf\xdch%0/\x0fb\xd8\x1f'
426 426 ],
427 427 'roots': [
428 428 '\xca\xa2\xa4eE\x1d\xd1\xfa\xcd\xa0\xf5\xb1#\x12\xc3UXA\x88\xa1',
429 429 '\xcd%4vk\xec\xe18\xc7\xc1\xaf\xdch%0/\x0fb\xd8\x1f'
430 430 ],
431 431 'type': 'changesetdagrange'
432 432 }
433 433 ]
434 434 }
435 435 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
436 436 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
437 437 received frame(size=13; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
438 438 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
439 439 checking for updated bookmarks
440 440 (run 'hg update' to get a working copy)
441 441 (sent 3 HTTP requests and * bytes; received * bytes in responses) (glob)
442 442
443 443 $ hg log -G -T '{rev} {node} {phase}\n'
444 444 o 4 caa2a465451dd1facda0f5b12312c355584188a1 draft
445 445 |
446 446 o 3 e96ae20f4188487b9ae4ef3941c27c81143146e5 draft
447 447 |
448 448 | o 2 cd2534766bece138c7c1afdc6825302f0f62d81f draft
449 449 | |
450 450 | o 1 4432d83626e8a98655f062ec1f2a43b07f7fbbb0 public
451 451 |/
452 452 o 0 3390ef850073fbc2f0dfff2244342c8e9229013a public
453 453
454 454
455 455 $ cd ..
456 456
457 457 Bookmarks are transferred on clone
458 458
459 459 $ hg -R server-simple bookmark -r 3390ef850073fbc2f0dfff2244342c8e9229013a book-1
460 460 $ hg -R server-simple bookmark -r cd2534766bece138c7c1afdc6825302f0f62d81f book-2
461 461
462 462 $ hg --debug clone -U http://localhost:$HGPORT/ client-bookmarks
463 463 using http://localhost:$HGPORT/
464 464 sending capabilities command
465 465 query 1; heads
466 466 sending 2 commands
467 467 sending command heads: {}
468 468 sending command known: {
469 469 'nodes': []
470 470 }
471 471 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
472 472 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
473 473 received frame(size=43; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
474 474 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
475 475 received frame(size=11; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
476 476 received frame(size=1; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
477 477 received frame(size=0; request=3; stream=2; streamflags=; type=command-response; flags=eos)
478 478 sending 1 commands
479 479 sending command changesetdata: {
480 480 'fields': set([
481 481 'bookmarks',
482 482 'parents',
483 483 'phase',
484 484 'revision'
485 485 ]),
486 486 'revisions': [
487 487 {
488 488 'heads': [
489 489 '\xca\xa2\xa4eE\x1d\xd1\xfa\xcd\xa0\xf5\xb1#\x12\xc3UXA\x88\xa1',
490 490 '\xcd%4vk\xec\xe18\xc7\xc1\xaf\xdch%0/\x0fb\xd8\x1f'
491 491 ],
492 492 'roots': [],
493 493 'type': 'changesetdagrange'
494 494 }
495 495 ]
496 496 }
497 497 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
498 498 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
499 499 received frame(size=979; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
500 500 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
501 501 add changeset 3390ef850073
502 502 add changeset 4432d83626e8
503 503 add changeset cd2534766bec
504 504 add changeset e96ae20f4188
505 505 add changeset caa2a465451d
506 506 checking for updated bookmarks
507 507 adding remote bookmark book-1
508 508 adding remote bookmark book-2
509 509 sending 1 commands
510 510 sending command manifestdata: {
511 511 'fields': set([
512 512 'parents',
513 513 'revision'
514 514 ]),
515 515 'haveparents': True,
516 516 'nodes': [
517 517 '\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A',
518 518 '\xa9\x88\xfbCX>\x87\x1d\x1e\xd5u\x0e\xe0t\xc6\xd8@\xbb\xbf\xc8',
519 519 '\xec\x80NH\x8c \x88\xc25\t\x9a\x10 u\x13\xbe\xcd\xc3\xdd\xa5',
520 520 '\x04\\\x7f9\'\xda\x13\xe7Z\xf8\xf0\xe4\xf0HI\xe4a\xa9x\x0f',
521 521 '7\x9c\xb0\xc2\xe6d\\y\xdd\xc5\x9a\x1dG\'\xa9\xfb\x83\n\xeb&'
522 522 ],
523 523 'tree': ''
524 524 }
525 525 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
526 526 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
527 527 received frame(size=992; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
528 528 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
529 529 sending 1 commands
530 530 sending command filesdata: {
531 531 'fields': set([
532 532 'parents',
533 533 'revision'
534 534 ]),
535 535 'haveparents': True,
536 536 'revisions': [
537 537 {
538 538 'nodes': [
539 539 '3\x90\xef\x85\x00s\xfb\xc2\xf0\xdf\xff"D4,\x8e\x92)\x01:',
540 540 'D2\xd86&\xe8\xa9\x86U\xf0b\xec\x1f*C\xb0\x7f\x7f\xbb\xb0',
541 541 '\xcd%4vk\xec\xe18\xc7\xc1\xaf\xdch%0/\x0fb\xd8\x1f',
542 542 '\xe9j\xe2\x0fA\x88H{\x9a\xe4\xef9A\xc2|\x81\x141F\xe5',
543 543 '\xca\xa2\xa4eE\x1d\xd1\xfa\xcd\xa0\xf5\xb1#\x12\xc3UXA\x88\xa1'
544 544 ],
545 545 'type': 'changesetexplicit'
546 546 }
547 547 ]
548 548 }
549 549 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
550 550 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
551 551 received frame(size=901; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
552 552 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
553 553 updating the branch cache
554 554 new changesets 3390ef850073:caa2a465451d (1 drafts)
555 555 (sent 5 HTTP requests and * bytes; received * bytes in responses) (glob)
556 556
557 557 $ hg -R client-bookmarks bookmarks
558 558 book-1 0:3390ef850073
559 559 book-2 2:cd2534766bec
560 560
561 561 Server-side bookmark moves are reflected during `hg pull`
562 562
563 563 $ hg -R server-simple bookmark -r cd2534766bece138c7c1afdc6825302f0f62d81f book-1
564 564 moving bookmark 'book-1' forward from 3390ef850073
565 565
566 566 $ hg -R client-bookmarks --debug pull
567 567 pulling from http://localhost:$HGPORT/
568 568 using http://localhost:$HGPORT/
569 569 sending capabilities command
570 570 query 1; heads
571 571 sending 2 commands
572 572 sending command heads: {}
573 573 sending command known: {
574 574 'nodes': [
575 575 '\xcd%4vk\xec\xe18\xc7\xc1\xaf\xdch%0/\x0fb\xd8\x1f',
576 576 '\xca\xa2\xa4eE\x1d\xd1\xfa\xcd\xa0\xf5\xb1#\x12\xc3UXA\x88\xa1'
577 577 ]
578 578 }
579 579 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
580 580 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
581 581 received frame(size=43; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
582 582 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
583 583 received frame(size=11; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
584 584 received frame(size=3; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
585 585 received frame(size=0; request=3; stream=2; streamflags=; type=command-response; flags=eos)
586 586 searching for changes
587 587 all remote heads known locally
588 588 sending 1 commands
589 589 sending command changesetdata: {
590 590 'fields': set([
591 591 'bookmarks',
592 592 'parents',
593 593 'phase',
594 594 'revision'
595 595 ]),
596 596 'revisions': [
597 597 {
598 598 'heads': [
599 599 '\xca\xa2\xa4eE\x1d\xd1\xfa\xcd\xa0\xf5\xb1#\x12\xc3UXA\x88\xa1',
600 600 '\xcd%4vk\xec\xe18\xc7\xc1\xaf\xdch%0/\x0fb\xd8\x1f'
601 601 ],
602 602 'roots': [
603 603 '\xca\xa2\xa4eE\x1d\xd1\xfa\xcd\xa0\xf5\xb1#\x12\xc3UXA\x88\xa1',
604 604 '\xcd%4vk\xec\xe18\xc7\xc1\xaf\xdch%0/\x0fb\xd8\x1f'
605 605 ],
606 606 'type': 'changesetdagrange'
607 607 }
608 608 ]
609 609 }
610 610 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
611 611 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
612 612 received frame(size=65; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
613 613 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
614 614 checking for updated bookmarks
615 615 updating bookmark book-1
616 616 (run 'hg update' to get a working copy)
617 617 (sent 3 HTTP requests and * bytes; received * bytes in responses) (glob)
618 618
619 619 $ hg -R client-bookmarks bookmarks
620 620 book-1 2:cd2534766bec
621 621 book-2 2:cd2534766bec
622 622
623 623 $ killdaemons.py
624 624
625 625 Let's set up a slightly more complicated server
626 626
627 627 $ hg init server-2
628 628 $ enablehttpv2 server-2
629 629 $ cd server-2
630 630 $ mkdir dir0 dir1
631 631 $ echo a0 > a
632 632 $ echo b0 > b
633 633 $ hg -q commit -A -m 'commit 0'
634 634 $ echo c0 > dir0/c
635 635 $ echo d0 > dir0/d
636 636 $ hg -q commit -A -m 'commit 1'
637 637 $ echo e0 > dir1/e
638 638 $ echo f0 > dir1/f
639 639 $ hg -q commit -A -m 'commit 2'
640 640 $ echo c1 > dir0/c
641 641 $ echo e1 > dir1/e
642 642 $ hg commit -m 'commit 3'
643 643 $ hg serve -p $HGPORT -d --pid-file hg.pid -E error.log
644 644 $ cat hg.pid > $DAEMON_PIDS
645 645
646 646 $ cd ..
647 647
648 648 Narrow clone only fetches some files
649 649
650 650 $ hg --config extensions.pullext=$TESTDIR/pullext.py --debug clone -U --include dir0/ http://localhost:$HGPORT/ client-narrow-0
651 651 using http://localhost:$HGPORT/
652 652 sending capabilities command
653 653 query 1; heads
654 654 sending 2 commands
655 655 sending command heads: {}
656 656 sending command known: {
657 657 'nodes': []
658 658 }
659 659 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
660 660 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
661 661 received frame(size=22; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
662 662 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
663 663 received frame(size=11; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
664 664 received frame(size=1; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
665 665 received frame(size=0; request=3; stream=2; streamflags=; type=command-response; flags=eos)
666 666 sending 1 commands
667 667 sending command changesetdata: {
668 668 'fields': set([
669 669 'bookmarks',
670 670 'parents',
671 671 'phase',
672 672 'revision'
673 673 ]),
674 674 'revisions': [
675 675 {
676 676 'heads': [
677 677 '\x97v_\xc3\xcdbO\xd1\xfa\x01v\x93,!\xff\xd1j\xdfC.'
678 678 ],
679 679 'roots': [],
680 680 'type': 'changesetdagrange'
681 681 }
682 682 ]
683 683 }
684 684 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
685 685 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
686 686 received frame(size=783; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
687 687 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
688 688 add changeset 3390ef850073
689 689 add changeset b709380892b1
690 690 add changeset 47fe012ab237
691 691 add changeset 97765fc3cd62
692 692 checking for updated bookmarks
693 693 sending 1 commands
694 694 sending command manifestdata: {
695 695 'fields': set([
696 696 'parents',
697 697 'revision'
698 698 ]),
699 699 'haveparents': True,
700 700 'nodes': [
701 701 '\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A',
702 702 '|2 \x1a\xa3\xa1R\xa9\xe6\xa9"+?\xa8\xd0\xe3\x0f\xc2V\xe8',
703 703 '\x8d\xd0W<\x7f\xaf\xe2\x04F\xcc\xea\xac\x05N\xea\xa4x\x91M\xdb',
704 704 '113\x85\xf2!\x8b\x08^\xb2Z\x821\x1e*\xdd\x0e\xeb\x8c3'
705 705 ],
706 706 'tree': ''
707 707 }
708 708 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
709 709 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
710 710 received frame(size=967; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
711 711 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
712 712 sending 1 commands
713 713 sending command filesdata: {
714 714 'fields': set([
715 715 'parents',
716 716 'revision'
717 717 ]),
718 718 'haveparents': True,
719 719 'pathfilter': {
720 720 'include': [
721 721 'path:dir0'
722 722 ]
723 723 },
724 724 'revisions': [
725 725 {
726 726 'nodes': [
727 727 '3\x90\xef\x85\x00s\xfb\xc2\xf0\xdf\xff"D4,\x8e\x92)\x01:',
728 728 '\xb7\t8\x08\x92\xb1\x93\xc1\t\x1d:\x81\x7fp`R\xe3F\x82\x1b',
729 729 'G\xfe\x01*\xb27\xa8\xc7\xfc\x0cx\xf9\xf2mXf\xee\xf3\xf8%',
730 730 '\x97v_\xc3\xcdbO\xd1\xfa\x01v\x93,!\xff\xd1j\xdfC.'
731 731 ],
732 732 'type': 'changesetexplicit'
733 733 }
734 734 ]
735 735 }
736 736 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
737 737 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
738 738 received frame(size=449; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
739 739 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
740 740 updating the branch cache
741 741 new changesets 3390ef850073:97765fc3cd62
742 742 (sent 5 HTTP requests and * bytes; received * bytes in responses) (glob)
743 743
744 744 #if reporevlogstore
745 745 $ find client-narrow-0/.hg/store -type f -name '*.i' | sort
746 746 client-narrow-0/.hg/store/00changelog.i
747 747 client-narrow-0/.hg/store/00manifest.i
748 748 client-narrow-0/.hg/store/data/dir0/c.i
749 749 client-narrow-0/.hg/store/data/dir0/d.i
750 750 #endif
751 751
752 752 --exclude by itself works
753 753
754 754 $ hg --config extensions.pullext=$TESTDIR/pullext.py --debug clone -U --exclude dir0/ http://localhost:$HGPORT/ client-narrow-1
755 755 using http://localhost:$HGPORT/
756 756 sending capabilities command
757 757 query 1; heads
758 758 sending 2 commands
759 759 sending command heads: {}
760 760 sending command known: {
761 761 'nodes': []
762 762 }
763 763 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
764 764 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
765 765 received frame(size=22; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
766 766 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
767 767 received frame(size=11; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
768 768 received frame(size=1; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
769 769 received frame(size=0; request=3; stream=2; streamflags=; type=command-response; flags=eos)
770 770 sending 1 commands
771 771 sending command changesetdata: {
772 772 'fields': set([
773 773 'bookmarks',
774 774 'parents',
775 775 'phase',
776 776 'revision'
777 777 ]),
778 778 'revisions': [
779 779 {
780 780 'heads': [
781 781 '\x97v_\xc3\xcdbO\xd1\xfa\x01v\x93,!\xff\xd1j\xdfC.'
782 782 ],
783 783 'roots': [],
784 784 'type': 'changesetdagrange'
785 785 }
786 786 ]
787 787 }
788 788 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
789 789 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
790 790 received frame(size=783; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
791 791 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
792 792 add changeset 3390ef850073
793 793 add changeset b709380892b1
794 794 add changeset 47fe012ab237
795 795 add changeset 97765fc3cd62
796 796 checking for updated bookmarks
797 797 sending 1 commands
798 798 sending command manifestdata: {
799 799 'fields': set([
800 800 'parents',
801 801 'revision'
802 802 ]),
803 803 'haveparents': True,
804 804 'nodes': [
805 805 '\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A',
806 806 '|2 \x1a\xa3\xa1R\xa9\xe6\xa9"+?\xa8\xd0\xe3\x0f\xc2V\xe8',
807 807 '\x8d\xd0W<\x7f\xaf\xe2\x04F\xcc\xea\xac\x05N\xea\xa4x\x91M\xdb',
808 808 '113\x85\xf2!\x8b\x08^\xb2Z\x821\x1e*\xdd\x0e\xeb\x8c3'
809 809 ],
810 810 'tree': ''
811 811 }
812 812 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
813 813 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
814 814 received frame(size=967; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
815 815 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
816 816 sending 1 commands
817 817 sending command filesdata: {
818 818 'fields': set([
819 819 'parents',
820 820 'revision'
821 821 ]),
822 822 'haveparents': True,
823 823 'pathfilter': {
824 824 'exclude': [
825 825 'path:dir0'
826 826 ],
827 827 'include': [
828 828 'path:.'
829 829 ]
830 830 },
831 831 'revisions': [
832 832 {
833 833 'nodes': [
834 834 '3\x90\xef\x85\x00s\xfb\xc2\xf0\xdf\xff"D4,\x8e\x92)\x01:',
835 835 '\xb7\t8\x08\x92\xb1\x93\xc1\t\x1d:\x81\x7fp`R\xe3F\x82\x1b',
836 836 'G\xfe\x01*\xb27\xa8\xc7\xfc\x0cx\xf9\xf2mXf\xee\xf3\xf8%',
837 837 '\x97v_\xc3\xcdbO\xd1\xfa\x01v\x93,!\xff\xd1j\xdfC.'
838 838 ],
839 839 'type': 'changesetexplicit'
840 840 }
841 841 ]
842 842 }
843 843 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
844 844 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
845 845 received frame(size=709; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
846 846 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
847 847 updating the branch cache
848 848 new changesets 3390ef850073:97765fc3cd62
849 849 (sent 5 HTTP requests and * bytes; received * bytes in responses) (glob)
850 850
851 851 #if reporevlogstore
852 852 $ find client-narrow-1/.hg/store -type f -name '*.i' | sort
853 853 client-narrow-1/.hg/store/00changelog.i
854 854 client-narrow-1/.hg/store/00manifest.i
855 855 client-narrow-1/.hg/store/data/a.i
856 856 client-narrow-1/.hg/store/data/b.i
857 857 client-narrow-1/.hg/store/data/dir1/e.i
858 858 client-narrow-1/.hg/store/data/dir1/f.i
859 859 #endif
860 860
861 861 Mixing --include and --exclude works
862 862
863 863 $ hg --config extensions.pullext=$TESTDIR/pullext.py --debug clone -U --include dir0/ --exclude dir0/c http://localhost:$HGPORT/ client-narrow-2
864 864 using http://localhost:$HGPORT/
865 865 sending capabilities command
866 866 query 1; heads
867 867 sending 2 commands
868 868 sending command heads: {}
869 869 sending command known: {
870 870 'nodes': []
871 871 }
872 872 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
873 873 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
874 874 received frame(size=22; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
875 875 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
876 876 received frame(size=11; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
877 877 received frame(size=1; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
878 878 received frame(size=0; request=3; stream=2; streamflags=; type=command-response; flags=eos)
879 879 sending 1 commands
880 880 sending command changesetdata: {
881 881 'fields': set([
882 882 'bookmarks',
883 883 'parents',
884 884 'phase',
885 885 'revision'
886 886 ]),
887 887 'revisions': [
888 888 {
889 889 'heads': [
890 890 '\x97v_\xc3\xcdbO\xd1\xfa\x01v\x93,!\xff\xd1j\xdfC.'
891 891 ],
892 892 'roots': [],
893 893 'type': 'changesetdagrange'
894 894 }
895 895 ]
896 896 }
897 897 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
898 898 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
899 899 received frame(size=783; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
900 900 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
901 901 add changeset 3390ef850073
902 902 add changeset b709380892b1
903 903 add changeset 47fe012ab237
904 904 add changeset 97765fc3cd62
905 905 checking for updated bookmarks
906 906 sending 1 commands
907 907 sending command manifestdata: {
908 908 'fields': set([
909 909 'parents',
910 910 'revision'
911 911 ]),
912 912 'haveparents': True,
913 913 'nodes': [
914 914 '\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A',
915 915 '|2 \x1a\xa3\xa1R\xa9\xe6\xa9"+?\xa8\xd0\xe3\x0f\xc2V\xe8',
916 916 '\x8d\xd0W<\x7f\xaf\xe2\x04F\xcc\xea\xac\x05N\xea\xa4x\x91M\xdb',
917 917 '113\x85\xf2!\x8b\x08^\xb2Z\x821\x1e*\xdd\x0e\xeb\x8c3'
918 918 ],
919 919 'tree': ''
920 920 }
921 921 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
922 922 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
923 923 received frame(size=967; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
924 924 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
925 925 sending 1 commands
926 926 sending command filesdata: {
927 927 'fields': set([
928 928 'parents',
929 929 'revision'
930 930 ]),
931 931 'haveparents': True,
932 932 'pathfilter': {
933 933 'exclude': [
934 934 'path:dir0/c'
935 935 ],
936 936 'include': [
937 937 'path:dir0'
938 938 ]
939 939 },
940 940 'revisions': [
941 941 {
942 942 'nodes': [
943 943 '3\x90\xef\x85\x00s\xfb\xc2\xf0\xdf\xff"D4,\x8e\x92)\x01:',
944 944 '\xb7\t8\x08\x92\xb1\x93\xc1\t\x1d:\x81\x7fp`R\xe3F\x82\x1b',
945 945 'G\xfe\x01*\xb27\xa8\xc7\xfc\x0cx\xf9\xf2mXf\xee\xf3\xf8%',
946 946 '\x97v_\xc3\xcdbO\xd1\xfa\x01v\x93,!\xff\xd1j\xdfC.'
947 947 ],
948 948 'type': 'changesetexplicit'
949 949 }
950 950 ]
951 951 }
952 952 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
953 953 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
954 954 received frame(size=160; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
955 955 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
956 956 updating the branch cache
957 957 new changesets 3390ef850073:97765fc3cd62
958 958 (sent 5 HTTP requests and * bytes; received * bytes in responses) (glob)
959 959
960 960 #if reporevlogstore
961 961 $ find client-narrow-2/.hg/store -type f -name '*.i' | sort
962 962 client-narrow-2/.hg/store/00changelog.i
963 963 client-narrow-2/.hg/store/00manifest.i
964 964 client-narrow-2/.hg/store/data/dir0/d.i
965 965 #endif
966 966
967 967 --stream will use rawfiledata to transfer changelog and manifestlog, then
968 968 fall through to get files data
969 969
970 970 $ hg --debug clone --stream -U http://localhost:$HGPORT client-stream-0
971 971 using http://localhost:$HGPORT/
972 972 sending capabilities command
973 973 sending 1 commands
974 974 sending command rawstorefiledata: {
975 975 'files': [
976 976 'changelog',
977 977 'manifestlog'
978 978 ]
979 979 }
980 980 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
981 981 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
982 982 received frame(size=1275; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
983 983 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
984 984 updating the branch cache
985 985 query 1; heads
986 986 sending 2 commands
987 987 sending command heads: {}
988 988 sending command known: {
989 989 'nodes': [
990 990 '\x97v_\xc3\xcdbO\xd1\xfa\x01v\x93,!\xff\xd1j\xdfC.'
991 991 ]
992 992 }
993 993 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
994 994 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
995 995 received frame(size=22; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
996 996 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
997 997 received frame(size=11; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
998 998 received frame(size=2; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
999 999 received frame(size=0; request=3; stream=2; streamflags=; type=command-response; flags=eos)
1000 1000 searching for changes
1001 1001 all remote heads known locally
1002 1002 sending 1 commands
1003 1003 sending command changesetdata: {
1004 1004 'fields': set([
1005 1005 'bookmarks',
1006 1006 'parents',
1007 1007 'phase',
1008 1008 'revision'
1009 1009 ]),
1010 1010 'revisions': [
1011 1011 {
1012 1012 'heads': [
1013 1013 '\x97v_\xc3\xcdbO\xd1\xfa\x01v\x93,!\xff\xd1j\xdfC.'
1014 1014 ],
1015 1015 'roots': [
1016 1016 '\x97v_\xc3\xcdbO\xd1\xfa\x01v\x93,!\xff\xd1j\xdfC.'
1017 1017 ],
1018 1018 'type': 'changesetdagrange'
1019 1019 }
1020 1020 ]
1021 1021 }
1022 1022 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
1023 1023 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1024 1024 received frame(size=13; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1025 1025 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
1026 1026 checking for updated bookmarks
1027 1027 sending 1 commands
1028 1028 sending command filesdata: {
1029 1029 'fields': set([
1030 1030 'parents',
1031 1031 'revision'
1032 1032 ]),
1033 1033 'haveparents': True,
1034 1034 'revisions': [
1035 1035 {
1036 1036 'nodes': [
1037 1037 '3\x90\xef\x85\x00s\xfb\xc2\xf0\xdf\xff"D4,\x8e\x92)\x01:',
1038 1038 '\xb7\t8\x08\x92\xb1\x93\xc1\t\x1d:\x81\x7fp`R\xe3F\x82\x1b',
1039 1039 'G\xfe\x01*\xb27\xa8\xc7\xfc\x0cx\xf9\xf2mXf\xee\xf3\xf8%',
1040 1040 '\x97v_\xc3\xcdbO\xd1\xfa\x01v\x93,!\xff\xd1j\xdfC.'
1041 1041 ],
1042 1042 'type': 'changesetexplicit'
1043 1043 }
1044 1044 ]
1045 1045 }
1046 1046 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
1047 1047 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1048 1048 received frame(size=1133; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1049 1049 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
1050 1050 (sent 5 HTTP requests and * bytes; received * bytes in responses) (glob)
1051 1051
1052 1052 --stream + --include/--exclude will only obtain some files
1053 1053
1054 1054 $ hg --debug --config extensions.pullext=$TESTDIR/pullext.py clone --stream --include dir0/ -U http://localhost:$HGPORT client-stream-2
1055 1055 using http://localhost:$HGPORT/
1056 1056 sending capabilities command
1057 1057 sending 1 commands
1058 1058 sending command rawstorefiledata: {
1059 1059 'files': [
1060 1060 'changelog',
1061 1061 'manifestlog'
1062 1062 ]
1063 1063 }
1064 1064 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
1065 1065 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1066 1066 received frame(size=1275; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1067 1067 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
1068 1068 updating the branch cache
1069 1069 query 1; heads
1070 1070 sending 2 commands
1071 1071 sending command heads: {}
1072 1072 sending command known: {
1073 1073 'nodes': [
1074 1074 '\x97v_\xc3\xcdbO\xd1\xfa\x01v\x93,!\xff\xd1j\xdfC.'
1075 1075 ]
1076 1076 }
1077 1077 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
1078 1078 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1079 1079 received frame(size=22; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1080 1080 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
1081 1081 received frame(size=11; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1082 1082 received frame(size=2; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1083 1083 received frame(size=0; request=3; stream=2; streamflags=; type=command-response; flags=eos)
1084 1084 searching for changes
1085 1085 all remote heads known locally
1086 1086 sending 1 commands
1087 1087 sending command changesetdata: {
1088 1088 'fields': set([
1089 1089 'bookmarks',
1090 1090 'parents',
1091 1091 'phase',
1092 1092 'revision'
1093 1093 ]),
1094 1094 'revisions': [
1095 1095 {
1096 1096 'heads': [
1097 1097 '\x97v_\xc3\xcdbO\xd1\xfa\x01v\x93,!\xff\xd1j\xdfC.'
1098 1098 ],
1099 1099 'roots': [
1100 1100 '\x97v_\xc3\xcdbO\xd1\xfa\x01v\x93,!\xff\xd1j\xdfC.'
1101 1101 ],
1102 1102 'type': 'changesetdagrange'
1103 1103 }
1104 1104 ]
1105 1105 }
1106 1106 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
1107 1107 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1108 1108 received frame(size=13; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1109 1109 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
1110 1110 checking for updated bookmarks
1111 1111 sending 1 commands
1112 1112 sending command filesdata: {
1113 1113 'fields': set([
1114 1114 'parents',
1115 1115 'revision'
1116 1116 ]),
1117 1117 'haveparents': True,
1118 1118 'pathfilter': {
1119 1119 'include': [
1120 1120 'path:dir0'
1121 1121 ]
1122 1122 },
1123 1123 'revisions': [
1124 1124 {
1125 1125 'nodes': [
1126 1126 '3\x90\xef\x85\x00s\xfb\xc2\xf0\xdf\xff"D4,\x8e\x92)\x01:',
1127 1127 '\xb7\t8\x08\x92\xb1\x93\xc1\t\x1d:\x81\x7fp`R\xe3F\x82\x1b',
1128 1128 'G\xfe\x01*\xb27\xa8\xc7\xfc\x0cx\xf9\xf2mXf\xee\xf3\xf8%',
1129 1129 '\x97v_\xc3\xcdbO\xd1\xfa\x01v\x93,!\xff\xd1j\xdfC.'
1130 1130 ],
1131 1131 'type': 'changesetexplicit'
1132 1132 }
1133 1133 ]
1134 1134 }
1135 1135 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
1136 1136 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1137 1137 received frame(size=449; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1138 1138 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
1139 1139 (sent 5 HTTP requests and * bytes; received * bytes in responses) (glob)
1140 1140
1141 1141 #if reporevlogstore
1142 1142 $ find client-stream-2/.hg/store -type f -name '*.i' | sort
1143 1143 client-stream-2/.hg/store/00changelog.i
1144 1144 client-stream-2/.hg/store/00manifest.i
1145 1145 client-stream-2/.hg/store/data/dir0/c.i
1146 1146 client-stream-2/.hg/store/data/dir0/d.i
1147 1147 #endif
1148 1148
1149 1149 Shallow clone doesn't work with revlogs
1150 1150
1151 1151 $ hg --debug --config extensions.pullext=$TESTDIR/pullext.py clone --depth 1 -U http://localhost:$HGPORT client-shallow-revlogs
1152 1152 using http://localhost:$HGPORT/
1153 1153 sending capabilities command
1154 1154 query 1; heads
1155 1155 sending 2 commands
1156 1156 sending command heads: {}
1157 1157 sending command known: {
1158 1158 'nodes': []
1159 1159 }
1160 1160 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
1161 1161 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1162 1162 received frame(size=22; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1163 1163 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
1164 1164 received frame(size=11; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1165 1165 received frame(size=1; request=3; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1166 1166 received frame(size=0; request=3; stream=2; streamflags=; type=command-response; flags=eos)
1167 1167 sending 1 commands
1168 1168 sending command changesetdata: {
1169 1169 'fields': set([
1170 1170 'bookmarks',
1171 1171 'parents',
1172 1172 'phase',
1173 1173 'revision'
1174 1174 ]),
1175 1175 'revisions': [
1176 1176 {
1177 1177 'heads': [
1178 1178 '\x97v_\xc3\xcdbO\xd1\xfa\x01v\x93,!\xff\xd1j\xdfC.'
1179 1179 ],
1180 1180 'roots': [],
1181 1181 'type': 'changesetdagrange'
1182 1182 }
1183 1183 ]
1184 1184 }
1185 1185 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
1186 1186 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1187 1187 received frame(size=783; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1188 1188 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
1189 1189 add changeset 3390ef850073
1190 1190 add changeset b709380892b1
1191 1191 add changeset 47fe012ab237
1192 1192 add changeset 97765fc3cd62
1193 1193 checking for updated bookmarks
1194 1194 sending 1 commands
1195 1195 sending command manifestdata: {
1196 1196 'fields': set([
1197 1197 'parents',
1198 1198 'revision'
1199 1199 ]),
1200 1200 'haveparents': True,
1201 1201 'nodes': [
1202 1202 '\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A',
1203 1203 '|2 \x1a\xa3\xa1R\xa9\xe6\xa9"+?\xa8\xd0\xe3\x0f\xc2V\xe8',
1204 1204 '\x8d\xd0W<\x7f\xaf\xe2\x04F\xcc\xea\xac\x05N\xea\xa4x\x91M\xdb',
1205 1205 '113\x85\xf2!\x8b\x08^\xb2Z\x821\x1e*\xdd\x0e\xeb\x8c3'
1206 1206 ],
1207 1207 'tree': ''
1208 1208 }
1209 1209 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
1210 1210 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1211 1211 received frame(size=967; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1212 1212 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
1213 1213 sending 1 commands
1214 1214 sending command filesdata: {
1215 1215 'fields': set([
1216 1216 'linknode',
1217 1217 'parents',
1218 1218 'revision'
1219 1219 ]),
1220 1220 'haveparents': False,
1221 1221 'revisions': [
1222 1222 {
1223 1223 'nodes': [
1224 1224 '\x97v_\xc3\xcdbO\xd1\xfa\x01v\x93,!\xff\xd1j\xdfC.'
1225 1225 ],
1226 1226 'type': 'changesetexplicit'
1227 1227 }
1228 1228 ]
1229 1229 }
1230 1230 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
1231 1231 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1232 1232 received frame(size=1005; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
1233 1233 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
1234 1234 transaction abort!
1235 1235 rollback completed
1236 1236 (sent 5 HTTP requests and * bytes; received * bytes in responses) (glob)
1237 1237 abort: revlog storage does not support missing parents write mode
1238 1238 [255]
1239 1239
1240 1240 $ killdaemons.py
1241 1241
1242 1242 Repo with 2 DAG branches introducing same filenode, to test linknode adjustment
1243 1243
1244 1244 $ hg init server-linknode
1245 1245 $ enablehttpv2 server-linknode
1246 1246 $ cd server-linknode
1247 1247 $ touch foo
1248 1248 $ hg -q commit -Am initial
1249 1249 $ echo foo > dupe-file
1250 1250 $ hg commit -Am 'dupe 1'
1251 1251 adding dupe-file
1252 1252 $ hg -q up -r 0
1253 1253 $ echo foo > dupe-file
1254 1254 $ hg commit -Am 'dupe 2'
1255 1255 adding dupe-file
1256 1256 created new head
1257 1257 $ hg serve -p $HGPORT -d --pid-file hg.pid -E error.log
1258 1258 $ cat hg.pid > $DAEMON_PIDS
1259 1259 $ cd ..
1260 1260
1261 1261 Perform an incremental pull of both heads and ensure linkrev is written out properly
1262 1262
1263 1263 $ hg clone -r 96ee1d7354c4 http://localhost:$HGPORT client-linknode-1
1264 1264 new changesets 96ee1d7354c4
1265 1265 updating to branch default
1266 1266 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1267 1267 $ cd client-linknode-1
1268 1268 $ touch extra
1269 1269 $ hg commit -Am extra
1270 1270 adding extra
1271 1271 $ cd ..
1272 1272
1273 1273 $ hg clone -r 96ee1d7354c4 http://localhost:$HGPORT client-linknode-2
1274 1274 new changesets 96ee1d7354c4
1275 1275 updating to branch default
1276 1276 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1277 1277 $ cd client-linknode-2
1278 1278 $ touch extra
1279 1279 $ hg commit -Am extra
1280 1280 adding extra
1281 1281 $ cd ..
1282 1282
1283 1283 $ hg -R client-linknode-1 pull -r 1681c33f9f80
1284 1284 pulling from http://localhost:$HGPORT/
1285 1285 searching for changes
1286 1286 new changesets 1681c33f9f80
1287 1287 (run 'hg update' to get a working copy)
1288 1288
1289 1289 #if reporevlogstore
1290 1290 $ hg -R client-linknode-1 debugrevlogindex dupe-file
1291 1291 rev linkrev nodeid p1 p2
1292 1292 0 2 2ed2a3912a0b 000000000000 000000000000
1293 1293 #endif
1294 1294
1295 1295 $ hg -R client-linknode-2 pull -r 639c8990d6a5
1296 1296 pulling from http://localhost:$HGPORT/
1297 1297 searching for changes
1298 1298 new changesets 639c8990d6a5
1299 1299 (run 'hg update' to get a working copy)
1300 1300
1301 1301 #if reporevlogstore
1302 1302 $ hg -R client-linknode-2 debugrevlogindex dupe-file
1303 abort: revlog 'dupe-file' not found
1304 [255]
1303 rev linkrev nodeid p1 p2
1304 0 2 2ed2a3912a0b 000000000000 000000000000
1305 1305 #endif
1306
1307 $ hg -R client-linknode-2 verify
1308 checking changesets
1309 checking manifests
1310 crosschecking files in changesets and manifests
1311 checking files
1312 warning: revlog 'data/dupe-file.i' not in fncache!
1313 2: empty or missing dupe-file
1314 dupe-file@2: manifest refers to unknown revision 2ed2a3912a0b
1315 checked 3 changesets with 2 changes to 3 files
1316 1 warnings encountered!
1317 hint: run "hg debugrebuildfncache" to recover from corrupt fncache
1318 2 integrity errors encountered!
1319 (first damaged changeset appears to be 2)
1320 [1]
General Comments 0
You need to be logged in to leave comments. Login now