##// END OF EJS Templates
branching: merge default into stable for 6.8rc0
Raphaël Gomès -
r52926:6454c117 merge 6.8rc0 stable
parent child Browse files
Show More
@@ -0,0 +1,72 b''
1 = Mercurial 6.8rc0 =
2
3 /!\ This is a tentative release, any and all notes below are subject to change or removal.
4
5 As usual, a *lot* of patches don't make it to this list.
6
7 == New Features or performance improvements ==
8
9 * Phases have been reworked to improve their general performance
10 * revset: stop serializing node when using "%ln"
11 * phases: convert remote phase root to node while reading them
12 * phases: use revision number in new_heads
13 * phases: use revision number in analyze_remote_phases
14 * phases: stop using `repo.set` in `remotephasessummary`
15 * phases: move RemotePhasesSummary to revision number
16 * phases: use revision number in `_pushdiscoveryphase`
17 * phases: introduce a performant efficient way to access revision in a set
18 * phases: rework the logic of _pushdiscoveryphase to bound complexity
19 * The Rust working copy code is being used by more places now:
20 * matchers: support patternmatcher in rust
21 * dirstate: remove the python-side whitelist of allowed matchers
22 * stream-clone: disable gc for `_entries_walk` duration
23 * stream-clone: disable gc for the initial section for the v3 format
24 * postincoming: avoid computing branchhead if no report will be posted
25 * stream-clone: disable gc for the entry listing section for the v2 format
26 * perf: allow profiling of more than one run
27 * perf: run the gc before each run
28 * perf: start recording total time after warming
29 * perf: clear vfs audit_cache before each run
30 * outgoing: rework the handling of the `missingroots` case to be faster
31 * outgoing: add a simple fastpath when there is no common
32 * tags-cache: skip the filternode step if we are not going to use it
33 * tags-cache: directly operate on rev-num warming hgtagsfnodescache
34 * tags-cache: directly perform a monimal walk for hgtagsfnodescache warming
35 * exchange: improve computation of relevant markers for large repos
36
37
38 == New Experimental Features ==
39
40 * Introduce a new experimental branch cache "v3":
41 * branchcache: add more test for the logic around obsolescence and branch heads
42 * branchcache: skip entries that are topological heads in the on disk file
43 * branchcache: add a "pure topological head" fast path
44 * branchcache: allow to detect "pure topological case" for branchmap
45
46
47 == Bug Fixes ==
48
49 * perf-stream-locked-section: actually use v1 generation when requested
50 * perf-stream-locked-section: fix the call to the v3 generator
51 * perf-stream-locked-section: advertise the right version key in the help
52 * stream: in v3, skip the "size" fast path if the entries have some unknown size
53 * stream-clone: stop getting the file size of all file in v3
54 * streamclone: stop listing files for entries that have no volatile files
55 * perf-stream-consume: use the source repository config when applying
56 * bundle: do no check the changegroup version if no changegroup is included
57 * perf: create the temporary target next to the source in stream-consume
58 * bundlespec: fix the "streamv2" and "streamv3-exp" variant
59 * push: rework the computation of fallbackheads to be correct
60 * profiler: flush after writing the profiler output
61 * base-revsets: use an author that actually exercises a lot of changesets
62 * hgrc: search XDG_CONFIG_HOME on mac
63 * clonebundles: add missing newline to legacy response
64 * narrow: add a test for linkrev computation done during widen
65
66 == Backwards Compatibility Changes ==
67
68 == Internal API Changes ==
69
70 == Miscellaneous ==
71
72 * obsolete: quote the feature name No newline at end of file
This diff has been collapsed as it changes many lines, (563 lines changed) Show them Hide them
@@ -0,0 +1,563 b''
1 ================================================================
2 test the interaction of the branch cache with obsolete changeset
3 ================================================================
4
5 Some corner case have been covered by unrelated test (like rebase ones) this
6 file meant to gather explicite testing of those.
7
8 See also: test-obsolete-checkheads.t
9
10 #testcases v2 v3
11
12 $ cat >> $HGRCPATH << EOF
13 > [phases]
14 > publish = false
15 > [experimental]
16 > evolution = all
17 > server.allow-hidden-access = *
18 > EOF
19
20 #if v3
21 $ cat <<EOF >> $HGRCPATH
22 > [experimental]
23 > branch-cache-v3=yes
24 > EOF
25 $ CACHE_PREFIX=branch3-exp
26 #else
27 $ cat <<EOF >> $HGRCPATH
28 > [experimental]
29 > branch-cache-v3=no
30 > EOF
31 $ CACHE_PREFIX=branch2
32 #endif
33
34 $ show_cache() {
35 > for cache_file in .hg/cache/$CACHE_PREFIX*; do
36 > echo "##### $cache_file"
37 > cat $cache_file
38 > done
39 > }
40
41 Setup graph
42 #############
43
44 $ . $RUNTESTDIR/testlib/common.sh
45
46 graph with a single branch
47 --------------------------
48
49 We want some branching and some obsolescence
50
51 $ hg init main-single-branch
52 $ cd main-single-branch
53 $ mkcommit root
54 $ mkcommit A_1
55 $ mkcommit A_2
56 $ hg update 'desc("A_2")' --quiet
57 $ mkcommit B_1
58 $ mkcommit B_2
59 $ mkcommit B_3
60 $ mkcommit B_4
61 $ hg update 'desc("A_2")' --quiet
62 $ mkcommit A_3
63 created new head
64 $ mkcommit A_4
65 $ hg up null --quiet
66 $ hg clone --noupdate . ../main-single-branch-pre-ops
67 $ hg log -r 'desc("A_1")' -T '{node}' > ../main-single-branch-node_A1
68 $ hg log -r 'desc("A_2")' -T '{node}' > ../main-single-branch-node_A2
69 $ hg log -r 'desc("A_3")' -T '{node}' > ../main-single-branch-node_A3
70 $ hg log -r 'desc("A_4")' -T '{node}' > ../main-single-branch-node_A4
71 $ hg log -r 'desc("B_1")' -T '{node}' > ../main-single-branch-node_B1
72 $ hg log -r 'desc("B_2")' -T '{node}' > ../main-single-branch-node_B2
73 $ hg log -r 'desc("B_3")' -T '{node}' > ../main-single-branch-node_B3
74 $ hg log -r 'desc("B_4")' -T '{node}' > ../main-single-branch-node_B4
75
76 (double check the heads are right before we obsolete)
77
78 $ hg log -R ../main-single-branch-pre-ops -G -T '{desc}\n'
79 o A_4
80 |
81 o A_3
82 |
83 | o B_4
84 | |
85 | o B_3
86 | |
87 | o B_2
88 | |
89 | o B_1
90 |/
91 o A_2
92 |
93 o A_1
94 |
95 o root
96
97 $ hg log -G -T '{desc}\n'
98 o A_4
99 |
100 o A_3
101 |
102 | o B_4
103 | |
104 | o B_3
105 | |
106 | o B_2
107 | |
108 | o B_1
109 |/
110 o A_2
111 |
112 o A_1
113 |
114 o root
115
116
117 #if v2
118 $ show_cache
119 ##### .hg/cache/branch2-served
120 3d808bbc94408ea19da905596d4079357a1f28be 8
121 63ba7cd843d1e95aac1a24435befeb1909c53619 o default
122 3d808bbc94408ea19da905596d4079357a1f28be o default
123 #else
124 $ show_cache
125 ##### .hg/cache/branch3-exp-served
126 tip-node=3d808bbc94408ea19da905596d4079357a1f28be tip-rev=8 topo-mode=pure
127 default
128 #endif
129 $ hg log -T '{desc}\n' --rev 'head()'
130 B_4
131 A_4
132
133 Absolete a couple of changes
134
135 $ for d in B2 B3 B4 A4; do
136 > hg debugobsolete --record-parents `cat ../main-single-branch-node_$d`;
137 > done
138 1 new obsolescence markers
139 obsoleted 1 changesets
140 2 new orphan changesets
141 1 new obsolescence markers
142 obsoleted 1 changesets
143 1 new obsolescence markers
144 obsoleted 1 changesets
145 1 new obsolescence markers
146 obsoleted 1 changesets
147
148 (double check the result is okay)
149
150 $ hg log -G -T '{desc}\n'
151 o A_3
152 |
153 | o B_1
154 |/
155 o A_2
156 |
157 o A_1
158 |
159 o root
160
161 $ hg heads -T '{desc}\n'
162 A_3
163 B_1
164 #if v2
165 $ show_cache
166 ##### .hg/cache/branch2-served
167 7c29ff2453bf38c75ee8982935739103c38a9284 7 f8006d64a10d35c011a5c5fa88be1e25c5929514
168 550bb31f072912453ccbb503de1d554616911e88 o default
169 7c29ff2453bf38c75ee8982935739103c38a9284 o default
170 #else
171 $ show_cache
172 ##### .hg/cache/branch3-exp-served
173 filtered-hash=f8006d64a10d35c011a5c5fa88be1e25c5929514 tip-node=7c29ff2453bf38c75ee8982935739103c38a9284 tip-rev=7 topo-mode=pure
174 default
175 #endif
176 $ cd ..
177
178
179 Actual testing
180 ##############
181
182 Revealing obsolete changeset
183 ----------------------------
184
185 Check that revealing obsolete changesets does not confuse branch computation and checks
186
187 Revealing tipmost changeset
188 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
189
190
191 $ cp -R ./main-single-branch tmp-repo
192 $ cd tmp-repo
193 $ hg update --hidden --rev 'desc("A_4")' --quiet
194 updated to hidden changeset 3d808bbc9440
195 (hidden revision '3d808bbc9440' is pruned)
196 $ hg log -G -T '{desc}\n'
197 @ A_4
198 |
199 o A_3
200 |
201 | o B_1
202 |/
203 o A_2
204 |
205 o A_1
206 |
207 o root
208
209 $ hg heads -T '{desc}\n'
210 A_3
211 B_1
212 #if v2
213 $ show_cache
214 ##### .hg/cache/branch2
215 3d808bbc94408ea19da905596d4079357a1f28be 8 a943c3355ad9e93654d58b1c934c7c4329a5d1d4
216 550bb31f072912453ccbb503de1d554616911e88 o default
217 7c29ff2453bf38c75ee8982935739103c38a9284 o default
218 ##### .hg/cache/branch2-served
219 3d808bbc94408ea19da905596d4079357a1f28be 8 a943c3355ad9e93654d58b1c934c7c4329a5d1d4
220 550bb31f072912453ccbb503de1d554616911e88 o default
221 7c29ff2453bf38c75ee8982935739103c38a9284 o default
222 #else
223 $ show_cache
224 ##### .hg/cache/branch3-exp
225 obsolete-hash=b6d2b1f5b70f09c25c835edcae69be35f681605c tip-node=3d808bbc94408ea19da905596d4079357a1f28be tip-rev=8
226 7c29ff2453bf38c75ee8982935739103c38a9284 o default
227 ##### .hg/cache/branch3-exp-served
228 filtered-hash=f8006d64a10d35c011a5c5fa88be1e25c5929514 obsolete-hash=ac5282439f301518f362f37547fcd52bcc670373 tip-node=3d808bbc94408ea19da905596d4079357a1f28be tip-rev=8
229 7c29ff2453bf38c75ee8982935739103c38a9284 o default
230 #endif
231
232 Even when computing branches from scratch
233
234 $ rm -rf .hg/cache/branch*
235 $ rm -rf .hg/wcache/branch*
236 $ hg heads -T '{desc}\n'
237 A_3
238 B_1
239 #if v2
240 $ show_cache
241 ##### .hg/cache/branch2-served
242 3d808bbc94408ea19da905596d4079357a1f28be 8 a943c3355ad9e93654d58b1c934c7c4329a5d1d4
243 550bb31f072912453ccbb503de1d554616911e88 o default
244 7c29ff2453bf38c75ee8982935739103c38a9284 o default
245 #else
246 $ show_cache
247 ##### .hg/cache/branch3-exp-served
248 filtered-hash=f8006d64a10d35c011a5c5fa88be1e25c5929514 obsolete-hash=ac5282439f301518f362f37547fcd52bcc670373 tip-node=3d808bbc94408ea19da905596d4079357a1f28be tip-rev=8
249 7c29ff2453bf38c75ee8982935739103c38a9284 o default
250 #endif
251
252 And we can get back to normal
253
254 $ hg update null --quiet
255 $ hg heads -T '{desc}\n'
256 A_3
257 B_1
258 #if v2
259 $ show_cache
260 ##### .hg/cache/branch2-served
261 7c29ff2453bf38c75ee8982935739103c38a9284 7 f8006d64a10d35c011a5c5fa88be1e25c5929514
262 550bb31f072912453ccbb503de1d554616911e88 o default
263 7c29ff2453bf38c75ee8982935739103c38a9284 o default
264 #else
265 $ show_cache
266 ##### .hg/cache/branch3-exp-served
267 filtered-hash=f8006d64a10d35c011a5c5fa88be1e25c5929514 tip-node=7c29ff2453bf38c75ee8982935739103c38a9284 tip-rev=7 topo-mode=pure
268 default
269 #endif
270
271 $ cd ..
272 $ rm -rf tmp-repo
273
274 Revealing changeset in the middle of the changelog
275 ~~~~~~~~~~~~~~~~~~~~~~~~~~~------------------------
276
277 Check that revealing an obsolete changeset does not confuse branch computation and checks
278
279 $ cp -R ./main-single-branch tmp-repo
280 $ cd tmp-repo
281 $ hg update --hidden --rev 'desc("B_3")' --quiet
282 updated to hidden changeset 9c996d7674bb
283 (hidden revision '9c996d7674bb' is pruned)
284 $ hg log -G -T '{desc}\n'
285 o A_3
286 |
287 | @ B_3
288 | |
289 | x B_2
290 | |
291 | o B_1
292 |/
293 o A_2
294 |
295 o A_1
296 |
297 o root
298
299 $ hg heads -T '{desc}\n'
300 A_3
301 B_1
302 #if v2
303 $ show_cache
304 ##### .hg/cache/branch2
305 3d808bbc94408ea19da905596d4079357a1f28be 8 a943c3355ad9e93654d58b1c934c7c4329a5d1d4
306 550bb31f072912453ccbb503de1d554616911e88 o default
307 7c29ff2453bf38c75ee8982935739103c38a9284 o default
308 ##### .hg/cache/branch2-served
309 7c29ff2453bf38c75ee8982935739103c38a9284 7 f8006d64a10d35c011a5c5fa88be1e25c5929514
310 550bb31f072912453ccbb503de1d554616911e88 o default
311 7c29ff2453bf38c75ee8982935739103c38a9284 o default
312 #else
313 $ show_cache
314 ##### .hg/cache/branch3-exp
315 obsolete-hash=b6d2b1f5b70f09c25c835edcae69be35f681605c tip-node=3d808bbc94408ea19da905596d4079357a1f28be tip-rev=8
316 7c29ff2453bf38c75ee8982935739103c38a9284 o default
317 ##### .hg/cache/branch3-exp-served
318 filtered-hash=f1456c0d675980582dda9b8edc7f13f503ce544f obsolete-hash=3e74f5349008671629e39d13d7e00d9ba94c74f7 tip-node=7c29ff2453bf38c75ee8982935739103c38a9284 tip-rev=7
319 550bb31f072912453ccbb503de1d554616911e88 o default
320 #endif
321
322 Even when computing branches from scratch
323
324 $ rm -rf .hg/cache/branch*
325 $ rm -rf .hg/wcache/branch*
326 $ hg heads -T '{desc}\n'
327 A_3
328 B_1
329 #if v2
330 $ show_cache
331 ##### .hg/cache/branch2-served
332 7c29ff2453bf38c75ee8982935739103c38a9284 7 f8006d64a10d35c011a5c5fa88be1e25c5929514
333 550bb31f072912453ccbb503de1d554616911e88 o default
334 7c29ff2453bf38c75ee8982935739103c38a9284 o default
335 #else
336 $ show_cache
337 ##### .hg/cache/branch3-exp-served
338 filtered-hash=f1456c0d675980582dda9b8edc7f13f503ce544f obsolete-hash=3e74f5349008671629e39d13d7e00d9ba94c74f7 tip-node=7c29ff2453bf38c75ee8982935739103c38a9284 tip-rev=7
339 550bb31f072912453ccbb503de1d554616911e88 o default
340 #endif
341
342 And we can get back to normal
343
344 $ hg update null --quiet
345 $ hg heads -T '{desc}\n'
346 A_3
347 B_1
348 #if v2
349 $ show_cache
350 ##### .hg/cache/branch2-served
351 7c29ff2453bf38c75ee8982935739103c38a9284 7 f8006d64a10d35c011a5c5fa88be1e25c5929514
352 550bb31f072912453ccbb503de1d554616911e88 o default
353 7c29ff2453bf38c75ee8982935739103c38a9284 o default
354 #else
355 $ show_cache
356 ##### .hg/cache/branch3-exp-served
357 filtered-hash=f8006d64a10d35c011a5c5fa88be1e25c5929514 tip-node=7c29ff2453bf38c75ee8982935739103c38a9284 tip-rev=7 topo-mode=pure
358 default
359 #endif
360
361 $ cd ..
362 $ rm -rf tmp-repo
363
364 Getting the obsolescence marker after the fact for the tip rev
365 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
366
367 $ cp -R ./main-single-branch-pre-ops tmp-repo
368 $ cd tmp-repo
369 $ hg update --hidden --rev 'desc("A_4")' --quiet
370 $ hg log -G -T '{desc}\n'
371 @ A_4
372 |
373 o A_3
374 |
375 | o B_4
376 | |
377 | o B_3
378 | |
379 | o B_2
380 | |
381 | o B_1
382 |/
383 o A_2
384 |
385 o A_1
386 |
387 o root
388
389 $ hg heads -T '{desc}\n'
390 A_4
391 B_4
392 $ hg pull --rev `cat ../main-single-branch-node_A4` --remote-hidden
393 pulling from $TESTTMP/main-single-branch
394 no changes found
395 1 new obsolescence markers
396 obsoleted 1 changesets
397
398 branch head are okay
399
400 $ hg heads -T '{desc}\n'
401 A_3
402 B_4
403 #if v2
404 $ show_cache
405 ##### .hg/cache/branch2-served
406 3d808bbc94408ea19da905596d4079357a1f28be 8 ac5282439f301518f362f37547fcd52bcc670373
407 63ba7cd843d1e95aac1a24435befeb1909c53619 o default
408 7c29ff2453bf38c75ee8982935739103c38a9284 o default
409 #else
410 $ show_cache
411 ##### .hg/cache/branch3-exp-served
412 obsolete-hash=ac5282439f301518f362f37547fcd52bcc670373 tip-node=3d808bbc94408ea19da905596d4079357a1f28be tip-rev=8
413 7c29ff2453bf38c75ee8982935739103c38a9284 o default
414 #endif
415
416 Even when computing branches from scratch
417
418 $ rm -rf .hg/cache/branch*
419 $ rm -rf .hg/wcache/branch*
420 $ hg heads -T '{desc}\n'
421 A_3
422 B_4
423 #if v2
424 $ show_cache
425 ##### .hg/cache/branch2-served
426 3d808bbc94408ea19da905596d4079357a1f28be 8 ac5282439f301518f362f37547fcd52bcc670373
427 63ba7cd843d1e95aac1a24435befeb1909c53619 o default
428 7c29ff2453bf38c75ee8982935739103c38a9284 o default
429 #else
430 $ show_cache
431 ##### .hg/cache/branch3-exp-served
432 obsolete-hash=ac5282439f301518f362f37547fcd52bcc670373 tip-node=3d808bbc94408ea19da905596d4079357a1f28be tip-rev=8
433 7c29ff2453bf38c75ee8982935739103c38a9284 o default
434 #endif
435
436 And we can get back to normal
437
438 $ hg update null --quiet
439 $ hg heads -T '{desc}\n'
440 A_3
441 B_4
442 #if v2
443 $ show_cache
444 ##### .hg/cache/branch2-served
445 7c29ff2453bf38c75ee8982935739103c38a9284 7
446 63ba7cd843d1e95aac1a24435befeb1909c53619 o default
447 7c29ff2453bf38c75ee8982935739103c38a9284 o default
448 #else
449 $ show_cache
450 ##### .hg/cache/branch3-exp-served
451 tip-node=7c29ff2453bf38c75ee8982935739103c38a9284 tip-rev=7 topo-mode=pure
452 default
453 #endif
454
455 $ cd ..
456 $ rm -rf tmp-repo
457
458 Getting the obsolescence marker after the fact for another rev
459 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
460
461 $ cp -R ./main-single-branch-pre-ops tmp-repo
462 $ cd tmp-repo
463 $ hg update --hidden --rev 'desc("B_3")' --quiet
464 $ hg log -G -T '{desc}\n'
465 o A_4
466 |
467 o A_3
468 |
469 | o B_4
470 | |
471 | @ B_3
472 | |
473 | o B_2
474 | |
475 | o B_1
476 |/
477 o A_2
478 |
479 o A_1
480 |
481 o root
482
483 $ hg heads -T '{desc}\n'
484 A_4
485 B_4
486 #if v2
487 $ show_cache
488 ##### .hg/cache/branch2-served
489 3d808bbc94408ea19da905596d4079357a1f28be 8
490 63ba7cd843d1e95aac1a24435befeb1909c53619 o default
491 3d808bbc94408ea19da905596d4079357a1f28be o default
492 #else
493 $ show_cache
494 ##### .hg/cache/branch3-exp-served
495 tip-node=3d808bbc94408ea19da905596d4079357a1f28be tip-rev=8 topo-mode=pure
496 default
497 #endif
498
499 $ hg pull --rev `cat ../main-single-branch-node_B4` --remote-hidden
500 pulling from $TESTTMP/main-single-branch
501 no changes found
502 3 new obsolescence markers
503 obsoleted 3 changesets
504
505 branch head are okay
506
507 $ hg heads -T '{desc}\n'
508 A_4
509 B_1
510 #if v2
511 $ show_cache
512 ##### .hg/cache/branch2-served
513 3d808bbc94408ea19da905596d4079357a1f28be 8 f8006d64a10d35c011a5c5fa88be1e25c5929514
514 550bb31f072912453ccbb503de1d554616911e88 o default
515 3d808bbc94408ea19da905596d4079357a1f28be o default
516 #else
517 $ show_cache
518 ##### .hg/cache/branch3-exp-served
519 filtered-hash=f1456c0d675980582dda9b8edc7f13f503ce544f obsolete-hash=3e74f5349008671629e39d13d7e00d9ba94c74f7 tip-node=3d808bbc94408ea19da905596d4079357a1f28be tip-rev=8
520 550bb31f072912453ccbb503de1d554616911e88 o default
521 #endif
522
523 Even when computing branches from scratch
524
525 $ rm -rf .hg/cache/branch*
526 $ rm -rf .hg/wcache/branch*
527 $ hg heads -T '{desc}\n'
528 A_4
529 B_1
530 #if v2
531 $ show_cache
532 ##### .hg/cache/branch2-served
533 3d808bbc94408ea19da905596d4079357a1f28be 8 f8006d64a10d35c011a5c5fa88be1e25c5929514
534 550bb31f072912453ccbb503de1d554616911e88 o default
535 3d808bbc94408ea19da905596d4079357a1f28be o default
536 #else
537 $ show_cache
538 ##### .hg/cache/branch3-exp-served
539 filtered-hash=f1456c0d675980582dda9b8edc7f13f503ce544f obsolete-hash=3e74f5349008671629e39d13d7e00d9ba94c74f7 tip-node=3d808bbc94408ea19da905596d4079357a1f28be tip-rev=8
540 550bb31f072912453ccbb503de1d554616911e88 o default
541 #endif
542
543 And we can get back to normal
544
545 $ hg update null --quiet
546 $ hg heads -T '{desc}\n'
547 A_4
548 B_1
549 #if v2
550 $ show_cache
551 ##### .hg/cache/branch2-served
552 3d808bbc94408ea19da905596d4079357a1f28be 8 f8006d64a10d35c011a5c5fa88be1e25c5929514
553 550bb31f072912453ccbb503de1d554616911e88 o default
554 3d808bbc94408ea19da905596d4079357a1f28be o default
555 #else
556 $ show_cache
557 ##### .hg/cache/branch3-exp-served
558 filtered-hash=f8006d64a10d35c011a5c5fa88be1e25c5929514 tip-node=3d808bbc94408ea19da905596d4079357a1f28be tip-rev=8 topo-mode=pure
559 default
560 #endif
561
562 $ cd ..
563 $ rm -rf tmp-repo
This diff has been collapsed as it changes many lines, (627 lines changed) Show them Hide them
@@ -0,0 +1,627 b''
1 ==============================================================================================
2 Test the computation of linkrev that is needed when sending file content after their changeset
3 ==============================================================================================
4
5 Setup
6 =====
7
8 tree/flat make the hash unstable had are anoying, reinstall that later.
9 .. #testcases tree flat
10 $ . "$TESTDIR/narrow-library.sh"
11
12 .. #if tree
13 .. $ cat << EOF >> $HGRCPATH
14 .. > [experimental]
15 .. > treemanifest = 1
16 .. > EOF
17 .. #endif
18
19 $ hg init server
20 $ cd server
21
22 We build a non linear history with some filenome that exist in parallel.
23
24 $ echo foo > readme.txt
25 $ hg add readme.txt
26 $ hg ci -m 'root'
27 $ mkdir dir_x
28 $ echo foo > dir_x/f1
29 $ echo fo0 > dir_x/f2
30 $ echo f0o > dir_x/f3
31 $ mkdir dir_y
32 $ echo bar > dir_y/f1
33 $ echo 8ar > dir_y/f2
34 $ echo ba9 > dir_y/f3
35 $ hg add dir_x dir_y
36 adding dir_x/f1
37 adding dir_x/f2
38 adding dir_x/f3
39 adding dir_y/f1
40 adding dir_y/f2
41 adding dir_y/f3
42 $ hg ci -m 'rev_a_'
43
44 $ hg update 'desc("rev_a_")'
45 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 $ echo foo-01 > dir_x/f1
47 $ hg ci -m 'rev_b_0_'
48
49 $ hg update 'desc("rev_b_0_")'
50 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
51 $ echo foo-02 > dir_x/f1
52 $ hg ci -m 'rev_b_1_'
53
54 $ hg update 'desc("rev_a_")'
55 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
56 $ mkdir dir_z
57 $ echo bar-01 > dir_y/f1
58 $ echo 8ar-01 > dir_y/f2
59 $ echo babar > dir_z/f1
60 $ hg add dir_z
61 adding dir_z/f1
62 $ hg ci -m 'rev_c_0_'
63 created new head
64
65 $ hg update 'desc("rev_c_0_")'
66 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
67 $ echo celeste > dir_z/f2
68 $ echo zephir > dir_z/f1
69 $ hg add dir_z
70 adding dir_z/f2
71 $ hg ci -m 'rev_c_1_'
72
73 $ hg update 'desc("rev_b_1_")'
74 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
75 $ echo fo0-01 > dir_x/f2
76 $ mkdir dir_z
77 $ ls dir_z
78 $ echo babar > dir_z/f1
79 $ echo celeste > dir_z/f2
80 $ echo foo > dir_z/f3
81 $ hg add dir_z
82 adding dir_z/f1
83 adding dir_z/f2
84 adding dir_z/f3
85 $ hg ci -m 'rev_b_2_'
86
87 $ hg update 'desc("rev_b_2_")'
88 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
89 $ echo f0o-01 > dir_x/f3
90 $ echo zephir > dir_z/f1
91 $ echo arthur > dir_z/f2
92 $ hg ci -m 'rev_b_3_'
93
94 $ hg update 'desc("rev_c_1_")'
95 6 files updated, 0 files merged, 1 files removed, 0 files unresolved
96 $ echo bar-02 > dir_y/f1
97 $ echo ba9-01 > dir_y/f3
98 $ echo bar > dir_z/f4
99 $ hg add dir_z/
100 adding dir_z/f4
101 $ echo arthur > dir_z/f2
102 $ hg ci -m 'rev_c_2_'
103
104 $ hg update 'desc("rev_b_3_")'
105 7 files updated, 0 files merged, 1 files removed, 0 files unresolved
106 $ hg merge 'desc("rev_c_2_")'
107 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
108 (branch merge, don't forget to commit)
109 $ echo flore > dir_z/f1
110 $ echo foo-04 > dir_x/f1
111 $ echo foo-01 > dir_z/f3
112 $ hg ci -m 'rev_d_0_'
113 $ echo alexandre > dir_z/f1
114 $ echo bar-01 > dir_z/f4
115 $ echo bar-04 > dir_y/f1
116 $ hg ci -m 'rev_d_1_'
117 $ hg status
118 $ hg status -A
119 C dir_x/f1
120 C dir_x/f2
121 C dir_x/f3
122 C dir_y/f1
123 C dir_y/f2
124 C dir_y/f3
125 C dir_z/f1
126 C dir_z/f2
127 C dir_z/f3
128 C dir_z/f4
129 C readme.txt
130 $ hg up null
131 0 files updated, 0 files merged, 11 files removed, 0 files unresolved
132
133 Resulting graph
134
135 $ hg log -GT "{rev}:{node|short}: {desc}\n {files}\n"
136 o 10:71e6a9c7a6a2: rev_d_1_
137 | dir_y/f1 dir_z/f1 dir_z/f4
138 o 9:b0a0cbe5ce57: rev_d_0_
139 |\ dir_x/f1 dir_z/f1 dir_z/f3
140 | o 8:d04e01dcc82d: rev_c_2_
141 | | dir_y/f1 dir_y/f3 dir_z/f2 dir_z/f4
142 o | 7:fc05b303b551: rev_b_3_
143 | | dir_x/f3 dir_z/f1 dir_z/f2
144 o | 6:17fd34adb43b: rev_b_2_
145 | | dir_x/f2 dir_z/f1 dir_z/f2 dir_z/f3
146 | o 5:fa05dbe8eed1: rev_c_1_
147 | | dir_z/f1 dir_z/f2
148 | o 4:59b4258b00dc: rev_c_0_
149 | | dir_y/f1 dir_y/f2 dir_z/f1
150 o | 3:328f8ced5276: rev_b_1_
151 | | dir_x/f1
152 o | 2:0ccce83dd29b: rev_b_0_
153 |/ dir_x/f1
154 o 1:63f468a0fdac: rev_a_
155 | dir_x/f1 dir_x/f2 dir_x/f3 dir_y/f1 dir_y/f2 dir_y/f3
156 o 0:4978c5c7386b: root
157 readme.txt
158
159 Useful save useful nodes :
160
161 $ hg log -T '{node}' > ../rev_c_2_ --rev 'desc("rev_c_2_")'
162 $ hg log -T '{node}' > ../rev_b_3_ --rev 'desc("rev_b_3_")'
163
164 Reference output
165
166 Since we have the same file conent on each side, we should get a limited number
167 of file revision (and the associated linkrev).
168
169 This these shared file-revision and the associated linkrev computation is
170 fueling the complexity test in this file.
171
172 $ cat > ../linkrev-check.sh << EOF
173 > echo '# expected linkrev for dir_z/f1'
174 > hg log -T '0 {rev}\n' --rev 'min(desc(rev_b_2_) or desc(rev_c_0_))'
175 > hg log -T '1 {rev}\n' --rev 'min(desc(rev_b_3_) or desc(rev_c_1_))'
176 > hg log -T '2 {rev}\n' --rev 'min(desc(rev_d_0_))'
177 > hg log -T '3 {rev}\n' --rev 'min(desc(rev_d_1_))'
178 > hg debugindex dir_z/f1
179 > # rev linkrev nodeid p1-nodeid p2-nodeid
180 > # 0 4 360afd990eef 000000000000 000000000000
181 > # 1 5 7054ee088631 360afd990eef 000000000000
182 > # 2 9 6bb290463f21 7054ee088631 000000000000
183 > # 3 10 91fec784ff86 6bb290463f21 000000000000
184 > echo '# expected linkrev for dir_z/f2'
185 > hg log -T '0 {rev}\n' --rev 'min(desc(rev_c_1_) or desc(rev_b_2_))'
186 > hg log -T '1 {rev}\n' --rev 'min(desc(rev_c_2_) or desc(rev_b_3_))'
187 > hg debugindex dir_z/f2
188 > # rev linkrev nodeid p1-nodeid p2-nodeid
189 > # 0 5 093bb0f8a0fb 000000000000 000000000000
190 > # 1 7 0f47e254cb19 093bb0f8a0fb 000000000000
191 > if hg files --rev tip | grep dir_z/f3 > /dev/null; then
192 > echo '# expected linkrev for dir_z/f3'
193 > hg log -T '0 {rev}\n' --rev 'desc(rev_b_2_)'
194 > hg log -T '1 {rev}\n' --rev 'desc(rev_d_0_)'
195 > hg debugindex dir_z/f3
196 > # rev linkrev nodeid p1-nodeid p2-nodeid
197 > # 0 6 2ed2a3912a0b 000000000000 000000000000
198 > # 1 9 7c6d649320ae 2ed2a3912a0b 000000000000
199 > fi
200 > if hg files --rev tip | grep dir_z/f4 > /dev/null; then
201 > echo '# expected linkrev for dir_z/f4'
202 > hg log -T '0 {rev}\n' --rev 'desc(rev_c_2_)'
203 > hg log -T '1 {rev}\n' --rev 'desc(rev_d_1_)'
204 > hg debugindex dir_z/f4
205 > # rev linkrev nodeid p1-nodeid p2-nodeid
206 > # 0 8 b004912a8510 000000000000 000000000000
207 > # 1 10 9f85b3b95e70 b004912a8510 000000000000
208 > fi
209 > echo '# verify the repository'
210 > hg verify
211 > EOF
212 $ sh ../linkrev-check.sh
213 # expected linkrev for dir_z/f1
214 0 4
215 1 5
216 2 9
217 3 10
218 rev linkrev nodeid p1-nodeid p2-nodeid
219 0 4 360afd990eef 000000000000 000000000000
220 1 5 7054ee088631 360afd990eef 000000000000
221 2 9 6bb290463f21 7054ee088631 000000000000
222 3 10 91fec784ff86 6bb290463f21 000000000000
223 # expected linkrev for dir_z/f2
224 0 5
225 1 7
226 rev linkrev nodeid p1-nodeid p2-nodeid
227 0 5 093bb0f8a0fb 000000000000 000000000000
228 1 7 0f47e254cb19 093bb0f8a0fb 000000000000
229 # expected linkrev for dir_z/f3
230 0 6
231 1 9
232 rev linkrev nodeid p1-nodeid p2-nodeid
233 0 6 2ed2a3912a0b 000000000000 000000000000
234 1 9 7c6d649320ae 2ed2a3912a0b 000000000000
235 # expected linkrev for dir_z/f4
236 0 8
237 1 10
238 rev linkrev nodeid p1-nodeid p2-nodeid
239 0 8 b004912a8510 000000000000 000000000000
240 1 10 9f85b3b95e70 b004912a8510 000000000000
241 # verify the repository
242 checking changesets
243 checking manifests
244 crosschecking files in changesets and manifests
245 checking files
246 checking dirstate
247 checked 11 changesets with 27 changes to 11 files
248
249 $ cd ..
250
251 Test linkrev computation for various widening scenario
252 ======================================================
253
254 Having cloning all revisions initially
255 --------------------------------------
256
257 $ hg clone --narrow ssh://user@dummy/server --include dir_x --include dir_y client_xy_rev_all --noupdate
258 requesting all changes
259 adding changesets
260 adding manifests
261 adding file changes
262 added 11 changesets with 16 changes to 6 files
263 new changesets 4978c5c7386b:71e6a9c7a6a2
264 $ cd client_xy_rev_all
265 $ hg log -GT "{rev}:{node|short}: {desc}\n {files}\n"
266 o 10:71e6a9c7a6a2: rev_d_1_
267 | dir_y/f1 dir_z/f1 dir_z/f4
268 o 9:b0a0cbe5ce57: rev_d_0_
269 |\ dir_x/f1 dir_z/f1 dir_z/f3
270 | o 8:d04e01dcc82d: rev_c_2_
271 | | dir_y/f1 dir_y/f3 dir_z/f2 dir_z/f4
272 o | 7:fc05b303b551: rev_b_3_
273 | | dir_x/f3 dir_z/f1 dir_z/f2
274 o | 6:17fd34adb43b: rev_b_2_
275 | | dir_x/f2 dir_z/f1 dir_z/f2 dir_z/f3
276 | o 5:fa05dbe8eed1: rev_c_1_
277 | | dir_z/f1 dir_z/f2
278 | o 4:59b4258b00dc: rev_c_0_
279 | | dir_y/f1 dir_y/f2 dir_z/f1
280 o | 3:328f8ced5276: rev_b_1_
281 | | dir_x/f1
282 o | 2:0ccce83dd29b: rev_b_0_
283 |/ dir_x/f1
284 o 1:63f468a0fdac: rev_a_
285 | dir_x/f1 dir_x/f2 dir_x/f3 dir_y/f1 dir_y/f2 dir_y/f3
286 o 0:4978c5c7386b: root
287 readme.txt
288
289 $ hg tracked --addinclude dir_z
290 comparing with ssh://user@dummy/server
291 searching for changes
292 adding changesets
293 adding manifests
294 adding file changes
295 added 0 changesets with 10 changes to 4 files
296 $ sh ../linkrev-check.sh
297 # expected linkrev for dir_z/f1
298 0 4
299 1 5
300 2 9
301 3 10
302 rev linkrev nodeid p1-nodeid p2-nodeid
303 0 4 360afd990eef 000000000000 000000000000
304 1 5 7054ee088631 360afd990eef 000000000000
305 2 9 6bb290463f21 7054ee088631 000000000000
306 3 10 91fec784ff86 6bb290463f21 000000000000
307 # expected linkrev for dir_z/f2
308 0 5
309 1 7
310 rev linkrev nodeid p1-nodeid p2-nodeid
311 0 5 093bb0f8a0fb 000000000000 000000000000
312 1 7 0f47e254cb19 093bb0f8a0fb 000000000000
313 # expected linkrev for dir_z/f3
314 0 6
315 1 9
316 rev linkrev nodeid p1-nodeid p2-nodeid
317 0 6 2ed2a3912a0b 000000000000 000000000000
318 1 9 7c6d649320ae 2ed2a3912a0b 000000000000
319 # expected linkrev for dir_z/f4
320 0 8
321 1 10
322 rev linkrev nodeid p1-nodeid p2-nodeid
323 0 8 b004912a8510 000000000000 000000000000
324 1 10 9f85b3b95e70 b004912a8510 000000000000
325 # verify the repository
326 checking changesets
327 checking manifests
328 crosschecking files in changesets and manifests
329 checking files
330 checking dirstate
331 checked 11 changesets with 26 changes to 10 files
332 $ cd ..
333
334
335 Having cloning all only branch b
336 --------------------------------
337
338 $ hg clone --narrow ssh://user@dummy/server --rev `cat ./rev_b_3_` --include dir_x --include dir_y client_xy_rev_from_b_only --noupdate
339 adding changesets
340 adding manifests
341 adding file changes
342 added 6 changesets with 10 changes to 6 files
343 new changesets 4978c5c7386b:fc05b303b551
344 $ cd client_xy_rev_from_b_only
345 $ hg log -GT "{rev}:{node|short}: {desc}\n {files}\n"
346 o 5:fc05b303b551: rev_b_3_
347 | dir_x/f3 dir_z/f1 dir_z/f2
348 o 4:17fd34adb43b: rev_b_2_
349 | dir_x/f2 dir_z/f1 dir_z/f2 dir_z/f3
350 o 3:328f8ced5276: rev_b_1_
351 | dir_x/f1
352 o 2:0ccce83dd29b: rev_b_0_
353 | dir_x/f1
354 o 1:63f468a0fdac: rev_a_
355 | dir_x/f1 dir_x/f2 dir_x/f3 dir_y/f1 dir_y/f2 dir_y/f3
356 o 0:4978c5c7386b: root
357 readme.txt
358
359 $ hg tracked --addinclude dir_z
360 comparing with ssh://user@dummy/server
361 searching for changes
362 adding changesets
363 adding manifests
364 adding file changes
365 added 0 changesets with 5 changes to 3 files
366 $ sh ../linkrev-check.sh
367 # expected linkrev for dir_z/f1
368 0 4
369 1 5
370 rev linkrev nodeid p1-nodeid p2-nodeid
371 0 4 360afd990eef 000000000000 000000000000
372 1 5 7054ee088631 360afd990eef 000000000000
373 # expected linkrev for dir_z/f2
374 0 4
375 1 5
376 rev linkrev nodeid p1-nodeid p2-nodeid
377 0 4 093bb0f8a0fb 000000000000 000000000000
378 1 5 0f47e254cb19 093bb0f8a0fb 000000000000
379 # expected linkrev for dir_z/f3
380 0 4
381 rev linkrev nodeid p1-nodeid p2-nodeid
382 0 4 2ed2a3912a0b 000000000000 000000000000
383 # verify the repository
384 checking changesets
385 checking manifests
386 crosschecking files in changesets and manifests
387 checking files
388 checking dirstate
389 checked 6 changesets with 15 changes to 9 files
390 $ cd ..
391
392
393 Having cloning all only branch c
394 --------------------------------
395
396 $ hg clone --narrow ssh://user@dummy/server --rev `cat ./rev_c_2_` --include dir_x --include dir_y client_xy_rev_from_c_only --noupdate
397 adding changesets
398 adding manifests
399 adding file changes
400 added 5 changesets with 10 changes to 6 files
401 new changesets 4978c5c7386b:d04e01dcc82d
402 $ cd client_xy_rev_from_c_only
403 $ hg log -GT "{rev}:{node|short}: {desc}\n {files}\n"
404 o 4:d04e01dcc82d: rev_c_2_
405 | dir_y/f1 dir_y/f3 dir_z/f2 dir_z/f4
406 o 3:fa05dbe8eed1: rev_c_1_
407 | dir_z/f1 dir_z/f2
408 o 2:59b4258b00dc: rev_c_0_
409 | dir_y/f1 dir_y/f2 dir_z/f1
410 o 1:63f468a0fdac: rev_a_
411 | dir_x/f1 dir_x/f2 dir_x/f3 dir_y/f1 dir_y/f2 dir_y/f3
412 o 0:4978c5c7386b: root
413 readme.txt
414
415 $ hg tracked --addinclude dir_z
416 comparing with ssh://user@dummy/server
417 searching for changes
418 adding changesets
419 adding manifests
420 adding file changes
421 added 0 changesets with 5 changes to 3 files
422 $ sh ../linkrev-check.sh
423 # expected linkrev for dir_z/f1
424 0 2
425 1 3
426 rev linkrev nodeid p1-nodeid p2-nodeid
427 0 2 360afd990eef 000000000000 000000000000
428 1 3 7054ee088631 360afd990eef 000000000000
429 # expected linkrev for dir_z/f2
430 0 3
431 1 4
432 rev linkrev nodeid p1-nodeid p2-nodeid
433 0 3 093bb0f8a0fb 000000000000 000000000000
434 1 4 0f47e254cb19 093bb0f8a0fb 000000000000
435 # expected linkrev for dir_z/f4
436 0 4
437 rev linkrev nodeid p1-nodeid p2-nodeid
438 0 4 b004912a8510 000000000000 000000000000
439 # verify the repository
440 checking changesets
441 checking manifests
442 crosschecking files in changesets and manifests
443 checking files
444 checking dirstate
445 checked 5 changesets with 15 changes to 9 files
446 $ cd ..
447
448 Having cloning all first branch b
449 ---------------------------------
450
451 $ hg clone --narrow ssh://user@dummy/server --rev `cat ./rev_b_3_` --include dir_x --include dir_y client_xy_rev_from_b_first --noupdate
452 adding changesets
453 adding manifests
454 adding file changes
455 added 6 changesets with 10 changes to 6 files
456 new changesets 4978c5c7386b:fc05b303b551
457 $ cd client_xy_rev_from_b_first
458 $ hg pull
459 pulling from ssh://user@dummy/server
460 searching for changes
461 adding changesets
462 adding manifests
463 adding file changes
464 added 5 changesets with 6 changes to 4 files
465 new changesets 59b4258b00dc:71e6a9c7a6a2
466 (run 'hg update' to get a working copy)
467 $ hg log -GT "{rev}:{node|short}: {desc}\n {files}\n"
468 o 10:71e6a9c7a6a2: rev_d_1_
469 | dir_y/f1 dir_z/f1 dir_z/f4
470 o 9:b0a0cbe5ce57: rev_d_0_
471 |\ dir_x/f1 dir_z/f1 dir_z/f3
472 | o 8:d04e01dcc82d: rev_c_2_
473 | | dir_y/f1 dir_y/f3 dir_z/f2 dir_z/f4
474 | o 7:fa05dbe8eed1: rev_c_1_
475 | | dir_z/f1 dir_z/f2
476 | o 6:59b4258b00dc: rev_c_0_
477 | | dir_y/f1 dir_y/f2 dir_z/f1
478 o | 5:fc05b303b551: rev_b_3_
479 | | dir_x/f3 dir_z/f1 dir_z/f2
480 o | 4:17fd34adb43b: rev_b_2_
481 | | dir_x/f2 dir_z/f1 dir_z/f2 dir_z/f3
482 o | 3:328f8ced5276: rev_b_1_
483 | | dir_x/f1
484 o | 2:0ccce83dd29b: rev_b_0_
485 |/ dir_x/f1
486 o 1:63f468a0fdac: rev_a_
487 | dir_x/f1 dir_x/f2 dir_x/f3 dir_y/f1 dir_y/f2 dir_y/f3
488 o 0:4978c5c7386b: root
489 readme.txt
490
491 $ hg tracked --addinclude dir_z
492 comparing with ssh://user@dummy/server
493 searching for changes
494 adding changesets
495 adding manifests
496 adding file changes
497 added 0 changesets with 10 changes to 4 files
498 $ sh ../linkrev-check.sh
499 # expected linkrev for dir_z/f1
500 0 4
501 1 5
502 2 9
503 3 10
504 rev linkrev nodeid p1-nodeid p2-nodeid
505 0 6 360afd990eef 000000000000 000000000000 (known-bad-output !)
506 0 4 360afd990eef 000000000000 000000000000 (missing-correct-output !)
507 1 7 7054ee088631 360afd990eef 000000000000 (known-bad-output !)
508 1 5 7054ee088631 360afd990eef 000000000000 (missing-correct-output !)
509 2 9 6bb290463f21 7054ee088631 000000000000
510 3 10 91fec784ff86 6bb290463f21 000000000000
511 # expected linkrev for dir_z/f2
512 0 4
513 1 5
514 rev linkrev nodeid p1-nodeid p2-nodeid
515 0 7 093bb0f8a0fb 000000000000 000000000000 (known-bad-output !)
516 0 4 093bb0f8a0fb 000000000000 000000000000 (missing-correct-output !)
517 1 5 0f47e254cb19 093bb0f8a0fb 000000000000
518 # expected linkrev for dir_z/f3
519 0 4
520 1 9
521 rev linkrev nodeid p1-nodeid p2-nodeid
522 0 4 2ed2a3912a0b 000000000000 000000000000
523 1 9 7c6d649320ae 2ed2a3912a0b 000000000000
524 # expected linkrev for dir_z/f4
525 0 8
526 1 10
527 rev linkrev nodeid p1-nodeid p2-nodeid
528 0 8 b004912a8510 000000000000 000000000000
529 1 10 9f85b3b95e70 b004912a8510 000000000000
530 # verify the repository
531 checking changesets
532 checking manifests
533 crosschecking files in changesets and manifests
534 checking files
535 checking dirstate
536 checked 11 changesets with 26 changes to 10 files
537 $ cd ..
538
539
540 Having cloning all first branch c
541 ---------------------------------
542
543 $ hg clone --narrow ssh://user@dummy/server --rev `cat ./rev_c_2_` --include dir_x --include dir_y client_xy_rev_from_c_first --noupdate
544 adding changesets
545 adding manifests
546 adding file changes
547 added 5 changesets with 10 changes to 6 files
548 new changesets 4978c5c7386b:d04e01dcc82d
549 $ cd client_xy_rev_from_c_first
550 $ hg pull
551 pulling from ssh://user@dummy/server
552 searching for changes
553 adding changesets
554 adding manifests
555 adding file changes
556 added 6 changesets with 6 changes to 4 files
557 new changesets 0ccce83dd29b:71e6a9c7a6a2
558 (run 'hg update' to get a working copy)
559 $ hg log -GT "{rev}:{node|short}: {desc}\n {files}\n"
560 o 10:71e6a9c7a6a2: rev_d_1_
561 | dir_y/f1 dir_z/f1 dir_z/f4
562 o 9:b0a0cbe5ce57: rev_d_0_
563 |\ dir_x/f1 dir_z/f1 dir_z/f3
564 | o 8:fc05b303b551: rev_b_3_
565 | | dir_x/f3 dir_z/f1 dir_z/f2
566 | o 7:17fd34adb43b: rev_b_2_
567 | | dir_x/f2 dir_z/f1 dir_z/f2 dir_z/f3
568 | o 6:328f8ced5276: rev_b_1_
569 | | dir_x/f1
570 | o 5:0ccce83dd29b: rev_b_0_
571 | | dir_x/f1
572 o | 4:d04e01dcc82d: rev_c_2_
573 | | dir_y/f1 dir_y/f3 dir_z/f2 dir_z/f4
574 o | 3:fa05dbe8eed1: rev_c_1_
575 | | dir_z/f1 dir_z/f2
576 o | 2:59b4258b00dc: rev_c_0_
577 |/ dir_y/f1 dir_y/f2 dir_z/f1
578 o 1:63f468a0fdac: rev_a_
579 | dir_x/f1 dir_x/f2 dir_x/f3 dir_y/f1 dir_y/f2 dir_y/f3
580 o 0:4978c5c7386b: root
581 readme.txt
582
583 $ hg tracked --addinclude dir_z
584 comparing with ssh://user@dummy/server
585 searching for changes
586 adding changesets
587 adding manifests
588 adding file changes
589 added 0 changesets with 10 changes to 4 files
590 $ sh ../linkrev-check.sh
591 # expected linkrev for dir_z/f1
592 0 2
593 1 3
594 2 9
595 3 10
596 rev linkrev nodeid p1-nodeid p2-nodeid
597 0 2 360afd990eef 000000000000 000000000000
598 1 3 7054ee088631 360afd990eef 000000000000
599 2 9 6bb290463f21 7054ee088631 000000000000
600 3 10 91fec784ff86 6bb290463f21 000000000000
601 # expected linkrev for dir_z/f2
602 0 3
603 1 4
604 rev linkrev nodeid p1-nodeid p2-nodeid
605 0 3 093bb0f8a0fb 000000000000 000000000000
606 1 8 0f47e254cb19 093bb0f8a0fb 000000000000 (known-bad-output !)
607 1 4 0f47e254cb19 093bb0f8a0fb 000000000000 (missing-correct-output !)
608 # expected linkrev for dir_z/f3
609 0 7
610 1 9
611 rev linkrev nodeid p1-nodeid p2-nodeid
612 0 7 2ed2a3912a0b 000000000000 000000000000
613 1 9 7c6d649320ae 2ed2a3912a0b 000000000000
614 # expected linkrev for dir_z/f4
615 0 4
616 1 10
617 rev linkrev nodeid p1-nodeid p2-nodeid
618 0 4 b004912a8510 000000000000 000000000000
619 1 10 9f85b3b95e70 b004912a8510 000000000000
620 # verify the repository
621 checking changesets
622 checking manifests
623 crosschecking files in changesets and manifests
624 checking files
625 checking dirstate
626 checked 11 changesets with 26 changes to 10 files
627 $ cd ..
@@ -46,8 +46,8 b' p1()'
46 46 # Used in revision c1546d7400ef
47 47 min(0::)
48 48 # Used in revision 546fa6576815
49 author(lmoscovicz) or author(olivia)
50 author(olivia) or author(lmoscovicz)
49 author(lmoscovicz) or author("pierre-yves")
50 author("pierre-yves") or author(lmoscovicz)
51 51 # Used in revision 9bfe68357c01
52 52 public() and id("d82e2223f132")
53 53 # Used in revision ba89f7b542c9
@@ -100,7 +100,7 b' draft()'
100 100 draft() and ::tip
101 101 ::tip and draft()
102 102 author(lmoscovicz)
103 author(olivia)
103 author("pierre-yves")
104 104 ::p1(p1(tip))::
105 105 public()
106 106 :10000 and public()
@@ -130,7 +130,7 b' roots((:42) + (tip~42:))'
130 130 head()
131 131 head() - public()
132 132 draft() and head()
133 head() and author("olivia")
133 head() and author("pierre-yves")
134 134
135 135 # testing the mutable phases set
136 136 draft()
@@ -25,9 +25,9 b' draft() and ::tip'
25 25 0::tip
26 26 roots(0::tip)
27 27 author(lmoscovicz)
28 author(olivia)
29 author(lmoscovicz) or author(olivia)
30 author(olivia) or author(lmoscovicz)
28 author("pierre-yves")
29 author(lmoscovicz) or author("pierre-yves")
30 author("pierre-yves") or author(lmoscovicz)
31 31 tip:0
32 32 0::
33 33 # those two `roots(...)` inputs are close to what phase movement use.
@@ -20,7 +20,10 b' Configurations'
20 20
21 21 ``profile-benchmark``
22 22 Enable profiling for the benchmarked section.
23 (The first iteration is benchmarked)
23 (by default, the first iteration is benchmarked)
24
25 ``profiled-runs``
26 list of iteration to profile (starting from 0)
24 27
25 28 ``run-limits``
26 29 Control the number of runs each benchmark will perform. The option value
@@ -318,6 +321,11 b' try:'
318 321 )
319 322 configitem(
320 323 b'perf',
324 b'profiled-runs',
325 default=mercurial.configitems.dynamicdefault,
326 )
327 configitem(
328 b'perf',
321 329 b'run-limits',
322 330 default=mercurial.configitems.dynamicdefault,
323 331 experimental=True,
@@ -354,7 +362,7 b' except TypeError:'
354 362 )
355 363 configitem(
356 364 b'perf',
357 b'profile-benchmark',
365 b'profiled-runs',
358 366 default=mercurial.configitems.dynamicdefault,
359 367 )
360 368 configitem(
@@ -491,9 +499,12 b' def gettimer(ui, opts=None):'
491 499 limits = DEFAULTLIMITS
492 500
493 501 profiler = None
502 profiled_runs = set()
494 503 if profiling is not None:
495 504 if ui.configbool(b"perf", b"profile-benchmark", False):
496 profiler = profiling.profile(ui)
505 profiler = lambda: profiling.profile(ui)
506 for run in ui.configlist(b"perf", b"profiled-runs", [0]):
507 profiled_runs.add(int(run))
497 508
498 509 prerun = getint(ui, b"perf", b"pre-run", 0)
499 510 t = functools.partial(
@@ -503,6 +514,7 b' def gettimer(ui, opts=None):'
503 514 limits=limits,
504 515 prerun=prerun,
505 516 profiler=profiler,
517 profiled_runs=profiled_runs,
506 518 )
507 519 return t, fm
508 520
@@ -547,27 +559,32 b' def _timer('
547 559 limits=DEFAULTLIMITS,
548 560 prerun=0,
549 561 profiler=None,
562 profiled_runs=(0,),
550 563 ):
551 564 gc.collect()
552 565 results = []
553 begin = util.timer()
554 566 count = 0
555 567 if profiler is None:
556 profiler = NOOPCTX
568 profiler = lambda: NOOPCTX
557 569 for i in range(prerun):
558 570 if setup is not None:
559 571 setup()
560 572 with context():
561 573 func()
574 begin = util.timer()
562 575 keepgoing = True
563 576 while keepgoing:
577 if count in profiled_runs:
578 prof = profiler()
579 else:
580 prof = NOOPCTX
564 581 if setup is not None:
565 582 setup()
566 583 with context():
567 with profiler:
584 gc.collect()
585 with prof:
568 586 with timeone() as item:
569 587 r = func()
570 profiler = NOOPCTX
571 588 count += 1
572 589 results.append(item[0])
573 590 cstop = util.timer()
@@ -2029,6 +2046,19 b' def perfstartup(ui, repo, **opts):'
2029 2046 fm.end()
2030 2047
2031 2048
2049 def _clear_store_audit_cache(repo):
2050 vfs = getsvfs(repo)
2051 # unwrap the fncache proxy
2052 if not hasattr(vfs, "audit"):
2053 vfs = getattr(vfs, "vfs", vfs)
2054 auditor = vfs.audit
2055 if hasattr(auditor, "clear_audit_cache"):
2056 auditor.clear_audit_cache()
2057 elif hasattr(auditor, "audited"):
2058 auditor.audited.clear()
2059 auditor.auditeddir.clear()
2060
2061
2032 2062 def _find_stream_generator(version):
2033 2063 """find the proper generator function for this stream version"""
2034 2064 import mercurial.streamclone
@@ -2040,7 +2070,7 b' def _find_stream_generator(version):'
2040 2070 if generatev1 is not None:
2041 2071
2042 2072 def generate(repo):
2043 entries, bytes, data = generatev2(repo, None, None, True)
2073 entries, bytes, data = generatev1(repo, None, None, True)
2044 2074 return data
2045 2075
2046 2076 available[b'v1'] = generatev1
@@ -2058,8 +2088,7 b' def _find_stream_generator(version):'
2058 2088 if generatev3 is not None:
2059 2089
2060 2090 def generate(repo):
2061 entries, bytes, data = generatev3(repo, None, None, True)
2062 return data
2091 return generatev3(repo, None, None, True)
2063 2092
2064 2093 available[b'v3-exp'] = generate
2065 2094
@@ -2085,7 +2114,8 b' def _find_stream_generator(version):'
2085 2114 b'',
2086 2115 b'stream-version',
2087 2116 b'latest',
2088 b'stream version to use ("v1", "v2", "v3" or "latest", (the default))',
2117 b'stream version to use ("v1", "v2", "v3-exp" '
2118 b'or "latest", (the default))',
2089 2119 ),
2090 2120 ]
2091 2121 + formatteropts,
@@ -2102,6 +2132,9 b' def perf_stream_clone_scan(ui, repo, str'
2102 2132
2103 2133 def setupone():
2104 2134 result_holder[0] = None
2135 # This is important for the full generation, even if it does not
2136 # currently matters, it seems safer to also real it here.
2137 _clear_store_audit_cache(repo)
2105 2138
2106 2139 generate = _find_stream_generator(stream_version)
2107 2140
@@ -2120,7 +2153,8 b' def perf_stream_clone_scan(ui, repo, str'
2120 2153 b'',
2121 2154 b'stream-version',
2122 2155 b'latest',
2123 b'stream version to us ("v1", "v2" or "latest", (the default))',
2156 b'stream version to us ("v1", "v2", "v3-exp" '
2157 b'or "latest", (the default))',
2124 2158 ),
2125 2159 ]
2126 2160 + formatteropts,
@@ -2136,12 +2170,15 b' def perf_stream_clone_generate(ui, repo,'
2136 2170
2137 2171 generate = _find_stream_generator(stream_version)
2138 2172
2173 def setup():
2174 _clear_store_audit_cache(repo)
2175
2139 2176 def runone():
2140 2177 # the lock is held for the duration the initialisation
2141 2178 for chunk in generate(repo):
2142 2179 pass
2143 2180
2144 timer(runone, title=b"generate")
2181 timer(runone, setup=setup, title=b"generate")
2145 2182 fm.end()
2146 2183
2147 2184
@@ -2187,10 +2224,18 b' def perf_stream_clone_consume(ui, repo, '
2187 2224
2188 2225 run_variables = [None, None]
2189 2226
2227 # we create the new repository next to the other one for two reasons:
2228 # - this way we use the same file system, which are relevant for benchmark
2229 # - if /tmp/ is small, the operation could overfills it.
2230 source_repo_dir = os.path.dirname(repo.root)
2231
2190 2232 @contextlib.contextmanager
2191 2233 def context():
2192 2234 with open(filename, mode='rb') as bundle:
2193 with tempfile.TemporaryDirectory() as tmp_dir:
2235 with tempfile.TemporaryDirectory(
2236 prefix=b'hg-perf-stream-consume-',
2237 dir=source_repo_dir,
2238 ) as tmp_dir:
2194 2239 tmp_dir = fsencode(tmp_dir)
2195 2240 run_variables[0] = bundle
2196 2241 run_variables[1] = tmp_dir
@@ -2201,11 +2246,15 b' def perf_stream_clone_consume(ui, repo, '
2201 2246 def runone():
2202 2247 bundle = run_variables[0]
2203 2248 tmp_dir = run_variables[1]
2249
2250 # we actually wants to copy all config to ensure the repo config is
2251 # taken in account during the benchmark
2252 new_ui = repo.ui.__class__(repo.ui)
2204 2253 # only pass ui when no srcrepo
2205 2254 localrepo.createrepository(
2206 repo.ui, tmp_dir, requirements=repo.requirements
2255 new_ui, tmp_dir, requirements=repo.requirements
2207 2256 )
2208 target = hg.repository(repo.ui, tmp_dir)
2257 target = hg.repository(new_ui, tmp_dir)
2209 2258 gen = exchange.readbundle(target.ui, bundle, bundle.name)
2210 2259 # stream v1
2211 2260 if util.safehasattr(gen, 'apply'):
@@ -4205,15 +4254,24 b' def perfbranchmap(ui, repo, *filternames'
4205 4254 # add unfiltered
4206 4255 allfilters.append(None)
4207 4256
4208 if util.safehasattr(branchmap.branchcache, 'fromfile'):
4257 old_branch_cache_from_file = None
4258 branchcacheread = None
4259 if util.safehasattr(branchmap, 'branch_cache_from_file'):
4260 old_branch_cache_from_file = branchmap.branch_cache_from_file
4261 branchmap.branch_cache_from_file = lambda *args: None
4262 elif util.safehasattr(branchmap.branchcache, 'fromfile'):
4209 4263 branchcacheread = safeattrsetter(branchmap.branchcache, b'fromfile')
4210 4264 branchcacheread.set(classmethod(lambda *args: None))
4211 4265 else:
4212 4266 # older versions
4213 4267 branchcacheread = safeattrsetter(branchmap, b'read')
4214 4268 branchcacheread.set(lambda *args: None)
4215 branchcachewrite = safeattrsetter(branchmap.branchcache, b'write')
4216 branchcachewrite.set(lambda *args: None)
4269 if util.safehasattr(branchmap, '_LocalBranchCache'):
4270 branchcachewrite = safeattrsetter(branchmap._LocalBranchCache, b'write')
4271 branchcachewrite.set(lambda *args: None)
4272 else:
4273 branchcachewrite = safeattrsetter(branchmap.branchcache, b'write')
4274 branchcachewrite.set(lambda *args: None)
4217 4275 try:
4218 4276 for name in allfilters:
4219 4277 printname = name
@@ -4221,7 +4279,10 b' def perfbranchmap(ui, repo, *filternames'
4221 4279 printname = b'unfiltered'
4222 4280 timer(getbranchmap(name), title=printname)
4223 4281 finally:
4224 branchcacheread.restore()
4282 if old_branch_cache_from_file is not None:
4283 branchmap.branch_cache_from_file = old_branch_cache_from_file
4284 if branchcacheread is not None:
4285 branchcacheread.restore()
4225 4286 branchcachewrite.restore()
4226 4287 fm.end()
4227 4288
@@ -4303,6 +4364,19 b' def perfbranchmapupdate(ui, repo, base=('
4303 4364 baserepo = repo.filtered(b'__perf_branchmap_update_base')
4304 4365 targetrepo = repo.filtered(b'__perf_branchmap_update_target')
4305 4366
4367 bcache = repo.branchmap()
4368 copy_method = 'copy'
4369
4370 copy_base_kwargs = copy_base_kwargs = {}
4371 if hasattr(bcache, 'copy'):
4372 if 'repo' in getargspec(bcache.copy).args:
4373 copy_base_kwargs = {"repo": baserepo}
4374 copy_target_kwargs = {"repo": targetrepo}
4375 else:
4376 copy_method = 'inherit_for'
4377 copy_base_kwargs = {"repo": baserepo}
4378 copy_target_kwargs = {"repo": targetrepo}
4379
4306 4380 # try to find an existing branchmap to reuse
4307 4381 subsettable = getbranchmapsubsettable()
4308 4382 candidatefilter = subsettable.get(None)
@@ -4311,7 +4385,7 b' def perfbranchmapupdate(ui, repo, base=('
4311 4385 if candidatebm.validfor(baserepo):
4312 4386 filtered = repoview.filterrevs(repo, candidatefilter)
4313 4387 missing = [r for r in allbaserevs if r in filtered]
4314 base = candidatebm.copy()
4388 base = getattr(candidatebm, copy_method)(**copy_base_kwargs)
4315 4389 base.update(baserepo, missing)
4316 4390 break
4317 4391 candidatefilter = subsettable.get(candidatefilter)
@@ -4321,7 +4395,7 b' def perfbranchmapupdate(ui, repo, base=('
4321 4395 base.update(baserepo, allbaserevs)
4322 4396
4323 4397 def setup():
4324 x[0] = base.copy()
4398 x[0] = getattr(base, copy_method)(**copy_target_kwargs)
4325 4399 if clearcaches:
4326 4400 unfi._revbranchcache = None
4327 4401 clearchangelog(repo)
@@ -4368,10 +4442,10 b' def perfbranchmapload(ui, repo, filter=b'
4368 4442
4369 4443 repo.branchmap() # make sure we have a relevant, up to date branchmap
4370 4444
4371 try:
4372 fromfile = branchmap.branchcache.fromfile
4373 except AttributeError:
4374 # older versions
4445 fromfile = getattr(branchmap, 'branch_cache_from_file', None)
4446 if fromfile is None:
4447 fromfile = getattr(branchmap.branchcache, 'fromfile', None)
4448 if fromfile is None:
4375 4449 fromfile = branchmap.read
4376 4450
4377 4451 currentfilter = filter
@@ -430,6 +430,7 b' def composestandinmatcher(repo, rmatcher'
430 430 def composedmatchfn(f):
431 431 return isstandin(f) and rmatcher.matchfn(splitstandin(f))
432 432
433 smatcher._was_tampered_with = True
433 434 smatcher.matchfn = composedmatchfn
434 435
435 436 return smatcher
@@ -716,6 +717,7 b' def updatestandinsbymatch(repo, match):'
716 717 return match
717 718
718 719 lfiles = listlfiles(repo)
720 match._was_tampered_with = True
719 721 match._files = repo._subdirlfs(match.files(), lfiles)
720 722
721 723 # Case 2: user calls commit with specified patterns: refresh
@@ -746,6 +748,7 b' def updatestandinsbymatch(repo, match):'
746 748 # user. Have to modify _files to prevent commit() from
747 749 # complaining "not tracked" for big files.
748 750 match = copy.copy(match)
751 match._was_tampered_with = True
749 752 origmatchfn = match.matchfn
750 753
751 754 # Check both the list of largefiles and the list of
@@ -71,6 +71,7 b' def composelargefilematcher(match, manif'
71 71 """create a matcher that matches only the largefiles in the original
72 72 matcher"""
73 73 m = copy.copy(match)
74 m._was_tampered_with = True
74 75 lfile = lambda f: lfutil.standin(f) in manifest
75 76 m._files = [lf for lf in m._files if lfile(lf)]
76 77 m._fileset = set(m._files)
@@ -86,6 +87,7 b' def composenormalfilematcher(match, mani'
86 87 excluded.update(exclude)
87 88
88 89 m = copy.copy(match)
90 m._was_tampered_with = True
89 91 notlfile = lambda f: not (
90 92 lfutil.isstandin(f) or lfutil.standin(f) in manifest or f in excluded
91 93 )
@@ -442,6 +444,8 b' def overridelog(orig, ui, repo, *pats, *'
442 444
443 445 pats.update(fixpats(f, tostandin) for f in p)
444 446
447 m._was_tampered_with = True
448
445 449 for i in range(0, len(m._files)):
446 450 # Don't add '.hglf' to m.files, since that is already covered by '.'
447 451 if m._files[i] == b'.':
@@ -849,6 +853,7 b' def overridecopy(orig, ui, repo, pats, o'
849 853 newpats.append(pat)
850 854 match = orig(ctx, newpats, opts, globbed, default, badfn=badfn)
851 855 m = copy.copy(match)
856 m._was_tampered_with = True
852 857 lfile = lambda f: lfutil.standin(f) in manifest
853 858 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
854 859 m._fileset = set(m._files)
@@ -967,6 +972,7 b' def overriderevert(orig, ui, repo, ctx, '
967 972 opts = {}
968 973 match = orig(mctx, pats, opts, globbed, default, badfn=badfn)
969 974 m = copy.copy(match)
975 m._was_tampered_with = True
970 976
971 977 # revert supports recursing into subrepos, and though largefiles
972 978 # currently doesn't work correctly in that case, this match is
@@ -1595,6 +1601,7 b' def scmutiladdremove('
1595 1601 # confused state later.
1596 1602 if s.deleted:
1597 1603 m = copy.copy(matcher)
1604 m._was_tampered_with = True
1598 1605
1599 1606 # The m._files and m._map attributes are not changed to the deleted list
1600 1607 # because that affects the m.exact() test, which in turn governs whether
@@ -1721,6 +1728,7 b' def overridecat(orig, ui, repo, file1, *'
1721 1728 err = 1
1722 1729 notbad = set()
1723 1730 m = scmutil.match(ctx, (file1,) + pats, pycompat.byteskwargs(opts))
1731 m._was_tampered_with = True
1724 1732 origmatchfn = m.matchfn
1725 1733
1726 1734 def lfmatchfn(f):
@@ -181,6 +181,7 b' def reposetup(ui, repo):'
181 181 return newfiles
182 182
183 183 m = copy.copy(match)
184 m._was_tampered_with = True
184 185 m._files = tostandins(m._files)
185 186
186 187 result = orig(
@@ -193,6 +194,7 b' def reposetup(ui, repo):'
193 194 dirstate = self.dirstate
194 195 return sf in dirstate or dirstate.hasdir(sf)
195 196
197 match._was_tampered_with = True
196 198 match._files = [f for f in match._files if sfindirstate(f)]
197 199 # Don't waste time getting the ignored and unknown
198 200 # files from lfdirstate
@@ -2133,16 +2133,16 b' def pullrebase(orig, ui, repo, *args, **'
2133 2133 )
2134 2134
2135 2135 revsprepull = len(repo)
2136 origpostincoming = commands.postincoming
2136 origpostincoming = cmdutil.postincoming
2137 2137
2138 2138 def _dummy(*args, **kwargs):
2139 2139 pass
2140 2140
2141 commands.postincoming = _dummy
2141 cmdutil.postincoming = _dummy
2142 2142 try:
2143 2143 ret = orig(ui, repo, *args, **opts)
2144 2144 finally:
2145 commands.postincoming = origpostincoming
2145 cmdutil.postincoming = origpostincoming
2146 2146 revspostpull = len(repo)
2147 2147 if revspostpull > revsprepull:
2148 2148 # --rev option from pull conflict with rebase own --rev
@@ -34780,8 +34780,8 b' msgid "creating obsolete markers is not '
34780 34780 msgstr "廃止マーカの作成機能は無効化されています"
34781 34781
34782 34782 #, python-format
34783 msgid "obsolete feature not enabled but %i markers found!\n"
34784 msgstr "obsolete 機能は無効ですが、 %i 個の廃止情報マーカが存在します!\n"
34783 msgid "\"obsolete\" feature not enabled but %i markers found!\n"
34784 msgstr "\"obsolete\" 機能は無効ですが、 %i 個の廃止情報マーカが存在します!\n"
34785 34785
34786 34786 #, python-format
34787 34787 msgid "unknown key: %r"
@@ -36049,9 +36049,9 b' msgstr ""'
36049 36049 "repositório"
36050 36050
36051 36051 #, python-format
36052 msgid "obsolete feature not enabled but %i markers found!\n"
36053 msgstr ""
36054 "a funcionalidade obsolete não está habilitada, mas foram encontradas %i "
36052 msgid "\"obsolete\" feature not enabled but %i markers found!\n"
36053 msgstr ""
36054 "a funcionalidade \"obsolete\" não está habilitada, mas foram encontradas %i "
36055 36055 "marcações!\n"
36056 36056
36057 36057 #, python-format
This diff has been collapsed as it changes many lines, (1021 lines changed) Show them Hide them
@@ -15,6 +15,7 b' from .node import ('
15 15 )
16 16
17 17 from typing import (
18 Any,
18 19 Callable,
19 20 Dict,
20 21 Iterable,
@@ -24,6 +25,7 b' from typing import ('
24 25 TYPE_CHECKING,
25 26 Tuple,
26 27 Union,
28 cast,
27 29 )
28 30
29 31 from . import (
@@ -59,7 +61,37 b' class BranchMapCache:'
59 61
60 62 def __getitem__(self, repo):
61 63 self.updatecache(repo)
62 return self._per_filter[repo.filtername]
64 bcache = self._per_filter[repo.filtername]
65 bcache._ensure_populated(repo)
66 assert bcache._filtername == repo.filtername, (
67 bcache._filtername,
68 repo.filtername,
69 )
70 return bcache
71
72 def update_disk(self, repo, detect_pure_topo=False):
73 """ensure and up-to-date cache is (or will be) written on disk
74
75 The cache for this repository view is updated if needed and written on
76 disk.
77
78 If a transaction is in progress, the writing is schedule to transaction
79 close. See the `BranchMapCache.write_dirty` method.
80
81 This method exist independently of __getitem__ as it is sometime useful
82 to signal that we have no intend to use the data in memory yet.
83 """
84 self.updatecache(repo)
85 bcache = self._per_filter[repo.filtername]
86 assert bcache._filtername == repo.filtername, (
87 bcache._filtername,
88 repo.filtername,
89 )
90 if detect_pure_topo:
91 bcache._detect_pure_topo(repo)
92 tr = repo.currenttransaction()
93 if getattr(tr, 'finalized', True):
94 bcache.sync_disk(repo)
63 95
64 96 def updatecache(self, repo):
65 97 """Update the cache for the given filtered view on a repository"""
@@ -72,7 +104,7 b' class BranchMapCache:'
72 104 bcache = self._per_filter.get(filtername)
73 105 if bcache is None or not bcache.validfor(repo):
74 106 # cache object missing or cache object stale? Read from disk
75 bcache = branchcache.fromfile(repo)
107 bcache = branch_cache_from_file(repo)
76 108
77 109 revs = []
78 110 if bcache is None:
@@ -82,12 +114,13 b' class BranchMapCache:'
82 114 subsetname = subsettable.get(filtername)
83 115 if subsetname is not None:
84 116 subset = repo.filtered(subsetname)
85 bcache = self[subset].copy()
117 self.updatecache(subset)
118 bcache = self._per_filter[subset.filtername].inherit_for(repo)
86 119 extrarevs = subset.changelog.filteredrevs - cl.filteredrevs
87 120 revs.extend(r for r in extrarevs if r <= bcache.tiprev)
88 121 else:
89 122 # nothing to fall back on, start empty.
90 bcache = branchcache(repo)
123 bcache = new_branch_cache(repo)
91 124
92 125 revs.extend(cl.revs(start=bcache.tiprev + 1))
93 126 if revs:
@@ -118,7 +151,7 b' class BranchMapCache:'
118 151
119 152 if rbheads:
120 153 rtiprev = max((int(clrev(node)) for node in rbheads))
121 cache = branchcache(
154 cache = new_branch_cache(
122 155 repo,
123 156 remotebranchmap,
124 157 repo[rtiprev].node(),
@@ -131,19 +164,26 b' class BranchMapCache:'
131 164 for candidate in (b'base', b'immutable', b'served'):
132 165 rview = repo.filtered(candidate)
133 166 if cache.validfor(rview):
167 cache._filtername = candidate
134 168 self._per_filter[candidate] = cache
169 cache._state = STATE_DIRTY
135 170 cache.write(rview)
136 171 return
137 172
138 173 def clear(self):
139 174 self._per_filter.clear()
140 175
141 def write_delayed(self, repo):
176 def write_dirty(self, repo):
142 177 unfi = repo.unfiltered()
143 for filtername, cache in self._per_filter.items():
144 if cache._delayed:
178 for filtername in repoviewutil.get_ordered_subset():
179 cache = self._per_filter.get(filtername)
180 if cache is None:
181 continue
182 if filtername is None:
183 repo = unfi
184 else:
145 185 repo = unfi.filtered(filtername)
146 cache.write(repo)
186 cache.sync_disk(repo)
147 187
148 188
149 189 def _unknownnode(node):
@@ -158,26 +198,11 b' def _branchcachedesc(repo):'
158 198 return b'branch cache'
159 199
160 200
161 class branchcache:
201 class _BaseBranchCache:
162 202 """A dict like object that hold branches heads cache.
163 203
164 204 This cache is used to avoid costly computations to determine all the
165 205 branch heads of a repo.
166
167 The cache is serialized on disk in the following format:
168
169 <tip hex node> <tip rev number> [optional filtered repo hex hash]
170 <branch head hex node> <open/closed state> <branch name>
171 <branch head hex node> <open/closed state> <branch name>
172 ...
173
174 The first line is used to check if the cache is still valid. If the
175 branch cache is for a filtered repo view, an optional third hash is
176 included that hashes the hashes of all filtered and obsolete revisions.
177
178 The open/closed state is represented by a single letter 'o' or 'c'.
179 This field can be used to avoid changelog reads when determining if a
180 branch head closes a branch or not.
181 206 """
182 207
183 208 def __init__(
@@ -186,64 +211,18 b' class branchcache:'
186 211 entries: Union[
187 212 Dict[bytes, List[bytes]], Iterable[Tuple[bytes, List[bytes]]]
188 213 ] = (),
189 tipnode: Optional[bytes] = None,
190 tiprev: Optional[int] = nullrev,
191 filteredhash: Optional[bytes] = None,
192 closednodes: Optional[Set[bytes]] = None,
193 hasnode: Optional[Callable[[bytes], bool]] = None,
214 closed_nodes: Optional[Set[bytes]] = None,
194 215 ) -> None:
195 216 """hasnode is a function which can be used to verify whether changelog
196 217 has a given node or not. If it's not provided, we assume that every node
197 218 we have exists in changelog"""
198 self._repo = repo
199 self._delayed = False
200 if tipnode is None:
201 self.tipnode = repo.nullid
202 else:
203 self.tipnode = tipnode
204 self.tiprev = tiprev
205 self.filteredhash = filteredhash
206 219 # closednodes is a set of nodes that close their branch. If the branch
207 220 # cache has been updated, it may contain nodes that are no longer
208 221 # heads.
209 if closednodes is None:
210 self._closednodes = set()
211 else:
212 self._closednodes = closednodes
222 if closed_nodes is None:
223 closed_nodes = set()
224 self._closednodes = set(closed_nodes)
213 225 self._entries = dict(entries)
214 # whether closed nodes are verified or not
215 self._closedverified = False
216 # branches for which nodes are verified
217 self._verifiedbranches = set()
218 self._hasnode = hasnode
219 if self._hasnode is None:
220 self._hasnode = lambda x: True
221
222 def _verifyclosed(self):
223 """verify the closed nodes we have"""
224 if self._closedverified:
225 return
226 for node in self._closednodes:
227 if not self._hasnode(node):
228 _unknownnode(node)
229
230 self._closedverified = True
231
232 def _verifybranch(self, branch):
233 """verify head nodes for the given branch."""
234 if branch not in self._entries or branch in self._verifiedbranches:
235 return
236 for n in self._entries[branch]:
237 if not self._hasnode(n):
238 _unknownnode(n)
239
240 self._verifiedbranches.add(branch)
241
242 def _verifyall(self):
243 """verifies nodes of all the branches"""
244 needverification = set(self._entries.keys()) - self._verifiedbranches
245 for b in needverification:
246 self._verifybranch(b)
247 226
248 227 def __iter__(self):
249 228 return iter(self._entries)
@@ -252,115 +231,20 b' class branchcache:'
252 231 self._entries[key] = value
253 232
254 233 def __getitem__(self, key):
255 self._verifybranch(key)
256 234 return self._entries[key]
257 235
258 236 def __contains__(self, key):
259 self._verifybranch(key)
260 237 return key in self._entries
261 238
262 239 def iteritems(self):
263 for k, v in self._entries.items():
264 self._verifybranch(k)
265 yield k, v
240 return self._entries.items()
266 241
267 242 items = iteritems
268 243
269 244 def hasbranch(self, label):
270 245 """checks whether a branch of this name exists or not"""
271 self._verifybranch(label)
272 246 return label in self._entries
273 247
274 @classmethod
275 def fromfile(cls, repo):
276 f = None
277 try:
278 f = repo.cachevfs(cls._filename(repo))
279 lineiter = iter(f)
280 cachekey = next(lineiter).rstrip(b'\n').split(b" ", 2)
281 last, lrev = cachekey[:2]
282 last, lrev = bin(last), int(lrev)
283 filteredhash = None
284 hasnode = repo.changelog.hasnode
285 if len(cachekey) > 2:
286 filteredhash = bin(cachekey[2])
287 bcache = cls(
288 repo,
289 tipnode=last,
290 tiprev=lrev,
291 filteredhash=filteredhash,
292 hasnode=hasnode,
293 )
294 if not bcache.validfor(repo):
295 # invalidate the cache
296 raise ValueError('tip differs')
297 bcache.load(repo, lineiter)
298 except (IOError, OSError):
299 return None
300
301 except Exception as inst:
302 if repo.ui.debugflag:
303 msg = b'invalid %s: %s\n'
304 repo.ui.debug(
305 msg
306 % (
307 _branchcachedesc(repo),
308 stringutil.forcebytestr(inst),
309 )
310 )
311 bcache = None
312
313 finally:
314 if f:
315 f.close()
316
317 return bcache
318
319 def load(self, repo, lineiter):
320 """fully loads the branchcache by reading from the file using the line
321 iterator passed"""
322 for line in lineiter:
323 line = line.rstrip(b'\n')
324 if not line:
325 continue
326 node, state, label = line.split(b" ", 2)
327 if state not in b'oc':
328 raise ValueError('invalid branch state')
329 label = encoding.tolocal(label.strip())
330 node = bin(node)
331 self._entries.setdefault(label, []).append(node)
332 if state == b'c':
333 self._closednodes.add(node)
334
335 @staticmethod
336 def _filename(repo):
337 """name of a branchcache file for a given repo or repoview"""
338 filename = b"branch2"
339 if repo.filtername:
340 filename = b'%s-%s' % (filename, repo.filtername)
341 return filename
342
343 def validfor(self, repo):
344 """check that cache contents are valid for (a subset of) this repo
345
346 - False when the order of changesets changed or if we detect a strip.
347 - True when cache is up-to-date for the current repo or its subset."""
348 try:
349 node = repo.changelog.node(self.tiprev)
350 except IndexError:
351 # changesets were stripped and now we don't even have enough to
352 # find tiprev
353 return False
354 if self.tipnode != node:
355 # tiprev doesn't correspond to tipnode: repo was stripped, or this
356 # repo has a different order of changesets
357 return False
358 tiphash = scmutil.filteredhash(repo, self.tiprev, needobsolete=True)
359 # hashes don't match if this repo view has a different set of filtered
360 # revisions (e.g. due to phase changes) or obsolete revisions (e.g.
361 # history was rewritten)
362 return self.filteredhash == tiphash
363
364 248 def _branchtip(self, heads):
365 249 """Return tuple with last open head in heads and false,
366 250 otherwise return last closed head and true."""
@@ -383,7 +267,6 b' class branchcache:'
383 267 return (n for n in nodes if n not in self._closednodes)
384 268
385 269 def branchheads(self, branch, closed=False):
386 self._verifybranch(branch)
387 270 heads = self._entries[branch]
388 271 if not closed:
389 272 heads = list(self.iteropen(heads))
@@ -395,60 +278,8 b' class branchcache:'
395 278
396 279 def iterheads(self):
397 280 """returns all the heads"""
398 self._verifyall()
399 281 return self._entries.values()
400 282
401 def copy(self):
402 """return an deep copy of the branchcache object"""
403 return type(self)(
404 self._repo,
405 self._entries,
406 self.tipnode,
407 self.tiprev,
408 self.filteredhash,
409 self._closednodes,
410 )
411
412 def write(self, repo):
413 tr = repo.currenttransaction()
414 if not getattr(tr, 'finalized', True):
415 # Avoid premature writing.
416 #
417 # (The cache warming setup by localrepo will update the file later.)
418 self._delayed = True
419 return
420 try:
421 filename = self._filename(repo)
422 with repo.cachevfs(filename, b"w", atomictemp=True) as f:
423 cachekey = [hex(self.tipnode), b'%d' % self.tiprev]
424 if self.filteredhash is not None:
425 cachekey.append(hex(self.filteredhash))
426 f.write(b" ".join(cachekey) + b'\n')
427 nodecount = 0
428 for label, nodes in sorted(self._entries.items()):
429 label = encoding.fromlocal(label)
430 for node in nodes:
431 nodecount += 1
432 if node in self._closednodes:
433 state = b'c'
434 else:
435 state = b'o'
436 f.write(b"%s %s %s\n" % (hex(node), state, label))
437 repo.ui.log(
438 b'branchcache',
439 b'wrote %s with %d labels and %d nodes\n',
440 _branchcachedesc(repo),
441 len(self._entries),
442 nodecount,
443 )
444 self._delayed = False
445 except (IOError, OSError, error.Abort) as inst:
446 # Abort may be raised by read only opener, so log and continue
447 repo.ui.debug(
448 b"couldn't write branch cache: %s\n"
449 % stringutil.forcebytestr(inst)
450 )
451
452 283 def update(self, repo, revgen):
453 284 """Given a branchhead cache, self, that may have extra nodes or be
454 285 missing heads, and a generator of nodes that are strictly a superset of
@@ -456,29 +287,69 b' class branchcache:'
456 287 """
457 288 starttime = util.timer()
458 289 cl = repo.changelog
290 # Faster than using ctx.obsolete()
291 obsrevs = obsolete.getrevs(repo, b'obsolete')
459 292 # collect new branch entries
460 293 newbranches = {}
294 new_closed = set()
295 obs_ignored = set()
461 296 getbranchinfo = repo.revbranchcache().branchinfo
297 max_rev = -1
462 298 for r in revgen:
299 max_rev = max(max_rev, r)
300 if r in obsrevs:
301 # We ignore obsolete changesets as they shouldn't be
302 # considered heads.
303 obs_ignored.add(r)
304 continue
463 305 branch, closesbranch = getbranchinfo(r)
464 306 newbranches.setdefault(branch, []).append(r)
465 307 if closesbranch:
466 self._closednodes.add(cl.node(r))
308 new_closed.add(r)
309 if max_rev < 0:
310 msg = "running branchcache.update without revision to update"
311 raise error.ProgrammingError(msg)
312
313 self._process_new(
314 repo,
315 newbranches,
316 new_closed,
317 obs_ignored,
318 max_rev,
319 )
320
321 self._closednodes.update(cl.node(rev) for rev in new_closed)
467 322
468 # new tip revision which we found after iterating items from new
469 # branches
470 ntiprev = self.tiprev
323 duration = util.timer() - starttime
324 repo.ui.log(
325 b'branchcache',
326 b'updated %s in %.4f seconds\n',
327 _branchcachedesc(repo),
328 duration,
329 )
330 return max_rev
471 331
332 def _process_new(
333 self,
334 repo,
335 newbranches,
336 new_closed,
337 obs_ignored,
338 max_rev,
339 ):
340 """update the branchmap from a set of new information"""
472 341 # Delay fetching the topological heads until they are needed.
473 342 # A repository without non-continous branches can skip this part.
474 343 topoheads = None
475 344
345 cl = repo.changelog
346 getbranchinfo = repo.revbranchcache().branchinfo
347 # Faster than using ctx.obsolete()
348 obsrevs = obsolete.getrevs(repo, b'obsolete')
349
476 350 # If a changeset is visible, its parents must be visible too, so
477 351 # use the faster unfiltered parent accessor.
478 parentrevs = repo.unfiltered().changelog.parentrevs
479
480 # Faster than using ctx.obsolete()
481 obsrevs = obsolete.getrevs(repo, b'obsolete')
352 parentrevs = cl._uncheckedparentrevs
482 353
483 354 for branch, newheadrevs in newbranches.items():
484 355 # For every branch, compute the new branchheads.
@@ -520,11 +391,6 b' class branchcache:'
520 391 bheadset = {cl.rev(node) for node in bheads}
521 392 uncertain = set()
522 393 for newrev in sorted(newheadrevs):
523 if newrev in obsrevs:
524 # We ignore obsolete changesets as they shouldn't be
525 # considered heads.
526 continue
527
528 394 if not bheadset:
529 395 bheadset.add(newrev)
530 396 continue
@@ -561,50 +427,665 b' class branchcache:'
561 427 bheadset -= ancestors
562 428 if bheadset:
563 429 self[branch] = [cl.node(rev) for rev in sorted(bheadset)]
564 tiprev = max(newheadrevs)
565 if tiprev > ntiprev:
566 ntiprev = tiprev
430
431
432 STATE_CLEAN = 1
433 STATE_INHERITED = 2
434 STATE_DIRTY = 3
435
436
437 class _LocalBranchCache(_BaseBranchCache):
438 """base class of branch-map info for a local repo or repoview"""
439
440 _base_filename = None
441 _default_key_hashes: Tuple[bytes] = cast(Tuple[bytes], ())
442
443 def __init__(
444 self,
445 repo: "localrepo.localrepository",
446 entries: Union[
447 Dict[bytes, List[bytes]], Iterable[Tuple[bytes, List[bytes]]]
448 ] = (),
449 tipnode: Optional[bytes] = None,
450 tiprev: Optional[int] = nullrev,
451 key_hashes: Optional[Tuple[bytes]] = None,
452 closednodes: Optional[Set[bytes]] = None,
453 hasnode: Optional[Callable[[bytes], bool]] = None,
454 verify_node: bool = False,
455 inherited: bool = False,
456 ) -> None:
457 """hasnode is a function which can be used to verify whether changelog
458 has a given node or not. If it's not provided, we assume that every node
459 we have exists in changelog"""
460 self._filtername = repo.filtername
461 if tipnode is None:
462 self.tipnode = repo.nullid
463 else:
464 self.tipnode = tipnode
465 self.tiprev = tiprev
466 if key_hashes is None:
467 self.key_hashes = self._default_key_hashes
468 else:
469 self.key_hashes = key_hashes
470 self._state = STATE_CLEAN
471 if inherited:
472 self._state = STATE_INHERITED
473
474 super().__init__(repo=repo, entries=entries, closed_nodes=closednodes)
475 # closednodes is a set of nodes that close their branch. If the branch
476 # cache has been updated, it may contain nodes that are no longer
477 # heads.
478
479 # Do we need to verify branch at all ?
480 self._verify_node = verify_node
481 # branches for which nodes are verified
482 self._verifiedbranches = set()
483 self._hasnode = None
484 if self._verify_node:
485 self._hasnode = repo.changelog.hasnode
486
487 def _compute_key_hashes(self, repo) -> Tuple[bytes]:
488 raise NotImplementedError
489
490 def _ensure_populated(self, repo):
491 """make sure any lazily loaded values are fully populated"""
492
493 def _detect_pure_topo(self, repo) -> None:
494 pass
495
496 def validfor(self, repo):
497 """check that cache contents are valid for (a subset of) this repo
498
499 - False when the order of changesets changed or if we detect a strip.
500 - True when cache is up-to-date for the current repo or its subset."""
501 try:
502 node = repo.changelog.node(self.tiprev)
503 except IndexError:
504 # changesets were stripped and now we don't even have enough to
505 # find tiprev
506 return False
507 if self.tipnode != node:
508 # tiprev doesn't correspond to tipnode: repo was stripped, or this
509 # repo has a different order of changesets
510 return False
511 repo_key_hashes = self._compute_key_hashes(repo)
512 # hashes don't match if this repo view has a different set of filtered
513 # revisions (e.g. due to phase changes) or obsolete revisions (e.g.
514 # history was rewritten)
515 return self.key_hashes == repo_key_hashes
516
517 @classmethod
518 def fromfile(cls, repo):
519 f = None
520 try:
521 f = repo.cachevfs(cls._filename(repo))
522 lineiter = iter(f)
523 init_kwargs = cls._load_header(repo, lineiter)
524 bcache = cls(
525 repo,
526 verify_node=True,
527 **init_kwargs,
528 )
529 if not bcache.validfor(repo):
530 # invalidate the cache
531 raise ValueError('tip differs')
532 bcache._load_heads(repo, lineiter)
533 except (IOError, OSError):
534 return None
535
536 except Exception as inst:
537 if repo.ui.debugflag:
538 msg = b'invalid %s: %s\n'
539 msg %= (
540 _branchcachedesc(repo),
541 stringutil.forcebytestr(inst),
542 )
543 repo.ui.debug(msg)
544 bcache = None
545
546 finally:
547 if f:
548 f.close()
549
550 return bcache
551
552 @classmethod
553 def _load_header(cls, repo, lineiter) -> "dict[str, Any]":
554 raise NotImplementedError
555
556 def _load_heads(self, repo, lineiter):
557 """fully loads the branchcache by reading from the file using the line
558 iterator passed"""
559 for line in lineiter:
560 line = line.rstrip(b'\n')
561 if not line:
562 continue
563 node, state, label = line.split(b" ", 2)
564 if state not in b'oc':
565 raise ValueError('invalid branch state')
566 label = encoding.tolocal(label.strip())
567 node = bin(node)
568 self._entries.setdefault(label, []).append(node)
569 if state == b'c':
570 self._closednodes.add(node)
567 571
568 if ntiprev > self.tiprev:
569 self.tiprev = ntiprev
570 self.tipnode = cl.node(ntiprev)
572 @classmethod
573 def _filename(cls, repo):
574 """name of a branchcache file for a given repo or repoview"""
575 filename = cls._base_filename
576 assert filename is not None
577 if repo.filtername:
578 filename = b'%s-%s' % (filename, repo.filtername)
579 return filename
580
581 def inherit_for(self, repo):
582 """return a deep copy of the branchcache object"""
583 assert repo.filtername != self._filtername
584 other = type(self)(
585 repo=repo,
586 # we always do a shally copy of self._entries, and the values is
587 # always replaced, so no need to deepcopy until the above remains
588 # true.
589 entries=self._entries,
590 tipnode=self.tipnode,
591 tiprev=self.tiprev,
592 key_hashes=self.key_hashes,
593 closednodes=set(self._closednodes),
594 verify_node=self._verify_node,
595 inherited=True,
596 )
597 # also copy information about the current verification state
598 other._verifiedbranches = set(self._verifiedbranches)
599 return other
600
601 def sync_disk(self, repo):
602 """synchronise the on disk file with the cache state
603
604 If new value specific to this filter level need to be written, the file
605 will be updated, if the state of the branchcache is inherited from a
606 subset, any stalled on disk file will be deleted.
607
608 That method does nothing if there is nothing to do.
609 """
610 if self._state == STATE_DIRTY:
611 self.write(repo)
612 elif self._state == STATE_INHERITED:
613 filename = self._filename(repo)
614 repo.cachevfs.tryunlink(filename)
615
616 def write(self, repo):
617 assert self._filtername == repo.filtername, (
618 self._filtername,
619 repo.filtername,
620 )
621 assert self._state == STATE_DIRTY, self._state
622 # This method should not be called during an open transaction
623 tr = repo.currenttransaction()
624 if not getattr(tr, 'finalized', True):
625 msg = "writing branchcache in the middle of a transaction"
626 raise error.ProgrammingError(msg)
627 try:
628 filename = self._filename(repo)
629 with repo.cachevfs(filename, b"w", atomictemp=True) as f:
630 self._write_header(f)
631 nodecount = self._write_heads(repo, f)
632 repo.ui.log(
633 b'branchcache',
634 b'wrote %s with %d labels and %d nodes\n',
635 _branchcachedesc(repo),
636 len(self._entries),
637 nodecount,
638 )
639 self._state = STATE_CLEAN
640 except (IOError, OSError, error.Abort) as inst:
641 # Abort may be raised by read only opener, so log and continue
642 repo.ui.debug(
643 b"couldn't write branch cache: %s\n"
644 % stringutil.forcebytestr(inst)
645 )
646
647 def _write_header(self, fp) -> None:
648 raise NotImplementedError
649
650 def _write_heads(self, repo, fp) -> int:
651 """write list of heads to a file
652
653 Return the number of heads written."""
654 nodecount = 0
655 for label, nodes in sorted(self._entries.items()):
656 label = encoding.fromlocal(label)
657 for node in nodes:
658 nodecount += 1
659 if node in self._closednodes:
660 state = b'c'
661 else:
662 state = b'o'
663 fp.write(b"%s %s %s\n" % (hex(node), state, label))
664 return nodecount
665
666 def _verifybranch(self, branch):
667 """verify head nodes for the given branch."""
668 if not self._verify_node:
669 return
670 if branch not in self._entries or branch in self._verifiedbranches:
671 return
672 assert self._hasnode is not None
673 for n in self._entries[branch]:
674 if not self._hasnode(n):
675 _unknownnode(n)
676
677 self._verifiedbranches.add(branch)
678
679 def _verifyall(self):
680 """verifies nodes of all the branches"""
681 for b in self._entries.keys():
682 if b not in self._verifiedbranches:
683 self._verifybranch(b)
684
685 def __getitem__(self, key):
686 self._verifybranch(key)
687 return super().__getitem__(key)
688
689 def __contains__(self, key):
690 self._verifybranch(key)
691 return super().__contains__(key)
692
693 def iteritems(self):
694 self._verifyall()
695 return super().iteritems()
696
697 items = iteritems
698
699 def iterheads(self):
700 """returns all the heads"""
701 self._verifyall()
702 return super().iterheads()
703
704 def hasbranch(self, label):
705 """checks whether a branch of this name exists or not"""
706 self._verifybranch(label)
707 return super().hasbranch(label)
708
709 def branchheads(self, branch, closed=False):
710 self._verifybranch(branch)
711 return super().branchheads(branch, closed=closed)
712
713 def update(self, repo, revgen):
714 assert self._filtername == repo.filtername, (
715 self._filtername,
716 repo.filtername,
717 )
718 cl = repo.changelog
719 max_rev = super().update(repo, revgen)
720 # new tip revision which we found after iterating items from new
721 # branches
722 if max_rev is not None and max_rev > self.tiprev:
723 self.tiprev = max_rev
724 self.tipnode = cl.node(max_rev)
725 else:
726 # We should not be here is if this is false
727 assert cl.node(self.tiprev) == self.tipnode
571 728
572 729 if not self.validfor(repo):
573 # old cache key is now invalid for the repo, but we've just updated
574 # the cache and we assume it's valid, so let's make the cache key
575 # valid as well by recomputing it from the cached data
576 self.tipnode = repo.nullid
577 self.tiprev = nullrev
578 for heads in self.iterheads():
579 if not heads:
580 # all revisions on a branch are obsolete
581 continue
582 # note: tiprev is not necessarily the tip revision of repo,
583 # because the tip could be obsolete (i.e. not a head)
584 tiprev = max(cl.rev(node) for node in heads)
585 if tiprev > self.tiprev:
586 self.tipnode = cl.node(tiprev)
587 self.tiprev = tiprev
588 self.filteredhash = scmutil.filteredhash(
589 repo, self.tiprev, needobsolete=True
730 # the tiprev and tipnode should be aligned, so if the current repo
731 # is not seens as valid this is because old cache key is now
732 # invalid for the repo.
733 #
734 # However. we've just updated the cache and we assume it's valid,
735 # so let's make the cache key valid as well by recomputing it from
736 # the cached data
737 self.key_hashes = self._compute_key_hashes(repo)
738 self.filteredhash = scmutil.combined_filtered_and_obsolete_hash(
739 repo,
740 self.tiprev,
741 )
742
743 self._state = STATE_DIRTY
744 tr = repo.currenttransaction()
745 if getattr(tr, 'finalized', True):
746 # Avoid premature writing.
747 #
748 # (The cache warming setup by localrepo will update the file later.)
749 self.write(repo)
750
751
752 def branch_cache_from_file(repo) -> Optional[_LocalBranchCache]:
753 """Build a branch cache from on-disk data if possible
754
755 Return a branch cache of the right format depending of the repository.
756 """
757 if repo.ui.configbool(b"experimental", b"branch-cache-v3"):
758 return BranchCacheV3.fromfile(repo)
759 else:
760 return BranchCacheV2.fromfile(repo)
761
762
763 def new_branch_cache(repo, *args, **kwargs):
764 """Build a new branch cache from argument
765
766 Return a branch cache of the right format depending of the repository.
767 """
768 if repo.ui.configbool(b"experimental", b"branch-cache-v3"):
769 return BranchCacheV3(repo, *args, **kwargs)
770 else:
771 return BranchCacheV2(repo, *args, **kwargs)
772
773
774 class BranchCacheV2(_LocalBranchCache):
775 """a branch cache using version 2 of the format on disk
776
777 The cache is serialized on disk in the following format:
778
779 <tip hex node> <tip rev number> [optional filtered repo hex hash]
780 <branch head hex node> <open/closed state> <branch name>
781 <branch head hex node> <open/closed state> <branch name>
782 ...
783
784 The first line is used to check if the cache is still valid. If the
785 branch cache is for a filtered repo view, an optional third hash is
786 included that hashes the hashes of all filtered and obsolete revisions.
787
788 The open/closed state is represented by a single letter 'o' or 'c'.
789 This field can be used to avoid changelog reads when determining if a
790 branch head closes a branch or not.
791 """
792
793 _base_filename = b"branch2"
794
795 @classmethod
796 def _load_header(cls, repo, lineiter) -> "dict[str, Any]":
797 """parse the head of a branchmap file
798
799 return parameters to pass to a newly created class instance.
800 """
801 cachekey = next(lineiter).rstrip(b'\n').split(b" ", 2)
802 last, lrev = cachekey[:2]
803 last, lrev = bin(last), int(lrev)
804 filteredhash = ()
805 if len(cachekey) > 2:
806 filteredhash = (bin(cachekey[2]),)
807 return {
808 "tipnode": last,
809 "tiprev": lrev,
810 "key_hashes": filteredhash,
811 }
812
813 def _write_header(self, fp) -> None:
814 """write the branch cache header to a file"""
815 cachekey = [hex(self.tipnode), b'%d' % self.tiprev]
816 if self.key_hashes:
817 cachekey.append(hex(self.key_hashes[0]))
818 fp.write(b" ".join(cachekey) + b'\n')
819
820 def _compute_key_hashes(self, repo) -> Tuple[bytes]:
821 """return the cache key hashes that match this repoview state"""
822 filtered_hash = scmutil.combined_filtered_and_obsolete_hash(
823 repo,
824 self.tiprev,
825 needobsolete=True,
826 )
827 keys: Tuple[bytes] = cast(Tuple[bytes], ())
828 if filtered_hash is not None:
829 keys: Tuple[bytes] = (filtered_hash,)
830 return keys
831
832
833 class BranchCacheV3(_LocalBranchCache):
834 """a branch cache using version 3 of the format on disk
835
836 This version is still EXPERIMENTAL and the format is subject to changes.
837
838 The cache is serialized on disk in the following format:
839
840 <cache-key-xxx>=<xxx-value> <cache-key-yyy>=<yyy-value> […]
841 <branch head hex node> <open/closed state> <branch name>
842 <branch head hex node> <open/closed state> <branch name>
843 ...
844
845 The first line is used to check if the cache is still valid. It is a series
846 of key value pair. The following key are recognized:
847
848 - tip-rev: the rev-num of the tip-most revision seen by this cache
849 - tip-node: the node-id of the tip-most revision sen by this cache
850 - filtered-hash: the hash of all filtered revisions (before tip-rev)
851 ignored by this cache.
852 - obsolete-hash: the hash of all non-filtered obsolete revisions (before
853 tip-rev) ignored by this cache.
854
855 The tip-rev is used to know how far behind the value in the file are
856 compared to the current repository state.
857
858 The tip-node, filtered-hash and obsolete-hash are used to detect if this
859 cache can be used for this repository state at all.
860
861 The open/closed state is represented by a single letter 'o' or 'c'.
862 This field can be used to avoid changelog reads when determining if a
863 branch head closes a branch or not.
864
865 Topological heads are not included in the listing and should be dispatched
866 on the right branch at read time. Obsolete topological heads should be
867 ignored.
868 """
869
870 _base_filename = b"branch3-exp"
871 _default_key_hashes = (None, None)
872
873 def __init__(self, *args, pure_topo_branch=None, **kwargs):
874 super().__init__(*args, **kwargs)
875 self._pure_topo_branch = pure_topo_branch
876 self._needs_populate = self._pure_topo_branch is not None
877
878 def inherit_for(self, repo):
879 new = super().inherit_for(repo)
880 new._pure_topo_branch = self._pure_topo_branch
881 new._needs_populate = self._needs_populate
882 return new
883
884 def _get_topo_heads(self, repo):
885 """returns the topological head of a repoview content up to self.tiprev"""
886 cl = repo.changelog
887 if self.tiprev == nullrev:
888 return []
889 elif self.tiprev == cl.tiprev():
890 return cl.headrevs()
891 else:
892 # XXX passing tiprev as ceiling of cl.headrevs could be faster
893 heads = cl.headrevs(cl.revs(stop=self.tiprev))
894 return heads
895
896 def _write_header(self, fp) -> None:
897 cache_keys = {
898 b"tip-node": hex(self.tipnode),
899 b"tip-rev": b'%d' % self.tiprev,
900 }
901 if self.key_hashes:
902 if self.key_hashes[0] is not None:
903 cache_keys[b"filtered-hash"] = hex(self.key_hashes[0])
904 if self.key_hashes[1] is not None:
905 cache_keys[b"obsolete-hash"] = hex(self.key_hashes[1])
906 if self._pure_topo_branch is not None:
907 cache_keys[b"topo-mode"] = b"pure"
908 pieces = (b"%s=%s" % i for i in sorted(cache_keys.items()))
909 fp.write(b" ".join(pieces) + b'\n')
910 if self._pure_topo_branch is not None:
911 label = encoding.fromlocal(self._pure_topo_branch)
912 fp.write(label + b'\n')
913
914 def _write_heads(self, repo, fp) -> int:
915 """write list of heads to a file
916
917 Return the number of heads written."""
918 nodecount = 0
919 topo_heads = None
920 if self._pure_topo_branch is None:
921 topo_heads = set(self._get_topo_heads(repo))
922 to_rev = repo.changelog.index.rev
923 for label, nodes in sorted(self._entries.items()):
924 if label == self._pure_topo_branch:
925 # not need to write anything the header took care of that
926 continue
927 label = encoding.fromlocal(label)
928 for node in nodes:
929 if topo_heads is not None:
930 rev = to_rev(node)
931 if rev in topo_heads:
932 continue
933 if node in self._closednodes:
934 state = b'c'
935 else:
936 state = b'o'
937 nodecount += 1
938 fp.write(b"%s %s %s\n" % (hex(node), state, label))
939 return nodecount
940
941 @classmethod
942 def _load_header(cls, repo, lineiter):
943 header_line = next(lineiter)
944 pieces = header_line.rstrip(b'\n').split(b" ")
945 cache_keys = dict(p.split(b'=', 1) for p in pieces)
946
947 args = {}
948 filtered_hash = None
949 obsolete_hash = None
950 has_pure_topo_heads = False
951 for k, v in cache_keys.items():
952 if k == b"tip-rev":
953 args["tiprev"] = int(v)
954 elif k == b"tip-node":
955 args["tipnode"] = bin(v)
956 elif k == b"filtered-hash":
957 filtered_hash = bin(v)
958 elif k == b"obsolete-hash":
959 obsolete_hash = bin(v)
960 elif k == b"topo-mode":
961 if v == b"pure":
962 has_pure_topo_heads = True
963 else:
964 msg = b"unknown topo-mode: %r" % v
965 raise ValueError(msg)
966 else:
967 msg = b"unknown cache key: %r" % k
968 raise ValueError(msg)
969 args["key_hashes"] = (filtered_hash, obsolete_hash)
970 if has_pure_topo_heads:
971 pure_line = next(lineiter).rstrip(b'\n')
972 args["pure_topo_branch"] = encoding.tolocal(pure_line)
973 return args
974
975 def _load_heads(self, repo, lineiter):
976 """fully loads the branchcache by reading from the file using the line
977 iterator passed"""
978 super()._load_heads(repo, lineiter)
979 if self._pure_topo_branch is not None:
980 # no need to read the repository heads, we know their value already.
981 return
982 cl = repo.changelog
983 getbranchinfo = repo.revbranchcache().branchinfo
984 obsrevs = obsolete.getrevs(repo, b'obsolete')
985 to_node = cl.node
986 touched_branch = set()
987 for head in self._get_topo_heads(repo):
988 if head in obsrevs:
989 continue
990 node = to_node(head)
991 branch, closed = getbranchinfo(head)
992 self._entries.setdefault(branch, []).append(node)
993 if closed:
994 self._closednodes.add(node)
995 touched_branch.add(branch)
996 to_rev = cl.index.rev
997 for branch in touched_branch:
998 self._entries[branch].sort(key=to_rev)
999
1000 def _compute_key_hashes(self, repo) -> Tuple[bytes]:
1001 """return the cache key hashes that match this repoview state"""
1002 return scmutil.filtered_and_obsolete_hash(
1003 repo,
1004 self.tiprev,
590 1005 )
591 1006
592 duration = util.timer() - starttime
593 repo.ui.log(
594 b'branchcache',
595 b'updated %s in %.4f seconds\n',
596 _branchcachedesc(repo),
597 duration,
1007 def _process_new(
1008 self,
1009 repo,
1010 newbranches,
1011 new_closed,
1012 obs_ignored,
1013 max_rev,
1014 ) -> None:
1015 if (
1016 # note: the check about `obs_ignored` is too strict as the
1017 # obsolete revision could be non-topological, but lets keep
1018 # things simple for now
1019 #
1020 # The same apply to `new_closed` if the closed changeset are
1021 # not a head, we don't care that it is closed, but lets keep
1022 # things simple here too.
1023 not (obs_ignored or new_closed)
1024 and (
1025 not newbranches
1026 or (
1027 len(newbranches) == 1
1028 and (
1029 self.tiprev == nullrev
1030 or self._pure_topo_branch in newbranches
1031 )
1032 )
1033 )
1034 ):
1035 if newbranches:
1036 assert len(newbranches) == 1
1037 self._pure_topo_branch = list(newbranches.keys())[0]
1038 self._needs_populate = True
1039 self._entries.pop(self._pure_topo_branch, None)
1040 return
1041
1042 self._ensure_populated(repo)
1043 self._pure_topo_branch = None
1044 super()._process_new(
1045 repo,
1046 newbranches,
1047 new_closed,
1048 obs_ignored,
1049 max_rev,
598 1050 )
599 1051
600 self.write(repo)
1052 def _ensure_populated(self, repo):
1053 """make sure any lazily loaded values are fully populated"""
1054 if self._needs_populate:
1055 assert self._pure_topo_branch is not None
1056 cl = repo.changelog
1057 to_node = cl.node
1058 topo_heads = self._get_topo_heads(repo)
1059 heads = [to_node(r) for r in topo_heads]
1060 self._entries[self._pure_topo_branch] = heads
1061 self._needs_populate = False
1062
1063 def _detect_pure_topo(self, repo) -> None:
1064 if self._pure_topo_branch is not None:
1065 # we are pure topological already
1066 return
1067 to_node = repo.changelog.node
1068 topo_heads = [to_node(r) for r in self._get_topo_heads(repo)]
1069 if any(n in self._closednodes for n in topo_heads):
1070 return
1071 for branch, heads in self._entries.items():
1072 if heads == topo_heads:
1073 self._pure_topo_branch = branch
1074 break
601 1075
602 1076
603 class remotebranchcache(branchcache):
1077 class remotebranchcache(_BaseBranchCache):
604 1078 """Branchmap info for a remote connection, should not write locally"""
605 1079
606 def write(self, repo):
607 pass
1080 def __init__(
1081 self,
1082 repo: "localrepo.localrepository",
1083 entries: Union[
1084 Dict[bytes, List[bytes]], Iterable[Tuple[bytes, List[bytes]]]
1085 ] = (),
1086 closednodes: Optional[Set[bytes]] = None,
1087 ) -> None:
1088 super().__init__(repo=repo, entries=entries, closed_nodes=closednodes)
608 1089
609 1090
610 1091 # Revision branch info cache
@@ -1728,9 +1728,10 b' def writenewbundle('
1728 1728 caps = {}
1729 1729 if opts.get(b'obsolescence', False):
1730 1730 caps[b'obsmarkers'] = (b'V1',)
1731 if opts.get(b'streamv2'):
1731 stream_version = opts.get(b'stream', b"")
1732 if stream_version == b"v2":
1732 1733 caps[b'stream'] = [b'v2']
1733 elif opts.get(b'streamv3-exp'):
1734 elif stream_version == b"v3-exp":
1734 1735 caps[b'stream'] = [b'v3-exp']
1735 1736 bundle = bundle20(ui, caps)
1736 1737 bundle.setcompression(compression, compopts)
@@ -1774,10 +1775,10 b' def _addpartsfromopts(ui, repo, bundler,'
1774 1775 if repository.REPO_FEATURE_SIDE_DATA in repo.features:
1775 1776 part.addparam(b'exp-sidedata', b'1')
1776 1777
1777 if opts.get(b'streamv2', False):
1778 if opts.get(b'stream', b"") == b"v2":
1778 1779 addpartbundlestream2(bundler, repo, stream=True)
1779 1780
1780 if opts.get(b'streamv3-exp', False):
1781 if opts.get(b'stream', b"") == b"v3-exp":
1781 1782 addpartbundlestream2(bundler, repo, stream=True)
1782 1783
1783 1784 if opts.get(b'tagsfnodescache', True):
@@ -1787,7 +1788,7 b' def _addpartsfromopts(ui, repo, bundler,'
1787 1788 addpartrevbranchcache(repo, bundler, outgoing)
1788 1789
1789 1790 if opts.get(b'obsolescence', False):
1790 obsmarkers = repo.obsstore.relevantmarkers(outgoing.missing)
1791 obsmarkers = repo.obsstore.relevantmarkers(nodes=outgoing.missing)
1791 1792 buildobsmarkerspart(
1792 1793 bundler,
1793 1794 obsmarkers,
@@ -6,6 +6,8 b''
6 6 import collections
7 7
8 8 from typing import (
9 Dict,
10 Union,
9 11 cast,
10 12 )
11 13
@@ -106,7 +108,7 b' class bundlespec:'
106 108 }
107 109
108 110 # Maps bundle version with content opts to choose which part to bundle
109 _bundlespeccontentopts = {
111 _bundlespeccontentopts: Dict[bytes, Dict[bytes, Union[bool, bytes]]] = {
110 112 b'v1': {
111 113 b'changegroup': True,
112 114 b'cg.version': b'01',
@@ -136,7 +138,7 b' class bundlespec:'
136 138 b'cg.version': b'02',
137 139 b'obsolescence': False,
138 140 b'phases': False,
139 b"streamv2": True,
141 b"stream": b"v2",
140 142 b'tagsfnodescache': False,
141 143 b'revbranchcache': False,
142 144 },
@@ -145,7 +147,7 b' class bundlespec:'
145 147 b'cg.version': b'03',
146 148 b'obsolescence': False,
147 149 b'phases': False,
148 b"streamv3-exp": True,
150 b"stream": b"v3-exp",
149 151 b'tagsfnodescache': False,
150 152 b'revbranchcache': False,
151 153 },
@@ -158,8 +160,6 b' class bundlespec:'
158 160 }
159 161 _bundlespeccontentopts[b'bundle2'] = _bundlespeccontentopts[b'v2']
160 162
161 _bundlespecvariants = {b"streamv2": {}}
162
163 163 # Compression engines allowed in version 1. THIS SHOULD NEVER CHANGE.
164 164 _bundlespecv1compengines = {b'gzip', b'bzip2', b'none'}
165 165
@@ -391,10 +391,7 b' def isstreamclonespec(bundlespec):'
391 391 if (
392 392 bundlespec.wirecompression == b'UN'
393 393 and bundlespec.wireversion == b'02'
394 and (
395 bundlespec.contentopts.get(b'streamv2')
396 or bundlespec.contentopts.get(b'streamv3-exp')
397 )
394 and bundlespec.contentopts.get(b'stream', None) in (b"v2", b"v3-exp")
398 395 ):
399 396 return True
400 397
@@ -14,6 +14,8 b' def cachetocopy(srcrepo):'
14 14 # ones. Therefore copy all branch caches over.
15 15 cachefiles = [b'branch2']
16 16 cachefiles += [b'branch2-%s' % f for f in repoview.filtertable]
17 cachefiles += [b'branch3']
18 cachefiles += [b'branch3-%s' % f for f in repoview.filtertable]
17 19 cachefiles += [b'rbc-names-v1', b'rbc-revs-v1']
18 20 cachefiles += [b'tags2']
19 21 cachefiles += [b'tags2-%s' % f for f in repoview.filtertable]
@@ -327,6 +327,9 b' class changelog(revlog.revlog):'
327 327 self._filteredrevs_hashcache = {}
328 328 self._copiesstorage = opener.options.get(b'copies-storage')
329 329
330 def __contains__(self, rev):
331 return (0 <= rev < len(self)) and rev not in self._filteredrevs
332
330 333 @property
331 334 def filteredrevs(self):
332 335 return self._filteredrevs
@@ -35,11 +35,13 b' from .thirdparty import attr'
35 35
36 36 from . import (
37 37 bookmarks,
38 bundle2,
38 39 changelog,
39 40 copies,
40 41 crecord as crecordmod,
41 42 encoding,
42 43 error,
44 exchange,
43 45 formatter,
44 46 logcmdutil,
45 47 match as matchmod,
@@ -56,6 +58,7 b' from . import ('
56 58 rewriteutil,
57 59 scmutil,
58 60 state as statemod,
61 streamclone,
59 62 subrepoutil,
60 63 templatekw,
61 64 templater,
@@ -66,6 +69,7 b' from . import ('
66 69 from .utils import (
67 70 dateutil,
68 71 stringutil,
72 urlutil,
69 73 )
70 74
71 75 from .revlogutils import (
@@ -4135,3 +4139,90 b' def hgabortgraft(ui, repo):'
4135 4139 with repo.wlock():
4136 4140 graftstate = statemod.cmdstate(repo, b'graftstate')
4137 4141 return abortgraft(ui, repo, graftstate)
4142
4143
4144 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
4145 """Run after a changegroup has been added via pull/unbundle
4146
4147 This takes arguments below:
4148
4149 :modheads: change of heads by pull/unbundle
4150 :optupdate: updating working directory is needed or not
4151 :checkout: update destination revision (or None to default destination)
4152 :brev: a name, which might be a bookmark to be activated after updating
4153
4154 return True if update raise any conflict, False otherwise.
4155 """
4156 if modheads == 0:
4157 return False
4158 if optupdate:
4159 # avoid circular import
4160 from . import hg
4161
4162 try:
4163 return hg.updatetotally(ui, repo, checkout, brev)
4164 except error.UpdateAbort as inst:
4165 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
4166 hint = inst.hint
4167 raise error.UpdateAbort(msg, hint=hint)
4168 if ui.quiet:
4169 pass # we won't report anything so the other clause are useless.
4170 elif modheads is not None and modheads > 1:
4171 currentbranchheads = len(repo.branchheads())
4172 if currentbranchheads == modheads:
4173 ui.status(
4174 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
4175 )
4176 elif currentbranchheads > 1:
4177 ui.status(
4178 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
4179 )
4180 else:
4181 ui.status(_(b"(run 'hg heads' to see heads)\n"))
4182 elif not ui.configbool(b'commands', b'update.requiredest'):
4183 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
4184 return False
4185
4186
4187 def unbundle_files(ui, repo, fnames, unbundle_source=b'unbundle'):
4188 """utility for `hg unbundle` and `hg debug::unbundle`"""
4189 assert fnames
4190 # avoid circular import
4191 from . import hg
4192
4193 with repo.lock():
4194 for fname in fnames:
4195 f = hg.openpath(ui, fname)
4196 gen = exchange.readbundle(ui, f, fname)
4197 if isinstance(gen, streamclone.streamcloneapplier):
4198 raise error.InputError(
4199 _(
4200 b'packed bundles cannot be applied with '
4201 b'"hg unbundle"'
4202 ),
4203 hint=_(b'use "hg debugapplystreamclonebundle"'),
4204 )
4205 url = b'bundle:' + fname
4206 try:
4207 txnname = b'unbundle'
4208 if not isinstance(gen, bundle2.unbundle20):
4209 txnname = b'unbundle\n%s' % urlutil.hidepassword(url)
4210 with repo.transaction(txnname) as tr:
4211 op = bundle2.applybundle(
4212 repo,
4213 gen,
4214 tr,
4215 source=unbundle_source, # used by debug::unbundle
4216 url=url,
4217 )
4218 except error.BundleUnknownFeatureError as exc:
4219 raise error.Abort(
4220 _(b'%s: unknown bundle feature, %s') % (fname, exc),
4221 hint=_(
4222 b"see https://mercurial-scm.org/"
4223 b"wiki/BundleFeature for more "
4224 b"information"
4225 ),
4226 )
4227 modheads = bundle2.combinechangegroupresults(op)
4228 return modheads
@@ -60,7 +60,6 b' from . import ('
60 60 server,
61 61 shelve as shelvemod,
62 62 state as statemod,
63 streamclone,
64 63 tags as tagsmod,
65 64 ui as uimod,
66 65 util,
@@ -1627,6 +1626,8 b' def bundle(ui, repo, fname, *dests, **op'
1627 1626 pycompat.bytestr(e),
1628 1627 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1629 1628 )
1629
1630 has_changegroup = bundlespec.params.get(b"changegroup", False)
1630 1631 cgversion = bundlespec.params[b"cg.version"]
1631 1632
1632 1633 # Packed bundles are a pseudo bundle format for now.
@@ -1663,7 +1664,8 b' def bundle(ui, repo, fname, *dests, **op'
1663 1664 base = [nullrev]
1664 1665 else:
1665 1666 base = None
1666 if cgversion not in changegroup.supportedoutgoingversions(repo):
1667 supported_cg_versions = changegroup.supportedoutgoingversions(repo)
1668 if has_changegroup and cgversion not in supported_cg_versions:
1667 1669 raise error.Abort(
1668 1670 _(b"repository does not support bundle version %s") % cgversion
1669 1671 )
@@ -5375,44 +5377,6 b' def phase(ui, repo, *revs, **opts):'
5375 5377 return ret
5376 5378
5377 5379
5378 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5379 """Run after a changegroup has been added via pull/unbundle
5380
5381 This takes arguments below:
5382
5383 :modheads: change of heads by pull/unbundle
5384 :optupdate: updating working directory is needed or not
5385 :checkout: update destination revision (or None to default destination)
5386 :brev: a name, which might be a bookmark to be activated after updating
5387
5388 return True if update raise any conflict, False otherwise.
5389 """
5390 if modheads == 0:
5391 return False
5392 if optupdate:
5393 try:
5394 return hg.updatetotally(ui, repo, checkout, brev)
5395 except error.UpdateAbort as inst:
5396 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5397 hint = inst.hint
5398 raise error.UpdateAbort(msg, hint=hint)
5399 if modheads is not None and modheads > 1:
5400 currentbranchheads = len(repo.branchheads())
5401 if currentbranchheads == modheads:
5402 ui.status(
5403 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5404 )
5405 elif currentbranchheads > 1:
5406 ui.status(
5407 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5408 )
5409 else:
5410 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5411 elif not ui.configbool(b'commands', b'update.requiredest'):
5412 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5413 return False
5414
5415
5416 5380 @command(
5417 5381 b'pull',
5418 5382 [
@@ -5608,7 +5572,7 b' def pull(ui, repo, *sources, **opts):'
5608 5572 # for pushes.
5609 5573 repo._subtoppath = path.loc
5610 5574 try:
5611 update_conflict = postincoming(
5575 update_conflict = cmdutil.postincoming(
5612 5576 ui, repo, modheads, opts.get('update'), checkout, brev
5613 5577 )
5614 5578 except error.FilteredRepoLookupError as exc:
@@ -7730,7 +7694,7 b' def tip(ui, repo, **opts):'
7730 7694 _(b'[-u] FILE...'),
7731 7695 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7732 7696 )
7733 def unbundle(ui, repo, fname1, *fnames, _unbundle_source=b'unbundle', **opts):
7697 def unbundle(ui, repo, fname1, *fnames, **opts):
7734 7698 """apply one or more bundle files
7735 7699
7736 7700 Apply one or more bundle files generated by :hg:`bundle`.
@@ -7738,44 +7702,9 b' def unbundle(ui, repo, fname1, *fnames, '
7738 7702 Returns 0 on success, 1 if an update has unresolved files.
7739 7703 """
7740 7704 fnames = (fname1,) + fnames
7741
7742 with repo.lock():
7743 for fname in fnames:
7744 f = hg.openpath(ui, fname)
7745 gen = exchange.readbundle(ui, f, fname)
7746 if isinstance(gen, streamclone.streamcloneapplier):
7747 raise error.InputError(
7748 _(
7749 b'packed bundles cannot be applied with '
7750 b'"hg unbundle"'
7751 ),
7752 hint=_(b'use "hg debugapplystreamclonebundle"'),
7753 )
7754 url = b'bundle:' + fname
7755 try:
7756 txnname = b'unbundle'
7757 if not isinstance(gen, bundle2.unbundle20):
7758 txnname = b'unbundle\n%s' % urlutil.hidepassword(url)
7759 with repo.transaction(txnname) as tr:
7760 op = bundle2.applybundle(
7761 repo,
7762 gen,
7763 tr,
7764 source=_unbundle_source, # used by debug::unbundle
7765 url=url,
7766 )
7767 except error.BundleUnknownFeatureError as exc:
7768 raise error.Abort(
7769 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7770 hint=_(
7771 b"see https://mercurial-scm.org/"
7772 b"wiki/BundleFeature for more "
7773 b"information"
7774 ),
7775 )
7776 modheads = bundle2.combinechangegroupresults(op)
7777
7778 if postincoming(ui, repo, modheads, opts.get('update'), None, None):
7705 modheads = cmdutil.unbundle_files(ui, repo, fnames)
7706
7707 if cmdutil.postincoming(ui, repo, modheads, opts.get('update'), None, None):
7779 7708 return 1
7780 7709 else:
7781 7710 return 0
@@ -719,6 +719,15 b' section = "experimental"'
719 719 name = "auto-publish"
720 720 default = "publish"
721 721
722
723 # The current implementation of the filtering/injecting of topological heads is
724 # naive and need proper benchmark and optimisation because we can envision
725 # moving the the v3 of the branch-cache format out of experimental
726 [[items]]
727 section = "experimental"
728 name = "branch-cache-v3"
729 default = false
730
722 731 [[items]]
723 732 section = "experimental"
724 733 name = "bundle-phases"
@@ -4078,26 +4078,17 b' def debugupgraderepo(ui, repo, run=False'
4078 4078
4079 4079 @command(
4080 4080 b'debug::unbundle',
4081 [
4082 (
4083 b'u',
4084 b'update',
4085 None,
4086 _(b'update to new branch head if changesets were unbundled'),
4087 )
4088 ],
4089 _(b'[-u] FILE...'),
4081 [],
4082 _(b'FILE...'),
4090 4083 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4091 4084 )
4092 def debugunbundle(ui, repo, *args, **kwargs):
4085 def debugunbundle(ui, repo, fname1, *fnames):
4093 4086 """same as `hg unbundle`, but pretent to come from a push
4094 4087
4095 4088 This is useful to debug behavior and performance change in this case.
4096 4089 """
4097 from . import commands # avoid cycle
4098
4099 unbundle = cmdutil.findcmd(b'unbundle', commands.table)[1][0]
4100 return unbundle(ui, repo, *args, _unbundle_source=b'push', **kwargs)
4090 fnames = (fname1,) + fnames
4091 cmdutil.unbundle_files(ui, repo, fnames)
4101 4092
4102 4093
4103 4094 @command(
@@ -1639,16 +1639,6 b' class dirstate:'
1639 1639
1640 1640 use_rust = True
1641 1641
1642 allowed_matchers = (
1643 matchmod.alwaysmatcher,
1644 matchmod.differencematcher,
1645 matchmod.exactmatcher,
1646 matchmod.includematcher,
1647 matchmod.intersectionmatcher,
1648 matchmod.nevermatcher,
1649 matchmod.unionmatcher,
1650 )
1651
1652 1642 if rustmod is None:
1653 1643 use_rust = False
1654 1644 elif self._checkcase:
@@ -1656,9 +1646,6 b' class dirstate:'
1656 1646 use_rust = False
1657 1647 elif subrepos:
1658 1648 use_rust = False
1659 elif not isinstance(match, allowed_matchers):
1660 # Some matchers have yet to be implemented
1661 use_rust = False
1662 1649
1663 1650 # Get the time from the filesystem so we can disambiguate files that
1664 1651 # appear modified in the present or future.
@@ -18,6 +18,7 b' from . import ('
18 18 bookmarks,
19 19 branchmap,
20 20 error,
21 node as nodemod,
21 22 obsolete,
22 23 phases,
23 24 pycompat,
@@ -98,29 +99,62 b' class outgoing:'
98 99 def __init__(
99 100 self, repo, commonheads=None, ancestorsof=None, missingroots=None
100 101 ):
101 # at least one of them must not be set
102 assert None in (commonheads, missingroots)
102 # at most one of them must not be set
103 if commonheads is not None and missingroots is not None:
104 m = 'commonheads and missingroots arguments are mutually exclusive'
105 raise error.ProgrammingError(m)
103 106 cl = repo.changelog
107 unfi = repo.unfiltered()
108 ucl = unfi.changelog
109 to_node = ucl.node
110 missing = None
111 common = None
112 arg_anc = ancestorsof
104 113 if ancestorsof is None:
105 114 ancestorsof = cl.heads()
106 if missingroots:
115
116 # XXX-perf: do we need all this to be node-list? They would be simpler
117 # as rev-num sets (and smartset)
118 if missingroots == [nodemod.nullrev] or missingroots == []:
119 commonheads = [repo.nullid]
120 common = set()
121 if arg_anc is None:
122 missing = [to_node(r) for r in cl]
123 else:
124 missing_rev = repo.revs('::%ln', missingroots, ancestorsof)
125 missing = [to_node(r) for r in missing_rev]
126 elif missingroots is not None:
107 127 # TODO remove call to nodesbetween.
108 # TODO populate attributes on outgoing instance instead of setting
109 # discbases.
110 csets, roots, heads = cl.nodesbetween(missingroots, ancestorsof)
111 included = set(csets)
112 discbases = []
113 for n in csets:
114 discbases.extend([p for p in cl.parents(n) if p != repo.nullid])
115 ancestorsof = heads
116 commonheads = [n for n in discbases if n not in included]
128 missing_rev = repo.revs('%ln::%ln', missingroots, ancestorsof)
129 ancestorsof = [to_node(r) for r in ucl.headrevs(missing_rev)]
130 parent_revs = ucl.parentrevs
131 common_legs = set()
132 for r in missing_rev:
133 p1, p2 = parent_revs(r)
134 if p1 not in missing_rev:
135 common_legs.add(p1)
136 if p2 not in missing_rev:
137 common_legs.add(p2)
138 common_legs.discard(nodemod.nullrev)
139 if not common_legs:
140 commonheads = [repo.nullid]
141 common = set()
142 else:
143 commonheads_revs = unfi.revs(
144 'heads(%ld::%ld)',
145 common_legs,
146 common_legs,
147 )
148 commonheads = [to_node(r) for r in commonheads_revs]
149 common = ucl.ancestors(commonheads_revs, inclusive=True)
150 missing = [to_node(r) for r in missing_rev]
117 151 elif not commonheads:
118 152 commonheads = [repo.nullid]
119 153 self.commonheads = commonheads
120 154 self.ancestorsof = ancestorsof
121 155 self._revlog = cl
122 self._common = None
123 self._missing = None
156 self._common = common
157 self._missing = missing
124 158 self.excluded = []
125 159
126 160 def _computecommonmissing(self):
@@ -190,7 +224,12 b' def findcommonoutgoing('
190 224 if len(missing) == len(allmissing):
191 225 ancestorsof = onlyheads
192 226 else: # update missing heads
193 ancestorsof = phases.newheads(repo, onlyheads, excluded)
227 to_rev = repo.changelog.index.rev
228 to_node = repo.changelog.node
229 excluded_revs = [to_rev(r) for r in excluded]
230 onlyheads_revs = [to_rev(r) for r in onlyheads]
231 new_heads = phases.new_heads(repo, onlyheads_revs, excluded_revs)
232 ancestorsof = [to_node(r) for r in new_heads]
194 233 og.ancestorsof = ancestorsof
195 234 if portable:
196 235 # recompute common and ancestorsof as if -r<rev> had been given for
@@ -344,32 +344,56 b' class pushoperation:'
344 344 # not target to push, all common are relevant
345 345 return self.outgoing.commonheads
346 346 unfi = self.repo.unfiltered()
347 # I want cheads = heads(::ancestorsof and ::commonheads)
348 # (ancestorsof is revs with secret changeset filtered out)
347 # I want cheads = heads(::push_heads and ::commonheads)
348 #
349 # To push, we already computed
350 # common = (::commonheads)
351 # missing = ((commonheads::push_heads) - commonheads)
352 #
353 # So we basically search
349 354 #
350 # This can be expressed as:
351 # cheads = ( (ancestorsof and ::commonheads)
352 # + (commonheads and ::ancestorsof))"
353 # )
355 # almost_heads = heads((parents(missing) + push_heads) & common)
354 356 #
355 # while trying to push we already computed the following:
356 # common = (::commonheads)
357 # missing = ((commonheads::ancestorsof) - commonheads)
357 # We use "almost" here as this can return revision that are ancestors
358 # of other in the set and we need to explicitly turn it into an
359 # antichain later. We can do so using:
360 #
361 # cheads = heads(almost_heads::almost_heads)
358 362 #
359 # We can pick:
360 # * ancestorsof part of common (::commonheads)
363 # In pratice the code is a bit more convulted to avoid some extra
364 # computation. It aims at doing the same computation as highlighted
365 # above however.
361 366 common = self.outgoing.common
362 rev = self.repo.changelog.index.rev
363 cheads = [node for node in self.revs if rev(node) in common]
364 # and
365 # * commonheads parents on missing
366 revset = unfi.set(
367 b'%ln and parents(roots(%ln))',
368 self.outgoing.commonheads,
369 self.outgoing.missing,
370 )
371 cheads.extend(c.node() for c in revset)
372 return cheads
367 unfi = self.repo.unfiltered()
368 cl = unfi.changelog
369 to_rev = cl.index.rev
370 to_node = cl.node
371 parent_revs = cl.parentrevs
372 unselected = []
373 cheads = set()
374 # XXX-perf: `self.revs` and `outgoing.missing` could hold revs directly
375 for n in self.revs:
376 r = to_rev(n)
377 if r in common:
378 cheads.add(r)
379 else:
380 unselected.append(r)
381 known_non_heads = cl.ancestors(cheads, inclusive=True)
382 if unselected:
383 missing_revs = {to_rev(n) for n in self.outgoing.missing}
384 missing_revs.add(nullrev)
385 root_points = set()
386 for r in missing_revs:
387 p1, p2 = parent_revs(r)
388 if p1 not in missing_revs and p1 not in known_non_heads:
389 root_points.add(p1)
390 if p2 not in missing_revs and p2 not in known_non_heads:
391 root_points.add(p2)
392 if root_points:
393 heads = unfi.revs('heads(%ld::%ld)', root_points, root_points)
394 cheads.update(heads)
395 # XXX-perf: could this be a set of revision?
396 return [to_node(r) for r in sorted(cheads)]
373 397
374 398 @property
375 399 def commonheads(self):
@@ -600,7 +624,10 b' def _pushdiscoveryphase(pushop):'
600 624
601 625 (computed for both success and failure case for changesets push)"""
602 626 outgoing = pushop.outgoing
603 unfi = pushop.repo.unfiltered()
627 repo = pushop.repo
628 unfi = repo.unfiltered()
629 cl = unfi.changelog
630 to_rev = cl.index.rev
604 631 remotephases = listkeys(pushop.remote, b'phases')
605 632
606 633 if (
@@ -622,38 +649,43 b' def _pushdiscoveryphase(pushop):'
622 649 pushop.fallbackoutdatedphases = []
623 650 return
624 651
625 pushop.remotephases = phases.remotephasessummary(
626 pushop.repo, pushop.fallbackheads, remotephases
652 fallbackheads_rev = {to_rev(n) for n in pushop.fallbackheads}
653 pushop.remotephases = phases.RemotePhasesSummary(
654 pushop.repo,
655 fallbackheads_rev,
656 remotephases,
627 657 )
628 droots = pushop.remotephases.draftroots
629
630 extracond = b''
631 if not pushop.remotephases.publishing:
632 extracond = b' and public()'
633 revset = b'heads((%%ln::%%ln) %s)' % extracond
634 # Get the list of all revs draft on remote by public here.
635 # XXX Beware that revset break if droots is not strictly
636 # XXX root we may want to ensure it is but it is costly
637 fallback = list(unfi.set(revset, droots, pushop.fallbackheads))
638 if not pushop.remotephases.publishing and pushop.publish:
639 future = list(
640 unfi.set(
641 b'%ln and (not public() or %ln::)', pushop.futureheads, droots
642 )
643 )
644 elif not outgoing.missing:
645 future = fallback
658 droots = set(pushop.remotephases.draft_roots)
659
660 fallback_publishing = pushop.remotephases.publishing
661 push_publishing = pushop.remotephases.publishing or pushop.publish
662 missing_revs = {to_rev(n) for n in outgoing.missing}
663 drafts = unfi._phasecache.get_raw_set(unfi, phases.draft)
664
665 if fallback_publishing:
666 fallback_roots = droots - missing_revs
667 revset = b'heads(%ld::%ld)'
646 668 else:
647 # adds changeset we are going to push as draft
648 #
649 # should not be necessary for publishing server, but because of an
650 # issue fixed in xxxxx we have to do it anyway.
651 fdroots = list(
652 unfi.set(b'roots(%ln + %ln::)', outgoing.missing, droots)
653 )
654 fdroots = [f.node() for f in fdroots]
655 future = list(unfi.set(revset, fdroots, pushop.futureheads))
656 pushop.outdatedphases = future
669 fallback_roots = droots - drafts
670 fallback_roots -= missing_revs
671 # Get the list of all revs draft on remote but public here.
672 revset = b'heads((%ld::%ld) and public())'
673 if not fallback_roots:
674 fallback = fallback_rev = []
675 else:
676 fallback_rev = unfi.revs(revset, fallback_roots, fallbackheads_rev)
677 fallback = [repo[r] for r in fallback_rev]
678
679 if push_publishing:
680 published = missing_revs.copy()
681 else:
682 published = missing_revs - drafts
683 if pushop.publish:
684 published.update(fallbackheads_rev & drafts)
685 elif fallback:
686 published.update(fallback_rev)
687
688 pushop.outdatedphases = [repo[r] for r in cl.headrevs(published)]
657 689 pushop.fallbackoutdatedphases = fallback
658 690
659 691
@@ -671,8 +703,8 b' def _pushdiscoveryobsmarkers(pushop):'
671 703 repo = pushop.repo
672 704 # very naive computation, that can be quite expensive on big repo.
673 705 # However: evolution is currently slow on them anyway.
674 nodes = (c.node() for c in repo.set(b'::%ln', pushop.futureheads))
675 pushop.outobsmarkers = pushop.repo.obsstore.relevantmarkers(nodes)
706 revs = repo.revs(b'::%ln', pushop.futureheads)
707 pushop.outobsmarkers = pushop.repo.obsstore.relevantmarkers(revs=revs)
676 708
677 709
678 710 @pushdiscovery(b'bookmarks')
@@ -888,8 +920,13 b' def _pushb2checkphases(pushop, bundler):'
888 920 if pushop.remotephases is not None and hasphaseheads:
889 921 # check that the remote phase has not changed
890 922 checks = {p: [] for p in phases.allphases}
891 checks[phases.public].extend(pushop.remotephases.publicheads)
892 checks[phases.draft].extend(pushop.remotephases.draftroots)
923 to_node = pushop.repo.unfiltered().changelog.node
924 checks[phases.public].extend(
925 to_node(r) for r in pushop.remotephases.public_heads
926 )
927 checks[phases.draft].extend(
928 to_node(r) for r in pushop.remotephases.draft_roots
929 )
893 930 if any(checks.values()):
894 931 for phase in checks:
895 932 checks[phase].sort()
@@ -1293,8 +1330,16 b' def _pushsyncphase(pushop):'
1293 1330 _localphasemove(pushop, cheads)
1294 1331 # don't push any phase data as there is nothing to push
1295 1332 else:
1296 ana = phases.analyzeremotephases(pushop.repo, cheads, remotephases)
1297 pheads, droots = ana
1333 unfi = pushop.repo.unfiltered()
1334 to_rev = unfi.changelog.index.rev
1335 to_node = unfi.changelog.node
1336 cheads_revs = [to_rev(n) for n in cheads]
1337 pheads_revs, _dr = phases.analyze_remote_phases(
1338 pushop.repo,
1339 cheads_revs,
1340 remotephases,
1341 )
1342 pheads = [to_node(r) for r in pheads_revs]
1298 1343 ### Apply remote phase on local
1299 1344 if remotephases.get(b'publishing', False):
1300 1345 _localphasemove(pushop, cheads)
@@ -2048,10 +2093,17 b' def _pullapplyphases(pullop, remotephase'
2048 2093 pullop.stepsdone.add(b'phases')
2049 2094 publishing = bool(remotephases.get(b'publishing', False))
2050 2095 if remotephases and not publishing:
2096 unfi = pullop.repo.unfiltered()
2097 to_rev = unfi.changelog.index.rev
2098 to_node = unfi.changelog.node
2099 pulledsubset_revs = [to_rev(n) for n in pullop.pulledsubset]
2051 2100 # remote is new and non-publishing
2052 pheads, _dr = phases.analyzeremotephases(
2053 pullop.repo, pullop.pulledsubset, remotephases
2101 pheads_revs, _dr = phases.analyze_remote_phases(
2102 pullop.repo,
2103 pulledsubset_revs,
2104 remotephases,
2054 2105 )
2106 pheads = [to_node(r) for r in pheads_revs]
2055 2107 dheads = pullop.pulledsubset
2056 2108 else:
2057 2109 # Remote is old or publishing all common changesets
@@ -2553,8 +2605,8 b' def _getbundleobsmarkerpart('
2553 2605 if kwargs.get('obsmarkers', False):
2554 2606 if heads is None:
2555 2607 heads = repo.heads()
2556 subset = [c.node() for c in repo.set(b'::%ln', heads)]
2557 markers = repo.obsstore.relevantmarkers(subset)
2608 revs = repo.revs(b'::%ln', heads)
2609 markers = repo.obsstore.relevantmarkers(revs=revs)
2558 2610 markers = obsutil.sortedmarkers(markers)
2559 2611 bundle2.buildobsmarkerspart(bundler, markers)
2560 2612
@@ -54,6 +54,8 b' CACHE_BRANCHMAP_ALL = b"branchmap-all"'
54 54 CACHE_BRANCHMAP_SERVED = b"branchmap-served"
55 55 # Warm internal changelog cache (eg: persistent nodemap)
56 56 CACHE_CHANGELOG_CACHE = b"changelog-cache"
57 # check of a branchmap can use the "pure topo" mode
58 CACHE_BRANCHMAP_DETECT_PURE_TOPO = b"branchmap-detect-pure-topo"
57 59 # Warm full manifest cache
58 60 CACHE_FULL_MANIFEST = b"full-manifest"
59 61 # Warm file-node-tags cache
@@ -78,6 +80,7 b' CACHES_DEFAULT = {'
78 80 CACHES_ALL = {
79 81 CACHE_BRANCHMAP_SERVED,
80 82 CACHE_BRANCHMAP_ALL,
83 CACHE_BRANCHMAP_DETECT_PURE_TOPO,
81 84 CACHE_CHANGELOG_CACHE,
82 85 CACHE_FILE_NODE_TAGS,
83 86 CACHE_FULL_MANIFEST,
@@ -2923,12 +2923,14 b' class localrepository:'
2923 2923
2924 2924 if repository.CACHE_BRANCHMAP_SERVED in caches:
2925 2925 if tr is None or tr.changes[b'origrepolen'] < len(self):
2926 # accessing the 'served' branchmap should refresh all the others,
2927 2926 self.ui.debug(b'updating the branch cache\n')
2928 self.filtered(b'served').branchmap()
2929 self.filtered(b'served.hidden').branchmap()
2930 # flush all possibly delayed write.
2931 self._branchcaches.write_delayed(self)
2927 dpt = repository.CACHE_BRANCHMAP_DETECT_PURE_TOPO in caches
2928 served = self.filtered(b'served')
2929 self._branchcaches.update_disk(served, detect_pure_topo=dpt)
2930 served_hidden = self.filtered(b'served.hidden')
2931 self._branchcaches.update_disk(
2932 served_hidden, detect_pure_topo=dpt
2933 )
2932 2934
2933 2935 if repository.CACHE_CHANGELOG_CACHE in caches:
2934 2936 self.changelog.update_caches(transaction=tr)
@@ -2957,7 +2959,7 b' class localrepository:'
2957 2959
2958 2960 if repository.CACHE_FILE_NODE_TAGS in caches:
2959 2961 # accessing fnode cache warms the cache
2960 tagsmod.fnoderevs(self.ui, unfi, unfi.changelog.revs())
2962 tagsmod.warm_cache(self)
2961 2963
2962 2964 if repository.CACHE_TAGS_DEFAULT in caches:
2963 2965 # accessing tags warm the cache
@@ -2971,9 +2973,14 b' class localrepository:'
2971 2973 # even if they haven't explicitly been requested yet (if they've
2972 2974 # never been used by hg, they won't ever have been written, even if
2973 2975 # they're a subset of another kind of cache that *has* been used).
2976 dpt = repository.CACHE_BRANCHMAP_DETECT_PURE_TOPO in caches
2977
2974 2978 for filt in repoview.filtertable.keys():
2975 2979 filtered = self.filtered(filt)
2976 filtered.branchmap().write(filtered)
2980 self._branchcaches.update_disk(filtered, detect_pure_topo=dpt)
2981
2982 # flush all possibly delayed write.
2983 self._branchcaches.write_dirty(self)
2977 2984
2978 2985 def invalidatecaches(self):
2979 2986 if '_tagscache' in vars(self):
@@ -395,9 +395,18 b' def _donormalize(patterns, default, root'
395 395
396 396 class basematcher:
397 397 def __init__(self, badfn=None):
398 self._was_tampered_with = False
398 399 if badfn is not None:
399 400 self.bad = badfn
400 401
402 def was_tampered_with_nonrec(self):
403 # [_was_tampered_with] is used to track if when extensions changed the matcher
404 # behavior (crazy stuff!), so we disable the rust fast path.
405 return self._was_tampered_with
406
407 def was_tampered_with(self):
408 return self.was_tampered_with_nonrec()
409
401 410 def __call__(self, fn):
402 411 return self.matchfn(fn)
403 412
@@ -638,6 +647,11 b' class patternmatcher(basematcher):'
638 647 super(patternmatcher, self).__init__(badfn)
639 648 kindpats.sort()
640 649
650 if rustmod is not None:
651 # We need to pass the patterns to Rust because they can contain
652 # patterns from the user interface
653 self._kindpats = kindpats
654
641 655 roots, dirs, parents = _rootsdirsandparents(kindpats)
642 656 self._files = _explicitfiles(kindpats)
643 657 self._dirs_explicit = set(dirs)
@@ -880,6 +894,13 b' class differencematcher(basematcher):'
880 894 self.bad = m1.bad
881 895 self.traversedir = m1.traversedir
882 896
897 def was_tampered_with(self):
898 return (
899 self.was_tampered_with_nonrec()
900 or self._m1.was_tampered_with()
901 or self._m2.was_tampered_with()
902 )
903
883 904 def matchfn(self, f):
884 905 return self._m1(f) and not self._m2(f)
885 906
@@ -963,6 +984,13 b' class intersectionmatcher(basematcher):'
963 984 self.bad = m1.bad
964 985 self.traversedir = m1.traversedir
965 986
987 def was_tampered_with(self):
988 return (
989 self.was_tampered_with_nonrec()
990 or self._m1.was_tampered_with()
991 or self._m2.was_tampered_with()
992 )
993
966 994 @propertycache
967 995 def _files(self):
968 996 if self.isexact():
@@ -1060,6 +1088,11 b' class subdirmatcher(basematcher):'
1060 1088 if matcher.prefix():
1061 1089 self._always = any(f == path for f in matcher._files)
1062 1090
1091 def was_tampered_with(self):
1092 return (
1093 self.was_tampered_with_nonrec() or self._matcher.was_tampered_with()
1094 )
1095
1063 1096 def bad(self, f, msg):
1064 1097 self._matcher.bad(self._path + b"/" + f, msg)
1065 1098
@@ -1194,6 +1227,11 b' class unionmatcher(basematcher):'
1194 1227 self.traversedir = m1.traversedir
1195 1228 self._matchers = matchers
1196 1229
1230 def was_tampered_with(self):
1231 return self.was_tampered_with_nonrec() or any(
1232 map(lambda m: m.was_tampered_with(), self._matchers)
1233 )
1234
1197 1235 def matchfn(self, f):
1198 1236 for match in self._matchers:
1199 1237 if match(f):
@@ -771,10 +771,11 b' class obsstore:'
771 771 _addchildren(self.children, markers)
772 772 _checkinvalidmarkers(self.repo, markers)
773 773
774 def relevantmarkers(self, nodes):
775 """return a set of all obsolescence markers relevant to a set of nodes.
774 def relevantmarkers(self, nodes=None, revs=None):
775 """return a set of all obsolescence markers relevant to a set of
776 nodes or revisions.
776 777
777 "relevant" to a set of nodes mean:
778 "relevant" to a set of nodes or revisions mean:
778 779
779 780 - marker that use this changeset as successor
780 781 - prune marker of direct children on this changeset
@@ -782,8 +783,21 b' class obsstore:'
782 783 markers
783 784
784 785 It is a set so you cannot rely on order."""
786 if nodes is None:
787 nodes = set()
788 if revs is None:
789 revs = set()
785 790
786 pendingnodes = set(nodes)
791 get_rev = self.repo.unfiltered().changelog.index.get_rev
792 pendingnodes = set()
793 for marker in self._all:
794 for node in (marker[0],) + marker[1] + (marker[5] or ()):
795 if node in nodes:
796 pendingnodes.add(node)
797 elif revs:
798 rev = get_rev(node)
799 if rev is not None and rev in revs:
800 pendingnodes.add(node)
787 801 seenmarkers = set()
788 802 seennodes = set(pendingnodes)
789 803 precursorsmarkers = self.predecessors
@@ -818,7 +832,7 b' def makestore(ui, repo):'
818 832 store = obsstore(repo, repo.svfs, readonly=readonly, **kwargs)
819 833 if store and readonly:
820 834 ui.warn(
821 _(b'obsolete feature not enabled but %i markers found!\n')
835 _(b'"obsolete" feature not enabled but %i markers found!\n')
822 836 % len(list(store))
823 837 )
824 838 return store
@@ -108,7 +108,7 b' def getmarkers(repo, nodes=None, exclusi'
108 108 elif exclusive:
109 109 rawmarkers = exclusivemarkers(repo, nodes)
110 110 else:
111 rawmarkers = repo.obsstore.relevantmarkers(nodes)
111 rawmarkers = repo.obsstore.relevantmarkers(nodes=nodes)
112 112
113 113 for markerdata in rawmarkers:
114 114 yield marker(repo, markerdata)
@@ -180,6 +180,13 b' class pathauditor:'
180 180 self.auditeddir.clear()
181 181 self._cached = False
182 182
183 def clear_audit_cache(self):
184 """reset all audit cache
185
186 intended for debug and performance benchmark purposes"""
187 self.audited.clear()
188 self.auditeddir.clear()
189
183 190
184 191 def canonpath(
185 192 root: bytes,
@@ -109,6 +109,7 b' import weakref'
109 109 from typing import (
110 110 Any,
111 111 Callable,
112 Collection,
112 113 Dict,
113 114 Iterable,
114 115 List,
@@ -127,7 +128,6 b' from .node import ('
127 128 )
128 129 from . import (
129 130 error,
130 pycompat,
131 131 requirements,
132 132 smartset,
133 133 txnutil,
@@ -414,6 +414,27 b' class phasecache:'
414 414 ]
415 415 )
416 416
417 def get_raw_set(
418 self,
419 repo: "localrepo.localrepository",
420 phase: int,
421 ) -> Set[int]:
422 """return the set of revision in that phase
423
424 The returned set is not filtered and might contains revision filtered
425 for the passed repoview.
426
427 The returned set might be the internal one and MUST NOT be mutated to
428 avoid side effect.
429 """
430 if phase == public:
431 raise error.ProgrammingError("cannot get_set for public phase")
432 self._ensure_phase_sets(repo.unfiltered())
433 revs = self._phasesets.get(phase)
434 if revs is None:
435 return set()
436 return revs
437
417 438 def getrevset(
418 439 self,
419 440 repo: "localrepo.localrepository",
@@ -1095,7 +1116,11 b' def updatephases(repo, trgetter, headsby'
1095 1116 advanceboundary(repo, trgetter(), phase, heads)
1096 1117
1097 1118
1098 def analyzeremotephases(repo, subset, roots):
1119 def analyze_remote_phases(
1120 repo,
1121 subset: Collection[int],
1122 roots: Dict[bytes, bytes],
1123 ) -> Tuple[Collection[int], Collection[int]]:
1099 1124 """Compute phases heads and root in a subset of node from root dict
1100 1125
1101 1126 * subset is heads of the subset
@@ -1105,8 +1130,8 b' def analyzeremotephases(repo, subset, ro'
1105 1130 """
1106 1131 repo = repo.unfiltered()
1107 1132 # build list from dictionary
1108 draftroots = []
1109 has_node = repo.changelog.index.has_node # to filter unknown nodes
1133 draft_roots = []
1134 to_rev = repo.changelog.index.get_rev
1110 1135 for nhex, phase in roots.items():
1111 1136 if nhex == b'publishing': # ignore data related to publish option
1112 1137 continue
@@ -1114,49 +1139,53 b' def analyzeremotephases(repo, subset, ro'
1114 1139 phase = int(phase)
1115 1140 if phase == public:
1116 1141 if node != repo.nullid:
1117 repo.ui.warn(
1118 _(
1119 b'ignoring inconsistent public root'
1120 b' from remote: %s\n'
1121 )
1122 % nhex
1123 )
1142 msg = _(b'ignoring inconsistent public root from remote: %s\n')
1143 repo.ui.warn(msg % nhex)
1124 1144 elif phase == draft:
1125 if has_node(node):
1126 draftroots.append(node)
1145 rev = to_rev(node)
1146 if rev is not None: # to filter unknown nodes
1147 draft_roots.append(rev)
1127 1148 else:
1128 repo.ui.warn(
1129 _(b'ignoring unexpected root from remote: %i %s\n')
1130 % (phase, nhex)
1131 )
1149 msg = _(b'ignoring unexpected root from remote: %i %s\n')
1150 repo.ui.warn(msg % (phase, nhex))
1132 1151 # compute heads
1133 publicheads = newheads(repo, subset, draftroots)
1134 return publicheads, draftroots
1152 public_heads = new_heads(repo, subset, draft_roots)
1153 return public_heads, draft_roots
1135 1154
1136 1155
1137 class remotephasessummary:
1156 class RemotePhasesSummary:
1138 1157 """summarize phase information on the remote side
1139 1158
1140 1159 :publishing: True is the remote is publishing
1141 :publicheads: list of remote public phase heads (nodes)
1142 :draftheads: list of remote draft phase heads (nodes)
1143 :draftroots: list of remote draft phase root (nodes)
1160 :public_heads: list of remote public phase heads (revs)
1161 :draft_heads: list of remote draft phase heads (revs)
1162 :draft_roots: list of remote draft phase root (revs)
1144 1163 """
1145 1164
1146 def __init__(self, repo, remotesubset, remoteroots):
1165 def __init__(
1166 self,
1167 repo,
1168 remote_subset: Collection[int],
1169 remote_roots: Dict[bytes, bytes],
1170 ):
1147 1171 unfi = repo.unfiltered()
1148 self._allremoteroots = remoteroots
1149
1150 self.publishing = remoteroots.get(b'publishing', False)
1172 self._allremoteroots: Dict[bytes, bytes] = remote_roots
1151 1173
1152 ana = analyzeremotephases(repo, remotesubset, remoteroots)
1153 self.publicheads, self.draftroots = ana
1174 self.publishing: bool = bool(remote_roots.get(b'publishing', False))
1175
1176 heads, roots = analyze_remote_phases(repo, remote_subset, remote_roots)
1177 self.public_heads: Collection[int] = heads
1178 self.draft_roots: Collection[int] = roots
1154 1179 # Get the list of all "heads" revs draft on remote
1155 dheads = unfi.set(b'heads(%ln::%ln)', self.draftroots, remotesubset)
1156 self.draftheads = [c.node() for c in dheads]
1180 dheads = unfi.revs(b'heads(%ld::%ld)', roots, remote_subset)
1181 self.draft_heads: Collection[int] = dheads
1157 1182
1158 1183
1159 def newheads(repo, heads, roots):
1184 def new_heads(
1185 repo,
1186 heads: Collection[int],
1187 roots: Collection[int],
1188 ) -> Collection[int]:
1160 1189 """compute new head of a subset minus another
1161 1190
1162 1191 * `heads`: define the first subset
@@ -1165,16 +1194,15 b' def newheads(repo, heads, roots):'
1165 1194 # phases > dagop > patch > copies > scmutil > obsolete > obsutil > phases
1166 1195 from . import dagop
1167 1196
1168 repo = repo.unfiltered()
1169 cl = repo.changelog
1170 rev = cl.index.get_rev
1171 1197 if not roots:
1172 1198 return heads
1173 if not heads or heads == [repo.nullid]:
1199 if not heads or heads == [nullrev]:
1174 1200 return []
1175 1201 # The logic operated on revisions, convert arguments early for convenience
1176 new_heads = {rev(n) for n in heads if n != repo.nullid}
1177 roots = [rev(n) for n in roots]
1202 # PERF-XXX: maybe heads could directly comes as a set without impacting
1203 # other user of that value
1204 new_heads = set(heads)
1205 new_heads.discard(nullrev)
1178 1206 # compute the area we need to remove
1179 1207 affected_zone = repo.revs(b"(%ld::%ld)", roots, new_heads)
1180 1208 # heads in the area are no longer heads
@@ -1192,7 +1220,9 b' def newheads(repo, heads, roots):'
1192 1220 pruned = dagop.reachableroots(repo, candidates, prunestart)
1193 1221 new_heads.difference_update(pruned)
1194 1222
1195 return pycompat.maplist(cl.node, sorted(new_heads))
1223 # PERF-XXX: do we actually need a sorted list here? Could we simply return
1224 # a set?
1225 return sorted(new_heads)
1196 1226
1197 1227
1198 1228 def newcommitphase(ui: "uimod.ui") -> int:
@@ -70,6 +70,7 b' def lsprofile(ui, fp):'
70 70 stats = lsprof.Stats(p.getstats())
71 71 stats.sort(pycompat.sysstr(field))
72 72 stats.pprint(limit=limit, file=fp, climit=climit)
73 fp.flush()
73 74
74 75
75 76 @contextlib.contextmanager
@@ -97,14 +98,15 b' def flameprofile(ui, fp):'
97 98 finally:
98 99 thread.stop()
99 100 thread.join()
100 print(
101 b'Collected %d stack frames (%d unique) in %2.2f seconds.'
102 % (
101 m = b'Collected %d stack frames (%d unique) in %2.2f seconds.'
102 m %= (
103 (
103 104 util.timer() - start_time,
104 105 thread.num_frames(),
105 106 thread.num_frames(unique=True),
106 )
107 ),
107 108 )
109 print(m, flush=True)
108 110
109 111
110 112 @contextlib.contextmanager
@@ -170,6 +172,7 b' def statprofile(ui, fp):'
170 172 kwargs['showtime'] = showtime
171 173
172 174 statprof.display(fp, data=data, format=displayformat, **kwargs)
175 fp.flush()
173 176
174 177
175 178 class profile:
@@ -397,6 +397,9 b' class repoview:'
397 397 """
398 398
399 399 def __init__(self, repo, filtername, visibilityexceptions=None):
400 if filtername is None:
401 msg = "repoview should have a non-None filtername"
402 raise error.ProgrammingError(msg)
400 403 object.__setattr__(self, '_unfilteredrepo', repo)
401 404 object.__setattr__(self, 'filtername', filtername)
402 405 object.__setattr__(self, '_clcachekey', None)
@@ -149,6 +149,22 b' def rawsmartset(repo, subset, x, order):'
149 149 return x & subset
150 150
151 151
152 def raw_node_set(repo, subset, x, order):
153 """argument is a list of nodeid, resolve and use them"""
154 nodes = _ordered_node_set(repo, x)
155 if order == followorder:
156 return subset & nodes
157 else:
158 return nodes & subset
159
160
161 def _ordered_node_set(repo, nodes):
162 if not nodes:
163 return baseset()
164 to_rev = repo.changelog.index.rev
165 return baseset([to_rev(r) for r in nodes])
166
167
152 168 def rangeset(repo, subset, x, y, order):
153 169 m = getset(repo, fullreposet(repo), x)
154 170 n = getset(repo, fullreposet(repo), y)
@@ -2772,6 +2788,7 b' methods = {'
2772 2788 b"parent": parentspec,
2773 2789 b"parentpost": parentpost,
2774 2790 b"smartset": rawsmartset,
2791 b"nodeset": raw_node_set,
2775 2792 }
2776 2793
2777 2794 relations = {
@@ -392,7 +392,7 b' def _analyze(x):'
392 392 elif op == b'negate':
393 393 s = getstring(x[1], _(b"can't negate that"))
394 394 return _analyze((b'string', b'-' + s))
395 elif op in (b'string', b'symbol', b'smartset'):
395 elif op in (b'string', b'symbol', b'smartset', b'nodeset'):
396 396 return x
397 397 elif op == b'rangeall':
398 398 return (op, None)
@@ -441,8 +441,9 b' def _optimize(x):'
441 441 return 0, x
442 442
443 443 op = x[0]
444 if op in (b'string', b'symbol', b'smartset'):
445 return 0.5, x # single revisions are small
444 if op in (b'string', b'symbol', b'smartset', b'nodeset'):
445 # single revisions are small, and set of already computed revision are assumed to be cheap.
446 return 0.5, x
446 447 elif op == b'and':
447 448 wa, ta = _optimize(x[1])
448 449 wb, tb = _optimize(x[2])
@@ -784,6 +785,8 b' def formatspec(expr, *args):'
784 785 if isinstance(arg, set):
785 786 arg = sorted(arg)
786 787 ret.append(_formatintlist(list(arg)))
788 elif t == b'nodeset':
789 ret.append(_formatlistexp(list(arg), b"n"))
787 790 else:
788 791 raise error.ProgrammingError(b"unknown revspec item type: %r" % t)
789 792 return b''.join(ret)
@@ -801,6 +804,10 b' def spectree(expr, *args):'
801 804 newtree = (b'smartset', smartset.baseset(arg))
802 805 inputs.append(newtree)
803 806 ret.append(b"$")
807 elif t == b'nodeset':
808 newtree = (b'nodeset', arg)
809 inputs.append(newtree)
810 ret.append(b"$")
804 811 else:
805 812 raise error.ProgrammingError(b"unknown revspec item type: %r" % t)
806 813 expr = b''.join(ret)
@@ -863,6 +870,12 b' def _parseargs(expr, args):'
863 870 ret.append((b'baseset', arg))
864 871 pos += 1
865 872 continue
873 elif islist and d == b'n' and arg:
874 # we cannot turn the node into revision yet, but not
875 # serializing them will same a lot of time for large set.
876 ret.append((b'nodeset', arg))
877 pos += 1
878 continue
866 879 try:
867 880 ret.append((None, f(list(arg), d)))
868 881 except (TypeError, ValueError):
@@ -60,8 +60,6 b' def systemrcpath() -> List[bytes]:'
60 60 def userrcpath() -> List[bytes]:
61 61 if pycompat.sysplatform == b'plan9':
62 62 return [encoding.environ[b'home'] + b'/lib/hgrc']
63 elif pycompat.isdarwin:
64 return [os.path.expanduser(b'~/.hgrc')]
65 63 else:
66 64 confighome = encoding.environ.get(b'XDG_CONFIG_HOME')
67 65 if confighome is None or not os.path.isabs(confighome):
@@ -349,7 +349,7 b' class casecollisionauditor:'
349 349 self._newfiles.add(f)
350 350
351 351
352 def filteredhash(repo, maxrev, needobsolete=False):
352 def combined_filtered_and_obsolete_hash(repo, maxrev, needobsolete=False):
353 353 """build hash of filtered revisions in the current repoview.
354 354
355 355 Multiple caches perform up-to-date validation by checking that the
@@ -375,16 +375,69 b' def filteredhash(repo, maxrev, needobsol'
375 375
376 376 result = cl._filteredrevs_hashcache.get(key)
377 377 if not result:
378 revs = sorted(r for r in cl.filteredrevs | obsrevs if r <= maxrev)
378 revs, obs_revs = _filtered_and_obs_revs(repo, maxrev)
379 if needobsolete:
380 revs = revs | obs_revs
381 revs = sorted(revs)
379 382 if revs:
380 s = hashutil.sha1()
381 for rev in revs:
382 s.update(b'%d;' % rev)
383 result = s.digest()
383 result = _hash_revs(revs)
384 384 cl._filteredrevs_hashcache[key] = result
385 385 return result
386 386
387 387
388 def filtered_and_obsolete_hash(repo, maxrev):
389 """build hashs of filtered and obsolete revisions in the current repoview.
390
391 Multiple caches perform up-to-date validation by checking that the
392 tiprev and tipnode stored in the cache file match the current repository.
393 However, this is not sufficient for validating repoviews because the set
394 of revisions in the view may change without the repository tiprev and
395 tipnode changing.
396
397 This function hashes all the revs filtered from the view up to maxrev and
398 returns that SHA-1 digest. The obsolete revisions hashed are only the
399 non-filtered one.
400 """
401 cl = repo.changelog
402 obs_set = obsolete.getrevs(repo, b'obsolete')
403 key = (maxrev, hash(cl.filteredrevs), hash(obs_set))
404
405 result = cl._filteredrevs_hashcache.get(key)
406 if result is None:
407 filtered_hash = None
408 obs_hash = None
409 filtered_revs, obs_revs = _filtered_and_obs_revs(repo, maxrev)
410 if filtered_revs:
411 filtered_hash = _hash_revs(filtered_revs)
412 if obs_revs:
413 obs_hash = _hash_revs(obs_revs)
414 result = (filtered_hash, obs_hash)
415 cl._filteredrevs_hashcache[key] = result
416 return result
417
418
419 def _filtered_and_obs_revs(repo, max_rev):
420 """return the set of filtered and non-filtered obsolete revision"""
421 cl = repo.changelog
422 obs_set = obsolete.getrevs(repo, b'obsolete')
423 filtered_set = cl.filteredrevs
424 if cl.filteredrevs:
425 obs_set = obs_set - cl.filteredrevs
426 if max_rev < (len(cl) - 1):
427 # there might be revision to filter out
428 filtered_set = set(r for r in filtered_set if r <= max_rev)
429 obs_set = set(r for r in obs_set if r <= max_rev)
430 return (filtered_set, obs_set)
431
432
433 def _hash_revs(revs):
434 """return a hash from a list of revision numbers"""
435 s = hashutil.sha1()
436 for rev in revs:
437 s.update(b'%d;' % rev)
438 return s.digest()
439
440
388 441 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
389 442 """yield every hg repository under path, always recursively.
390 443 The recurse flag will only control recursion into repo working dirs"""
@@ -453,6 +453,10 b' class StoreFile:'
453 453 self._file_size = 0
454 454 return self._file_size
455 455
456 @property
457 def has_size(self):
458 return self._file_size is not None
459
456 460 def get_stream(self, vfs, copies):
457 461 """return data "stream" information for this file
458 462
@@ -480,6 +484,8 b' class BaseStoreEntry:'
480 484
481 485 This is returned by `store.walk` and represent some data in the store."""
482 486
487 maybe_volatile = True
488
483 489 def files(self) -> List[StoreFile]:
484 490 raise NotImplementedError
485 491
@@ -505,6 +511,7 b' class SimpleStoreEntry(BaseStoreEntry):'
505 511
506 512 is_revlog = False
507 513
514 maybe_volatile = attr.ib()
508 515 _entry_path = attr.ib()
509 516 _is_volatile = attr.ib(default=False)
510 517 _file_size = attr.ib(default=None)
@@ -521,6 +528,7 b' class SimpleStoreEntry(BaseStoreEntry):'
521 528 self._is_volatile = is_volatile
522 529 self._file_size = file_size
523 530 self._files = None
531 self.maybe_volatile = is_volatile
524 532
525 533 def files(self) -> List[StoreFile]:
526 534 if self._files is None:
@@ -542,6 +550,7 b' class RevlogStoreEntry(BaseStoreEntry):'
542 550
543 551 revlog_type = attr.ib(default=None)
544 552 target_id = attr.ib(default=None)
553 maybe_volatile = attr.ib(default=True)
545 554 _path_prefix = attr.ib(default=None)
546 555 _details = attr.ib(default=None)
547 556 _files = attr.ib(default=None)
@@ -558,6 +567,12 b' class RevlogStoreEntry(BaseStoreEntry):'
558 567 self.target_id = target_id
559 568 self._path_prefix = path_prefix
560 569 assert b'.i' in details, (path_prefix, details)
570 for ext in details:
571 if ext.endswith(REVLOG_FILES_VOLATILE_EXT):
572 self.maybe_volatile = True
573 break
574 else:
575 self.maybe_volatile = False
561 576 self._details = details
562 577 self._files = None
563 578
@@ -601,7 +616,8 b' class RevlogStoreEntry(BaseStoreEntry):'
601 616 max_changeset=None,
602 617 preserve_file_count=False,
603 618 ):
604 if (
619 pre_sized = all(f.has_size for f in self.files())
620 if pre_sized and (
605 621 repo is None
606 622 or max_changeset is None
607 623 # This use revlog-v2, ignore for now
@@ -646,11 +646,12 b' def _emit2(repo, entries):'
646 646
647 647 max_linkrev = len(repo)
648 648 file_count = totalfilesize = 0
649 # record the expected size of every file
650 for k, vfs, e in entries:
651 for f in e.files():
652 file_count += 1
653 totalfilesize += f.file_size(vfs)
649 with util.nogc():
650 # record the expected size of every file
651 for k, vfs, e in entries:
652 for f in e.files():
653 file_count += 1
654 totalfilesize += f.file_size(vfs)
654 655
655 656 progress = repo.ui.makeprogress(
656 657 _(b'bundle'), total=totalfilesize, unit=_(b'bytes')
@@ -722,10 +723,12 b' def _emit3(repo, entries):'
722 723 with TempCopyManager() as copy, progress:
723 724 # create a copy of volatile files
724 725 for k, vfs, e in entries:
725 for f in e.files():
726 f.file_size(vfs) # record the expected size under lock
727 if f.is_volatile:
728 copy(vfs.join(f.unencoded_path))
726 if e.maybe_volatile:
727 for f in e.files():
728 if f.is_volatile:
729 # record the expected size under lock
730 f.file_size(vfs)
731 copy(vfs.join(f.unencoded_path))
729 732 # the first yield release the lock on the repository
730 733 yield None
731 734
@@ -770,23 +773,26 b' def _entries_walk(repo, includes, exclud'
770 773 matcher = narrowspec.match(repo.root, includes, excludes)
771 774
772 775 phase = not repo.publishing()
773 entries = _walkstreamfiles(
774 repo,
775 matcher,
776 phase=phase,
777 obsolescence=includeobsmarkers,
778 )
779 for entry in entries:
780 yield (_srcstore, entry)
776 # Python is getting crazy at all the small container we creates, disabling
777 # the gc while we do so helps performance a lot.
778 with util.nogc():
779 entries = _walkstreamfiles(
780 repo,
781 matcher,
782 phase=phase,
783 obsolescence=includeobsmarkers,
784 )
785 for entry in entries:
786 yield (_srcstore, entry)
781 787
782 for name in cacheutil.cachetocopy(repo):
783 if repo.cachevfs.exists(name):
784 # not really a StoreEntry, but close enough
785 entry = store.SimpleStoreEntry(
786 entry_path=name,
787 is_volatile=True,
788 )
789 yield (_srccache, entry)
788 for name in cacheutil.cachetocopy(repo):
789 if repo.cachevfs.exists(name):
790 # not really a StoreEntry, but close enough
791 entry = store.SimpleStoreEntry(
792 entry_path=name,
793 is_volatile=True,
794 )
795 yield (_srccache, entry)
790 796
791 797
792 798 def generatev2(repo, includes, excludes, includeobsmarkers):
@@ -847,7 +853,10 b' def generatev3(repo, includes, excludes,'
847 853 - ways to adjust the number of expected entries/files ?
848 854 """
849 855
850 with repo.lock():
856 # Python is getting crazy at all the small container we creates while
857 # considering the files to preserve, disabling the gc while we do so helps
858 # performance a lot.
859 with repo.lock(), util.nogc():
851 860
852 861 repo.ui.debug(b'scanning\n')
853 862
@@ -21,6 +21,7 b' from .node import ('
21 21 short,
22 22 )
23 23 from .i18n import _
24 from .revlogutils.constants import ENTRY_NODE_ID
24 25 from . import (
25 26 encoding,
26 27 error,
@@ -30,6 +31,7 b' from . import ('
30 31 )
31 32 from .utils import stringutil
32 33
34
33 35 # Tags computation can be expensive and caches exist to make it fast in
34 36 # the common case.
35 37 #
@@ -80,6 +82,34 b' from .utils import stringutil'
80 82 # setting it) for each tag is last.
81 83
82 84
85 def warm_cache(repo):
86 """ensure the cache is properly filled"""
87 unfi = repo.unfiltered()
88 fnodescache = hgtagsfnodescache(unfi)
89 validated_fnodes = set()
90 unknown_entries = set()
91 flog = None
92
93 entries = enumerate(repo.changelog.index)
94 node_revs = ((e[ENTRY_NODE_ID], rev) for (rev, e) in entries)
95
96 for node, rev in node_revs:
97 fnode = fnodescache.getfnode(node=node, rev=rev)
98 if fnode != repo.nullid:
99 if fnode not in validated_fnodes:
100 if flog is None:
101 flog = repo.file(b'.hgtags')
102 if flog.hasnode(fnode):
103 validated_fnodes.add(fnode)
104 else:
105 unknown_entries.add(node)
106
107 if unknown_entries:
108 fnodescache.refresh_invalid_nodes(unknown_entries)
109
110 fnodescache.write()
111
112
83 113 def fnoderevs(ui, repo, revs):
84 114 """return the list of '.hgtags' fnodes used in a set revisions
85 115
@@ -433,7 +463,11 b' def _readtagcache(ui, repo):'
433 463 if (
434 464 cacherev == tiprev
435 465 and cachenode == tipnode
436 and cachehash == scmutil.filteredhash(repo, tiprev)
466 and cachehash
467 == scmutil.combined_filtered_and_obsolete_hash(
468 repo,
469 tiprev,
470 )
437 471 ):
438 472 tags = _readtags(ui, repo, cachelines, cachefile.name)
439 473 cachefile.close()
@@ -441,7 +475,14 b' def _readtagcache(ui, repo):'
441 475 if cachefile:
442 476 cachefile.close() # ignore rest of file
443 477
444 valid = (tiprev, tipnode, scmutil.filteredhash(repo, tiprev))
478 valid = (
479 tiprev,
480 tipnode,
481 scmutil.combined_filtered_and_obsolete_hash(
482 repo,
483 tiprev,
484 ),
485 )
445 486
446 487 repoheads = repo.heads()
447 488 # Case 2 (uncommon): empty repo; get out quickly and don't bother
@@ -479,7 +520,7 b' def _readtagcache(ui, repo):'
479 520 return (repoheads, cachefnode, valid, None, True)
480 521
481 522
482 def _getfnodes(ui, repo, nodes):
523 def _getfnodes(ui, repo, nodes=None, revs=None):
483 524 """return .hgtags fnodes for a list of changeset nodes
484 525
485 526 Return value is a {node: fnode} mapping. There will be no entry for nodes
@@ -491,9 +532,21 b' def _getfnodes(ui, repo, nodes):'
491 532 validated_fnodes = set()
492 533 unknown_entries = set()
493 534
535 if nodes is None and revs is None:
536 raise error.ProgrammingError("need to specify either nodes or revs")
537 elif nodes is not None and revs is None:
538 to_rev = repo.changelog.index.rev
539 nodes_revs = ((n, to_rev(n)) for n in nodes)
540 elif nodes is None and revs is not None:
541 to_node = repo.changelog.node
542 nodes_revs = ((to_node(r), r) for r in revs)
543 else:
544 msg = "need to specify only one of nodes or revs"
545 raise error.ProgrammingError(msg)
546
494 547 flog = None
495 for node in nodes:
496 fnode = fnodescache.getfnode(node)
548 for node, rev in nodes_revs:
549 fnode = fnodescache.getfnode(node=node, rev=rev)
497 550 if fnode != repo.nullid:
498 551 if fnode not in validated_fnodes:
499 552 if flog is None:
@@ -746,7 +799,7 b' class hgtagsfnodescache:'
746 799 # TODO: zero fill entire record, because it's invalid not missing?
747 800 self._raw.extend(b'\xff' * (wantedlen - rawlen))
748 801
749 def getfnode(self, node, computemissing=True):
802 def getfnode(self, node, computemissing=True, rev=None):
750 803 """Obtain the filenode of the .hgtags file at a specified revision.
751 804
752 805 If the value is in the cache, the entry will be validated and returned.
@@ -761,7 +814,8 b' class hgtagsfnodescache:'
761 814 if node == self._repo.nullid:
762 815 return node
763 816
764 rev = self._repo.changelog.rev(node)
817 if rev is None:
818 rev = self._repo.changelog.rev(node)
765 819
766 820 self.lookupcount += 1
767 821
@@ -35,6 +35,7 b' import traceback'
35 35 import warnings
36 36
37 37 from typing import (
38 Any,
38 39 Iterable,
39 40 Iterator,
40 41 List,
@@ -1812,7 +1813,7 b' def never(fn):'
1812 1813 return False
1813 1814
1814 1815
1815 def nogc(func):
1816 def nogc(func=None) -> Any:
1816 1817 """disable garbage collector
1817 1818
1818 1819 Python's garbage collector triggers a GC each time a certain number of
@@ -1825,15 +1826,27 b' def nogc(func):'
1825 1826 This garbage collector issue have been fixed in 2.7. But it still affect
1826 1827 CPython's performance.
1827 1828 """
1828
1829 if func is None:
1830 return _nogc_context()
1831 else:
1832 return _nogc_decorator(func)
1833
1834
1835 @contextlib.contextmanager
1836 def _nogc_context():
1837 gcenabled = gc.isenabled()
1838 gc.disable()
1839 try:
1840 yield
1841 finally:
1842 if gcenabled:
1843 gc.enable()
1844
1845
1846 def _nogc_decorator(func):
1829 1847 def wrapper(*args, **kwargs):
1830 gcenabled = gc.isenabled()
1831 gc.disable()
1832 try:
1848 with _nogc_context():
1833 1849 return func(*args, **kwargs)
1834 finally:
1835 if gcenabled:
1836 gc.enable()
1837 1850
1838 1851 return wrapper
1839 1852
@@ -6,6 +6,7 b''
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 from .. import error
9 10
10 11 ### Nearest subset relation
11 12 # Nearest subset of filter X is a filter Y so that:
@@ -21,3 +22,30 b' subsettable = {'
21 22 b'served': b'immutable',
22 23 b'immutable': b'base',
23 24 }
25
26
27 def get_ordered_subset():
28 """return a list of subset name from dependencies to dependents"""
29 _unfinalized = set(subsettable.values())
30 ordered = []
31
32 # the subset table is expected to be small so we do the stupid N² version
33 # of the algorithm
34 while _unfinalized:
35 this_level = []
36 for candidate in _unfinalized:
37 dependency = subsettable.get(candidate)
38 if dependency not in _unfinalized:
39 this_level.append(candidate)
40
41 if not this_level:
42 msg = "cyclic dependencies in repoview subset %r"
43 msg %= subsettable
44 raise error.ProgrammingError(msg)
45
46 this_level.sort(key=lambda x: x if x is not None else '')
47
48 ordered.extend(this_level)
49 _unfinalized.difference_update(this_level)
50
51 return ordered
@@ -616,6 +616,10 b' class proxyvfs(abstractvfs):'
616 616 def options(self, value):
617 617 self.vfs.options = value
618 618
619 @property
620 def audit(self):
621 return self.vfs.audit
622
619 623
620 624 class filtervfs(proxyvfs, abstractvfs):
621 625 '''Wrapper vfs for filtering filenames with a function.'''
@@ -312,6 +312,7 b' def clonebundles(repo, proto):'
312 312 if line.startswith(bundlecaches.CLONEBUNDLESCHEME):
313 313 continue
314 314 modified_manifest.append(line)
315 modified_manifest.append(b'')
315 316 return wireprototypes.bytesresponse(b'\n'.join(modified_manifest))
316 317
317 318
@@ -150,21 +150,21 b' fn escape_pattern(pattern: &[u8]) -> Vec'
150 150 .collect()
151 151 }
152 152
153 pub fn parse_pattern_syntax(
153 pub fn parse_pattern_syntax_kind(
154 154 kind: &[u8],
155 155 ) -> Result<PatternSyntax, PatternError> {
156 156 match kind {
157 b"re:" => Ok(PatternSyntax::Regexp),
158 b"path:" => Ok(PatternSyntax::Path),
159 b"filepath:" => Ok(PatternSyntax::FilePath),
160 b"relpath:" => Ok(PatternSyntax::RelPath),
161 b"rootfilesin:" => Ok(PatternSyntax::RootFilesIn),
162 b"relglob:" => Ok(PatternSyntax::RelGlob),
163 b"relre:" => Ok(PatternSyntax::RelRegexp),
164 b"glob:" => Ok(PatternSyntax::Glob),
165 b"rootglob:" => Ok(PatternSyntax::RootGlob),
166 b"include:" => Ok(PatternSyntax::Include),
167 b"subinclude:" => Ok(PatternSyntax::SubInclude),
157 b"re" => Ok(PatternSyntax::Regexp),
158 b"path" => Ok(PatternSyntax::Path),
159 b"filepath" => Ok(PatternSyntax::FilePath),
160 b"relpath" => Ok(PatternSyntax::RelPath),
161 b"rootfilesin" => Ok(PatternSyntax::RootFilesIn),
162 b"relglob" => Ok(PatternSyntax::RelGlob),
163 b"relre" => Ok(PatternSyntax::RelRegexp),
164 b"glob" => Ok(PatternSyntax::Glob),
165 b"rootglob" => Ok(PatternSyntax::RootGlob),
166 b"include" => Ok(PatternSyntax::Include),
167 b"subinclude" => Ok(PatternSyntax::SubInclude),
168 168 _ => Err(PatternError::UnsupportedSyntax(
169 169 String::from_utf8_lossy(kind).to_string(),
170 170 )),
@@ -41,7 +41,7 b' pub mod vfs;'
41 41
42 42 use crate::utils::hg_path::{HgPathBuf, HgPathError};
43 43 pub use filepatterns::{
44 parse_pattern_syntax, read_pattern_file, IgnorePattern,
44 parse_pattern_syntax_kind, read_pattern_file, IgnorePattern,
45 45 PatternFileWarning, PatternSyntax,
46 46 };
47 47 use std::collections::HashMap;
@@ -18,11 +18,12 b' use crate::{'
18 18 };
19 19
20 20 pub const INDEX_ENTRY_SIZE: usize = 64;
21 pub const INDEX_HEADER_SIZE: usize = 4;
21 22 pub const COMPRESSION_MODE_INLINE: u8 = 2;
22 23
23 24 #[derive(Debug)]
24 25 pub struct IndexHeader {
25 pub(super) header_bytes: [u8; 4],
26 pub(super) header_bytes: [u8; INDEX_HEADER_SIZE],
26 27 }
27 28
28 29 #[derive(Copy, Clone)]
@@ -92,14 +93,21 b' struct IndexData {'
92 93 truncation: Option<usize>,
93 94 /// Bytes that were added after reading the index
94 95 added: Vec<u8>,
96 first_entry: [u8; INDEX_ENTRY_SIZE],
95 97 }
96 98
97 99 impl IndexData {
98 100 pub fn new(bytes: Box<dyn Deref<Target = [u8]> + Send + Sync>) -> Self {
101 let mut first_entry = [0; INDEX_ENTRY_SIZE];
102 if bytes.len() >= INDEX_ENTRY_SIZE {
103 first_entry[INDEX_HEADER_SIZE..]
104 .copy_from_slice(&bytes[INDEX_HEADER_SIZE..INDEX_ENTRY_SIZE])
105 }
99 106 Self {
100 107 bytes,
101 108 truncation: None,
102 109 added: vec![],
110 first_entry,
103 111 }
104 112 }
105 113
@@ -356,7 +364,6 b' impl Index {'
356 364 let end = offset + INDEX_ENTRY_SIZE;
357 365 let entry = IndexEntry {
358 366 bytes: &bytes[offset..end],
359 offset_override: None,
360 367 };
361 368
362 369 offset += INDEX_ENTRY_SIZE + entry.compressed_len() as usize;
@@ -449,11 +456,17 b' impl Index {'
449 456 if rev == NULL_REVISION {
450 457 return None;
451 458 }
452 Some(if self.is_inline() {
453 self.get_entry_inline(rev)
459 if rev.0 == 0 {
460 Some(IndexEntry {
461 bytes: &self.bytes.first_entry[..],
462 })
454 463 } else {
455 self.get_entry_separated(rev)
456 })
464 Some(if self.is_inline() {
465 self.get_entry_inline(rev)
466 } else {
467 self.get_entry_separated(rev)
468 })
469 }
457 470 }
458 471
459 472 /// Return the binary content of the index entry for the given revision
@@ -512,13 +525,7 b' impl Index {'
512 525 let end = start + INDEX_ENTRY_SIZE;
513 526 let bytes = &self.bytes[start..end];
514 527
515 // See IndexEntry for an explanation of this override.
516 let offset_override = Some(end);
517
518 IndexEntry {
519 bytes,
520 offset_override,
521 }
528 IndexEntry { bytes }
522 529 }
523 530
524 531 fn get_entry_separated(&self, rev: Revision) -> IndexEntry {
@@ -526,20 +533,12 b' impl Index {'
526 533 let end = start + INDEX_ENTRY_SIZE;
527 534 let bytes = &self.bytes[start..end];
528 535
529 // Override the offset of the first revision as its bytes are used
530 // for the index's metadata (saving space because it is always 0)
531 let offset_override = if rev == Revision(0) { Some(0) } else { None };
532
533 IndexEntry {
534 bytes,
535 offset_override,
536 }
536 IndexEntry { bytes }
537 537 }
538 538
539 539 fn null_entry(&self) -> IndexEntry {
540 540 IndexEntry {
541 541 bytes: &[0; INDEX_ENTRY_SIZE],
542 offset_override: Some(0),
543 542 }
544 543 }
545 544
@@ -755,13 +754,20 b' impl Index {'
755 754 revision_data: RevisionDataParams,
756 755 ) -> Result<(), RevlogError> {
757 756 revision_data.validate()?;
757 let entry_v1 = revision_data.into_v1();
758 let entry_bytes = entry_v1.as_bytes();
759 if self.bytes.len() == 0 {
760 self.bytes.first_entry[INDEX_HEADER_SIZE..].copy_from_slice(
761 &entry_bytes[INDEX_HEADER_SIZE..INDEX_ENTRY_SIZE],
762 )
763 }
758 764 if self.is_inline() {
759 765 let new_offset = self.bytes.len();
760 766 if let Some(offsets) = &mut *self.get_offsets_mut() {
761 767 offsets.push(new_offset)
762 768 }
763 769 }
764 self.bytes.added.extend(revision_data.into_v1().as_bytes());
770 self.bytes.added.extend(entry_bytes);
765 771 self.clear_head_revs();
766 772 Ok(())
767 773 }
@@ -1654,7 +1660,6 b' fn inline_scan(bytes: &[u8]) -> (usize, '
1654 1660 let end = offset + INDEX_ENTRY_SIZE;
1655 1661 let entry = IndexEntry {
1656 1662 bytes: &bytes[offset..end],
1657 offset_override: None,
1658 1663 };
1659 1664
1660 1665 offset += INDEX_ENTRY_SIZE + entry.compressed_len() as usize;
@@ -1678,29 +1683,14 b' impl super::RevlogIndex for Index {'
1678 1683 #[derive(Debug)]
1679 1684 pub struct IndexEntry<'a> {
1680 1685 bytes: &'a [u8],
1681 /// Allows to override the offset value of the entry.
1682 ///
1683 /// For interleaved index and data, the offset stored in the index
1684 /// corresponds to the separated data offset.
1685 /// It has to be overridden with the actual offset in the interleaved
1686 /// index which is just after the index block.
1687 ///
1688 /// For separated index and data, the offset stored in the first index
1689 /// entry is mixed with the index headers.
1690 /// It has to be overridden with 0.
1691 offset_override: Option<usize>,
1692 1686 }
1693 1687
1694 1688 impl<'a> IndexEntry<'a> {
1695 1689 /// Return the offset of the data.
1696 1690 pub fn offset(&self) -> usize {
1697 if let Some(offset_override) = self.offset_override {
1698 offset_override
1699 } else {
1700 let mut bytes = [0; 8];
1701 bytes[2..8].copy_from_slice(&self.bytes[0..=5]);
1702 BigEndian::read_u64(&bytes[..]) as usize
1703 }
1691 let mut bytes = [0; 8];
1692 bytes[2..8].copy_from_slice(&self.bytes[0..=5]);
1693 BigEndian::read_u64(&bytes[..]) as usize
1704 1694 }
1705 1695 pub fn raw_offset(&self) -> u64 {
1706 1696 BigEndian::read_u64(&self.bytes[0..8])
@@ -1956,32 +1946,15 b' mod tests {'
1956 1946 #[test]
1957 1947 fn test_offset() {
1958 1948 let bytes = IndexEntryBuilder::new().with_offset(1).build();
1959 let entry = IndexEntry {
1960 bytes: &bytes,
1961 offset_override: None,
1962 };
1949 let entry = IndexEntry { bytes: &bytes };
1963 1950
1964 1951 assert_eq!(entry.offset(), 1)
1965 1952 }
1966 1953
1967 1954 #[test]
1968 fn test_with_overridden_offset() {
1969 let bytes = IndexEntryBuilder::new().with_offset(1).build();
1970 let entry = IndexEntry {
1971 bytes: &bytes,
1972 offset_override: Some(2),
1973 };
1974
1975 assert_eq!(entry.offset(), 2)
1976 }
1977
1978 #[test]
1979 1955 fn test_compressed_len() {
1980 1956 let bytes = IndexEntryBuilder::new().with_compressed_len(1).build();
1981 let entry = IndexEntry {
1982 bytes: &bytes,
1983 offset_override: None,
1984 };
1957 let entry = IndexEntry { bytes: &bytes };
1985 1958
1986 1959 assert_eq!(entry.compressed_len(), 1)
1987 1960 }
@@ -1989,10 +1962,7 b' mod tests {'
1989 1962 #[test]
1990 1963 fn test_uncompressed_len() {
1991 1964 let bytes = IndexEntryBuilder::new().with_uncompressed_len(1).build();
1992 let entry = IndexEntry {
1993 bytes: &bytes,
1994 offset_override: None,
1995 };
1965 let entry = IndexEntry { bytes: &bytes };
1996 1966
1997 1967 assert_eq!(entry.uncompressed_len(), 1)
1998 1968 }
@@ -2002,10 +1972,7 b' mod tests {'
2002 1972 let bytes = IndexEntryBuilder::new()
2003 1973 .with_base_revision_or_base_of_delta_chain(Revision(1))
2004 1974 .build();
2005 let entry = IndexEntry {
2006 bytes: &bytes,
2007 offset_override: None,
2008 };
1975 let entry = IndexEntry { bytes: &bytes };
2009 1976
2010 1977 assert_eq!(entry.base_revision_or_base_of_delta_chain(), 1.into())
2011 1978 }
@@ -2016,10 +1983,7 b' mod tests {'
2016 1983 .with_link_revision(Revision(123))
2017 1984 .build();
2018 1985
2019 let entry = IndexEntry {
2020 bytes: &bytes,
2021 offset_override: None,
2022 };
1986 let entry = IndexEntry { bytes: &bytes };
2023 1987
2024 1988 assert_eq!(entry.link_revision(), 123.into());
2025 1989 }
@@ -2028,10 +1992,7 b' mod tests {'
2028 1992 fn p1_test() {
2029 1993 let bytes = IndexEntryBuilder::new().with_p1(Revision(123)).build();
2030 1994
2031 let entry = IndexEntry {
2032 bytes: &bytes,
2033 offset_override: None,
2034 };
1995 let entry = IndexEntry { bytes: &bytes };
2035 1996
2036 1997 assert_eq!(entry.p1(), 123.into());
2037 1998 }
@@ -2040,10 +2001,7 b' mod tests {'
2040 2001 fn p2_test() {
2041 2002 let bytes = IndexEntryBuilder::new().with_p2(Revision(123)).build();
2042 2003
2043 let entry = IndexEntry {
2044 bytes: &bytes,
2045 offset_override: None,
2046 };
2004 let entry = IndexEntry { bytes: &bytes };
2047 2005
2048 2006 assert_eq!(entry.p2(), 123.into());
2049 2007 }
@@ -2054,10 +2012,7 b' mod tests {'
2054 2012 .unwrap();
2055 2013 let bytes = IndexEntryBuilder::new().with_node(node).build();
2056 2014
2057 let entry = IndexEntry {
2058 bytes: &bytes,
2059 offset_override: None,
2060 };
2015 let entry = IndexEntry { bytes: &bytes };
2061 2016
2062 2017 assert_eq!(*entry.hash(), node);
2063 2018 }
@@ -29,6 +29,7 b' use zstd;'
29 29 use self::node::{NODE_BYTES_LENGTH, NULL_NODE};
30 30 use self::nodemap_docket::NodeMapDocket;
31 31 use super::index::Index;
32 use super::index::INDEX_ENTRY_SIZE;
32 33 use super::nodemap::{NodeMap, NodeMapError};
33 34 use crate::errors::HgError;
34 35 use crate::vfs::Vfs;
@@ -537,7 +538,12 b' impl Revlog {'
537 538 .index
538 539 .get_entry(rev)
539 540 .ok_or(RevlogError::InvalidRevision)?;
540 let start = index_entry.offset();
541 let offset = index_entry.offset();
542 let start = if self.index.is_inline() {
543 offset + ((rev.0 as usize + 1) * INDEX_ENTRY_SIZE)
544 } else {
545 offset
546 };
541 547 let end = start + index_entry.compressed_len() as usize;
542 548 let data = if self.index.is_inline() {
543 549 self.index.data(start, end)
@@ -865,7 +871,7 b' fn hash('
865 871 #[cfg(test)]
866 872 mod tests {
867 873 use super::*;
868 use crate::index::{IndexEntryBuilder, INDEX_ENTRY_SIZE};
874 use crate::index::IndexEntryBuilder;
869 875 use itertools::Itertools;
870 876
871 877 #[test]
@@ -903,15 +909,10 b' mod tests {'
903 909 .is_first(true)
904 910 .with_version(1)
905 911 .with_inline(true)
906 .with_offset(INDEX_ENTRY_SIZE)
907 912 .with_node(node0)
908 913 .build();
909 let entry1_bytes = IndexEntryBuilder::new()
910 .with_offset(INDEX_ENTRY_SIZE)
911 .with_node(node1)
912 .build();
914 let entry1_bytes = IndexEntryBuilder::new().with_node(node1).build();
913 915 let entry2_bytes = IndexEntryBuilder::new()
914 .with_offset(INDEX_ENTRY_SIZE)
915 916 .with_p1(Revision(0))
916 917 .with_p2(Revision(1))
917 918 .with_node(node2)
@@ -977,13 +978,9 b' mod tests {'
977 978 .is_first(true)
978 979 .with_version(1)
979 980 .with_inline(true)
980 .with_offset(INDEX_ENTRY_SIZE)
981 981 .with_node(node0)
982 982 .build();
983 let entry1_bytes = IndexEntryBuilder::new()
984 .with_offset(INDEX_ENTRY_SIZE)
985 .with_node(node1)
986 .build();
983 let entry1_bytes = IndexEntryBuilder::new().with_node(node1).build();
987 984 let contents = vec![entry0_bytes, entry1_bytes]
988 985 .into_iter()
989 986 .flatten()
@@ -11,23 +11,23 b''
11 11
12 12 use crate::{dirstate::DirstateMap, exceptions::FallbackError};
13 13 use cpython::{
14 exc::ValueError, ObjectProtocol, PyBytes, PyErr, PyList, PyObject,
14 exc::ValueError, ObjectProtocol, PyBool, PyBytes, PyErr, PyList, PyObject,
15 15 PyResult, PyTuple, Python, PythonObject, ToPyObject,
16 16 };
17 17 use hg::dirstate::status::StatusPath;
18 18 use hg::matchers::{
19 19 DifferenceMatcher, IntersectionMatcher, Matcher, NeverMatcher,
20 UnionMatcher,
20 PatternMatcher, UnionMatcher,
21 21 };
22 22 use hg::{
23 23 matchers::{AlwaysMatcher, FileMatcher, IncludeMatcher},
24 parse_pattern_syntax,
24 parse_pattern_syntax_kind,
25 25 utils::{
26 26 files::{get_bytes_from_path, get_path_from_bytes},
27 27 hg_path::{HgPath, HgPathBuf},
28 28 },
29 BadMatch, DirstateStatus, IgnorePattern, PatternFileWarning, StatusError,
30 StatusOptions,
29 BadMatch, DirstateStatus, IgnorePattern, PatternError, PatternFileWarning,
30 StatusError, StatusOptions,
31 31 };
32 32 use std::borrow::Borrow;
33 33
@@ -153,11 +153,46 b' pub fn status_wrapper('
153 153 )
154 154 }
155 155
156 fn collect_kindpats(
157 py: Python,
158 matcher: PyObject,
159 ) -> PyResult<Vec<IgnorePattern>> {
160 matcher
161 .getattr(py, "_kindpats")?
162 .iter(py)?
163 .map(|k| {
164 let k = k?;
165 let syntax = parse_pattern_syntax_kind(
166 k.get_item(py, 0)?.extract::<PyBytes>(py)?.data(py),
167 )
168 .map_err(|e| handle_fallback(py, StatusError::Pattern(e)))?;
169 let pattern = k.get_item(py, 1)?.extract::<PyBytes>(py)?;
170 let pattern = pattern.data(py);
171 let source = k.get_item(py, 2)?.extract::<PyBytes>(py)?;
172 let source = get_path_from_bytes(source.data(py));
173 let new = IgnorePattern::new(syntax, pattern, source);
174 Ok(new)
175 })
176 .collect()
177 }
178
156 179 /// Transform a Python matcher into a Rust matcher.
157 180 fn extract_matcher(
158 181 py: Python,
159 182 matcher: PyObject,
160 183 ) -> PyResult<Box<dyn Matcher + Sync>> {
184 let tampered = matcher
185 .call_method(py, "was_tampered_with_nonrec", PyTuple::empty(py), None)?
186 .extract::<PyBool>(py)?
187 .is_true();
188 if tampered {
189 return Err(handle_fallback(
190 py,
191 StatusError::Pattern(PatternError::UnsupportedSyntax(
192 "Pattern matcher was tampered with!".to_string(),
193 )),
194 ));
195 };
161 196 match matcher.get_type(py).name(py).borrow() {
162 197 "alwaysmatcher" => Ok(Box::new(AlwaysMatcher)),
163 198 "nevermatcher" => Ok(Box::new(NeverMatcher)),
@@ -187,33 +222,7 b' fn extract_matcher('
187 222 // Get the patterns from Python even though most of them are
188 223 // redundant with those we will parse later on, as they include
189 224 // those passed from the command line.
190 let ignore_patterns: PyResult<Vec<_>> = matcher
191 .getattr(py, "_kindpats")?
192 .iter(py)?
193 .map(|k| {
194 let k = k?;
195 let syntax = parse_pattern_syntax(
196 &[
197 k.get_item(py, 0)?
198 .extract::<PyBytes>(py)?
199 .data(py),
200 &b":"[..],
201 ]
202 .concat(),
203 )
204 .map_err(|e| {
205 handle_fallback(py, StatusError::Pattern(e))
206 })?;
207 let pattern = k.get_item(py, 1)?.extract::<PyBytes>(py)?;
208 let pattern = pattern.data(py);
209 let source = k.get_item(py, 2)?.extract::<PyBytes>(py)?;
210 let source = get_path_from_bytes(source.data(py));
211 let new = IgnorePattern::new(syntax, pattern, source);
212 Ok(new)
213 })
214 .collect();
215
216 let ignore_patterns = ignore_patterns?;
225 let ignore_patterns = collect_kindpats(py, matcher)?;
217 226
218 227 let matcher = IncludeMatcher::new(ignore_patterns)
219 228 .map_err(|e| handle_fallback(py, e.into()))?;
@@ -241,6 +250,14 b' fn extract_matcher('
241 250
242 251 Ok(Box::new(DifferenceMatcher::new(m1, m2)))
243 252 }
253 "patternmatcher" => {
254 let patterns = collect_kindpats(py, matcher)?;
255
256 let matcher = PatternMatcher::new(patterns)
257 .map_err(|e| handle_fallback(py, e.into()))?;
258
259 Ok(Box::new(matcher))
260 }
244 261 e => Err(PyErr::new::<FallbackError, _>(
245 262 py,
246 263 format!("Unsupported matcher {}", e),
@@ -252,7 +252,10 b' def filterhgerr(err):'
252 252 if (
253 253 not e.startswith(b'not trusting file')
254 254 and not e.startswith(b'warning: Not importing')
255 and not e.startswith(b'obsolete feature not enabled')
255 and not (
256 e.startswith(b'obsolete feature not enabled')
257 or e.startswith(b'"obsolete" feature not enabled')
258 )
256 259 and not e.startswith(b'*** failed to import extension')
257 260 and not e.startswith(b'devel-warn:')
258 261 and not (
@@ -114,14 +114,6 b' substitutions = ['
114 114 br'(.*file:/)/?(/\$TESTTMP.*)',
115 115 lambda m: m.group(1) + b'*' + m.group(2) + b' (glob)',
116 116 ),
117 # `hg clone --stream` output
118 (
119 br'transferred (\S+?) KB in \S+? seconds \(.+?/sec\)(?: \(glob\))?(.*)',
120 lambda m: (
121 br'transferred %s KB in * seconds (* */sec) (glob)%s'
122 % (m.group(1), m.group(2))
123 ),
124 ),
125 117 # `discovery debug output
126 118 (
127 119 br'\b(\d+) total queries in \d.\d\d\d\ds\b',
@@ -167,7 +167,6 b' Extension disabled for lack of acl.sourc'
167 167 listing keys for "phases"
168 168 checking for updated bookmarks
169 169 listing keys for "bookmarks"
170 invalid branch cache (served): tip differs
171 170 listing keys for "bookmarks"
172 171 3 changesets found
173 172 list of changesets:
@@ -187,7 +186,6 b' Extension disabled for lack of acl.sourc'
187 186 bundle2-input-part: total payload size * (glob)
188 187 bundle2-input-part: "check:updated-heads" supported
189 188 bundle2-input-part: total payload size * (glob)
190 invalid branch cache (served): tip differs
191 189 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
192 190 adding changesets
193 191 add changeset ef1ea85a6374
@@ -237,7 +235,6 b' No [acl.allow]/[acl.deny]'
237 235 listing keys for "phases"
238 236 checking for updated bookmarks
239 237 listing keys for "bookmarks"
240 invalid branch cache (served): tip differs
241 238 listing keys for "bookmarks"
242 239 3 changesets found
243 240 list of changesets:
@@ -257,7 +254,6 b' No [acl.allow]/[acl.deny]'
257 254 bundle2-input-part: total payload size * (glob)
258 255 bundle2-input-part: "check:updated-heads" supported
259 256 bundle2-input-part: total payload size * (glob)
260 invalid branch cache (served): tip differs
261 257 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
262 258 adding changesets
263 259 add changeset ef1ea85a6374
@@ -317,7 +313,6 b' Empty [acl.allow]'
317 313 listing keys for "phases"
318 314 checking for updated bookmarks
319 315 listing keys for "bookmarks"
320 invalid branch cache (served): tip differs
321 316 listing keys for "bookmarks"
322 317 3 changesets found
323 318 list of changesets:
@@ -337,7 +332,6 b' Empty [acl.allow]'
337 332 bundle2-input-part: total payload size * (glob)
338 333 bundle2-input-part: "check:updated-heads" supported
339 334 bundle2-input-part: total payload size * (glob)
340 invalid branch cache (served): tip differs
341 335 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
342 336 adding changesets
343 337 add changeset ef1ea85a6374
@@ -388,7 +382,6 b' fred is allowed inside foo/'
388 382 listing keys for "phases"
389 383 checking for updated bookmarks
390 384 listing keys for "bookmarks"
391 invalid branch cache (served): tip differs
392 385 listing keys for "bookmarks"
393 386 3 changesets found
394 387 list of changesets:
@@ -408,7 +401,6 b' fred is allowed inside foo/'
408 401 bundle2-input-part: total payload size * (glob)
409 402 bundle2-input-part: "check:updated-heads" supported
410 403 bundle2-input-part: total payload size * (glob)
411 invalid branch cache (served): tip differs
412 404 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
413 405 adding changesets
414 406 add changeset ef1ea85a6374
@@ -463,7 +455,6 b' Empty [acl.deny]'
463 455 listing keys for "phases"
464 456 checking for updated bookmarks
465 457 listing keys for "bookmarks"
466 invalid branch cache (served): tip differs
467 458 listing keys for "bookmarks"
468 459 3 changesets found
469 460 list of changesets:
@@ -483,7 +474,6 b' Empty [acl.deny]'
483 474 bundle2-input-part: total payload size * (glob)
484 475 bundle2-input-part: "check:updated-heads" supported
485 476 bundle2-input-part: total payload size * (glob)
486 invalid branch cache (served): tip differs
487 477 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
488 478 adding changesets
489 479 add changeset ef1ea85a6374
@@ -535,7 +525,6 b' fred is allowed inside foo/, but not foo'
535 525 listing keys for "phases"
536 526 checking for updated bookmarks
537 527 listing keys for "bookmarks"
538 invalid branch cache (served): tip differs
539 528 listing keys for "bookmarks"
540 529 3 changesets found
541 530 list of changesets:
@@ -555,7 +544,6 b' fred is allowed inside foo/, but not foo'
555 544 bundle2-input-part: total payload size * (glob)
556 545 bundle2-input-part: "check:updated-heads" supported
557 546 bundle2-input-part: total payload size * (glob)
558 invalid branch cache (served): tip differs
559 547 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
560 548 adding changesets
561 549 add changeset ef1ea85a6374
@@ -612,7 +600,6 b' fred is allowed inside foo/, but not foo'
612 600 listing keys for "phases"
613 601 checking for updated bookmarks
614 602 listing keys for "bookmarks"
615 invalid branch cache (served): tip differs
616 603 listing keys for "bookmarks"
617 604 3 changesets found
618 605 list of changesets:
@@ -632,7 +619,6 b' fred is allowed inside foo/, but not foo'
632 619 bundle2-input-part: total payload size * (glob)
633 620 bundle2-input-part: "check:updated-heads" supported
634 621 bundle2-input-part: total payload size * (glob)
635 invalid branch cache (served): tip differs
636 622 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
637 623 adding changesets
638 624 add changeset ef1ea85a6374
@@ -686,7 +672,6 b' fred is allowed inside foo/, but not foo'
686 672 listing keys for "phases"
687 673 checking for updated bookmarks
688 674 listing keys for "bookmarks"
689 invalid branch cache (served): tip differs
690 675 listing keys for "bookmarks"
691 676 3 changesets found
692 677 list of changesets:
@@ -706,7 +691,6 b' fred is allowed inside foo/, but not foo'
706 691 bundle2-input-part: total payload size * (glob)
707 692 bundle2-input-part: "check:updated-heads" supported
708 693 bundle2-input-part: total payload size * (glob)
709 invalid branch cache (served): tip differs
710 694 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
711 695 adding changesets
712 696 add changeset ef1ea85a6374
@@ -761,7 +745,6 b' fred is not blocked from moving bookmark'
761 745 listing keys for "phases"
762 746 checking for updated bookmarks
763 747 listing keys for "bookmarks"
764 invalid branch cache (served): tip differs
765 748 listing keys for "bookmarks"
766 749 1 changesets found
767 750 list of changesets:
@@ -783,7 +766,6 b' fred is not blocked from moving bookmark'
783 766 bundle2-input-part: total payload size * (glob)
784 767 bundle2-input-part: "check:updated-heads" supported
785 768 bundle2-input-part: total payload size * (glob)
786 invalid branch cache (served): tip differs
787 769 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
788 770 adding changesets
789 771 add changeset ef1ea85a6374
@@ -810,7 +792,6 b' fred is not blocked from moving bookmark'
810 792 acl: bookmark access granted: "ef1ea85a6374b77d6da9dcda9541f498f2d17df7" on bookmark "moving-bookmark"
811 793 bundle2-input-bundle: 7 parts total
812 794 updating the branch cache
813 invalid branch cache (served.hidden): tip differs
814 795 added 1 changesets with 1 changes to 1 files
815 796 bundle2-output-bundle: "HG20", 1 parts total
816 797 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
@@ -850,7 +831,6 b' fred is not allowed to move bookmarks'
850 831 listing keys for "phases"
851 832 checking for updated bookmarks
852 833 listing keys for "bookmarks"
853 invalid branch cache (served): tip differs
854 834 listing keys for "bookmarks"
855 835 1 changesets found
856 836 list of changesets:
@@ -872,7 +852,6 b' fred is not allowed to move bookmarks'
872 852 bundle2-input-part: total payload size * (glob)
873 853 bundle2-input-part: "check:updated-heads" supported
874 854 bundle2-input-part: total payload size * (glob)
875 invalid branch cache (served): tip differs
876 855 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
877 856 adding changesets
878 857 add changeset ef1ea85a6374
@@ -939,7 +918,6 b' barney is allowed everywhere'
939 918 listing keys for "phases"
940 919 checking for updated bookmarks
941 920 listing keys for "bookmarks"
942 invalid branch cache (served): tip differs
943 921 listing keys for "bookmarks"
944 922 3 changesets found
945 923 list of changesets:
@@ -959,7 +937,6 b' barney is allowed everywhere'
959 937 bundle2-input-part: total payload size * (glob)
960 938 bundle2-input-part: "check:updated-heads" supported
961 939 bundle2-input-part: total payload size * (glob)
962 invalid branch cache (served): tip differs
963 940 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
964 941 adding changesets
965 942 add changeset ef1ea85a6374
@@ -1025,7 +1002,6 b' wilma can change files with a .txt exten'
1025 1002 listing keys for "phases"
1026 1003 checking for updated bookmarks
1027 1004 listing keys for "bookmarks"
1028 invalid branch cache (served): tip differs
1029 1005 listing keys for "bookmarks"
1030 1006 3 changesets found
1031 1007 list of changesets:
@@ -1045,7 +1021,6 b' wilma can change files with a .txt exten'
1045 1021 bundle2-input-part: total payload size * (glob)
1046 1022 bundle2-input-part: "check:updated-heads" supported
1047 1023 bundle2-input-part: total payload size * (glob)
1048 invalid branch cache (served): tip differs
1049 1024 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1050 1025 adding changesets
1051 1026 add changeset ef1ea85a6374
@@ -1109,7 +1084,6 b' file specified by acl.config does not ex'
1109 1084 listing keys for "phases"
1110 1085 checking for updated bookmarks
1111 1086 listing keys for "bookmarks"
1112 invalid branch cache (served): tip differs
1113 1087 listing keys for "bookmarks"
1114 1088 3 changesets found
1115 1089 list of changesets:
@@ -1129,7 +1103,6 b' file specified by acl.config does not ex'
1129 1103 bundle2-input-part: total payload size * (glob)
1130 1104 bundle2-input-part: "check:updated-heads" supported
1131 1105 bundle2-input-part: total payload size * (glob)
1132 invalid branch cache (served): tip differs
1133 1106 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1134 1107 adding changesets
1135 1108 add changeset ef1ea85a6374
@@ -1187,7 +1160,6 b' betty is allowed inside foo/ by a acl.co'
1187 1160 listing keys for "phases"
1188 1161 checking for updated bookmarks
1189 1162 listing keys for "bookmarks"
1190 invalid branch cache (served): tip differs
1191 1163 listing keys for "bookmarks"
1192 1164 3 changesets found
1193 1165 list of changesets:
@@ -1207,7 +1179,6 b' betty is allowed inside foo/ by a acl.co'
1207 1179 bundle2-input-part: total payload size * (glob)
1208 1180 bundle2-input-part: "check:updated-heads" supported
1209 1181 bundle2-input-part: total payload size * (glob)
1210 invalid branch cache (served): tip differs
1211 1182 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1212 1183 adding changesets
1213 1184 add changeset ef1ea85a6374
@@ -1276,7 +1247,6 b' acl.config can set only [acl.allow]/[acl'
1276 1247 listing keys for "phases"
1277 1248 checking for updated bookmarks
1278 1249 listing keys for "bookmarks"
1279 invalid branch cache (served): tip differs
1280 1250 listing keys for "bookmarks"
1281 1251 3 changesets found
1282 1252 list of changesets:
@@ -1296,7 +1266,6 b' acl.config can set only [acl.allow]/[acl'
1296 1266 bundle2-input-part: total payload size * (glob)
1297 1267 bundle2-input-part: "check:updated-heads" supported
1298 1268 bundle2-input-part: total payload size * (glob)
1299 invalid branch cache (served): tip differs
1300 1269 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1301 1270 adding changesets
1302 1271 add changeset ef1ea85a6374
@@ -1366,7 +1335,6 b' fred is always allowed'
1366 1335 listing keys for "phases"
1367 1336 checking for updated bookmarks
1368 1337 listing keys for "bookmarks"
1369 invalid branch cache (served): tip differs
1370 1338 listing keys for "bookmarks"
1371 1339 3 changesets found
1372 1340 list of changesets:
@@ -1386,7 +1354,6 b' fred is always allowed'
1386 1354 bundle2-input-part: total payload size * (glob)
1387 1355 bundle2-input-part: "check:updated-heads" supported
1388 1356 bundle2-input-part: total payload size * (glob)
1389 invalid branch cache (served): tip differs
1390 1357 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1391 1358 adding changesets
1392 1359 add changeset ef1ea85a6374
@@ -1453,7 +1420,6 b' no one is allowed inside foo/Bar/'
1453 1420 listing keys for "phases"
1454 1421 checking for updated bookmarks
1455 1422 listing keys for "bookmarks"
1456 invalid branch cache (served): tip differs
1457 1423 listing keys for "bookmarks"
1458 1424 3 changesets found
1459 1425 list of changesets:
@@ -1473,7 +1439,6 b' no one is allowed inside foo/Bar/'
1473 1439 bundle2-input-part: total payload size * (glob)
1474 1440 bundle2-input-part: "check:updated-heads" supported
1475 1441 bundle2-input-part: total payload size * (glob)
1476 invalid branch cache (served): tip differs
1477 1442 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1478 1443 adding changesets
1479 1444 add changeset ef1ea85a6374
@@ -1536,7 +1501,6 b' OS-level groups'
1536 1501 listing keys for "phases"
1537 1502 checking for updated bookmarks
1538 1503 listing keys for "bookmarks"
1539 invalid branch cache (served): tip differs
1540 1504 listing keys for "bookmarks"
1541 1505 3 changesets found
1542 1506 list of changesets:
@@ -1556,7 +1520,6 b' OS-level groups'
1556 1520 bundle2-input-part: total payload size * (glob)
1557 1521 bundle2-input-part: "check:updated-heads" supported
1558 1522 bundle2-input-part: total payload size * (glob)
1559 invalid branch cache (served): tip differs
1560 1523 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1561 1524 adding changesets
1562 1525 add changeset ef1ea85a6374
@@ -1623,7 +1586,6 b' OS-level groups'
1623 1586 listing keys for "phases"
1624 1587 checking for updated bookmarks
1625 1588 listing keys for "bookmarks"
1626 invalid branch cache (served): tip differs
1627 1589 listing keys for "bookmarks"
1628 1590 3 changesets found
1629 1591 list of changesets:
@@ -1643,7 +1605,6 b' OS-level groups'
1643 1605 bundle2-input-part: total payload size * (glob)
1644 1606 bundle2-input-part: "check:updated-heads" supported
1645 1607 bundle2-input-part: total payload size * (glob)
1646 invalid branch cache (served): tip differs
1647 1608 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1648 1609 adding changesets
1649 1610 add changeset ef1ea85a6374
@@ -1797,7 +1758,6 b' No branch acls specified'
1797 1758 bundle2-input-part: total payload size * (glob)
1798 1759 bundle2-input-bundle: 5 parts total
1799 1760 updating the branch cache
1800 invalid branch cache (served.hidden): tip differs
1801 1761 added 4 changesets with 4 changes to 4 files (+1 heads)
1802 1762 bundle2-output-bundle: "HG20", 1 parts total
1803 1763 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
@@ -2104,7 +2064,6 b' Branch acl allow other'
2104 2064 bundle2-input-part: total payload size * (glob)
2105 2065 bundle2-input-bundle: 5 parts total
2106 2066 updating the branch cache
2107 invalid branch cache (served.hidden): tip differs
2108 2067 added 4 changesets with 4 changes to 4 files (+1 heads)
2109 2068 bundle2-output-bundle: "HG20", 1 parts total
2110 2069 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
@@ -2196,7 +2155,6 b' push foobar into the remote'
2196 2155 bundle2-input-part: total payload size * (glob)
2197 2156 bundle2-input-bundle: 5 parts total
2198 2157 updating the branch cache
2199 invalid branch cache (served.hidden): tip differs
2200 2158 added 4 changesets with 4 changes to 4 files (+1 heads)
2201 2159 bundle2-output-bundle: "HG20", 1 parts total
2202 2160 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
@@ -2360,7 +2318,6 b" User 'astro' must not be denied"
2360 2318 bundle2-input-part: total payload size * (glob)
2361 2319 bundle2-input-bundle: 5 parts total
2362 2320 updating the branch cache
2363 invalid branch cache (served.hidden): tip differs
2364 2321 added 4 changesets with 4 changes to 4 files (+1 heads)
2365 2322 bundle2-output-bundle: "HG20", 1 parts total
2366 2323 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
@@ -127,13 +127,11 b' clone, commit, pull'
127 127 added 1 changesets with 1 changes to 1 files
128 128 new changesets d02f48003e62
129 129 (run 'hg update' to get a working copy)
130 $ hg blackbox -l 6
130 $ hg blackbox -l 4
131 131 1970-01-01 00:00:00.000 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> wrote branch cache (served) with 1 labels and 2 nodes
132 1970-01-01 00:00:00.000 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> updated branch cache (served.hidden) in * seconds (glob)
133 1970-01-01 00:00:00.000 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> wrote branch cache (served.hidden) with 1 labels and 2 nodes
134 132 1970-01-01 00:00:00.000 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> 1 incoming changes - new heads: d02f48003e62
135 133 1970-01-01 00:00:00.000 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> pull exited 0 after * seconds (glob)
136 1970-01-01 00:00:00.000 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> blackbox -l 6
134 1970-01-01 00:00:00.000 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> blackbox -l 4
137 135
138 136 we must not cause a failure if we cannot write to the log
139 137
@@ -190,13 +188,11 b' backup bundles get logged'
190 188 $ hg strip tip
191 189 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
192 190 saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob)
193 $ hg blackbox -l 6
191 $ hg blackbox -l 4
194 192 1970-01-01 00:00:00.000 bob @73f6ee326b27d820b0472f1a825e3a50f3dc489b (5000)> strip tip
195 193 1970-01-01 00:00:00.000 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/73f6ee326b27-7612e004-backup.hg
196 1970-01-01 00:00:00.000 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> updated branch cache (immutable) in * seconds (glob)
197 1970-01-01 00:00:00.000 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> wrote branch cache (immutable) with 1 labels and 2 nodes
198 194 1970-01-01 00:00:00.000 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> strip tip exited 0 after * seconds (glob)
199 1970-01-01 00:00:00.000 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> blackbox -l 6
195 1970-01-01 00:00:00.000 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> blackbox -l 4
200 196
201 197 extension and python hooks - use the eol extension for a pythonhook
202 198
@@ -1,4 +1,5 b''
1 1 #testcases mmap nommap
2 #testcases v2 v3
2 3
3 4 #if mmap
4 5 $ cat <<EOF >> $HGRCPATH
@@ -7,6 +8,18 b''
7 8 > EOF
8 9 #endif
9 10
11 #if v3
12 $ cat <<EOF >> $HGRCPATH
13 > [experimental]
14 > branch-cache-v3=yes
15 > EOF
16 #else
17 $ cat <<EOF >> $HGRCPATH
18 > [experimental]
19 > branch-cache-v3=no
20 > EOF
21 #endif
22
10 23 $ hg init a
11 24 $ cd a
12 25
@@ -825,6 +838,7 b' recovery from invalid cache revs file wi'
825 838 truncating cache/rbc-revs-v1 to 160
826 839 $ f --size .hg/cache/rbc-revs*
827 840 .hg/cache/rbc-revs-v1: size=160
841
828 842 recovery from invalid cache file with partial last record
829 843 $ mv .hg/cache/rbc-revs-v1 .
830 844 $ f -qDB 119 rbc-revs-v1 > .hg/cache/rbc-revs-v1
@@ -835,6 +849,7 b' recovery from invalid cache file with pa'
835 849 truncating cache/rbc-revs-v1 to 112
836 850 $ f --size .hg/cache/rbc-revs*
837 851 .hg/cache/rbc-revs-v1: size=160
852
838 853 recovery from invalid cache file with missing record - no truncation
839 854 $ mv .hg/cache/rbc-revs-v1 .
840 855 $ f -qDB 112 rbc-revs-v1 > .hg/cache/rbc-revs-v1
@@ -842,6 +857,7 b' recovery from invalid cache file with mi'
842 857 5
843 858 $ f --size .hg/cache/rbc-revs*
844 859 .hg/cache/rbc-revs-v1: size=160
860
845 861 recovery from invalid cache file with some bad records
846 862 $ mv .hg/cache/rbc-revs-v1 .
847 863 $ f -qDB 8 rbc-revs-v1 > .hg/cache/rbc-revs-v1
@@ -851,7 +867,7 b' recovery from invalid cache file with so'
851 867 $ f --size .hg/cache/rbc-revs*
852 868 .hg/cache/rbc-revs-v1: size=120
853 869 $ hg log -r 'branch(.)' -T '{rev} ' --debug
854 history modification detected - truncating revision branch cache to revision 13
870 history modification detected - truncating revision branch cache to revision * (glob)
855 871 history modification detected - truncating revision branch cache to revision 1
856 872 3 4 8 9 10 11 12 13 truncating cache/rbc-revs-v1 to 8
857 873 $ rm -f .hg/cache/branch* && hg head a -T '{rev}\n' --debug
@@ -860,6 +876,7 b' recovery from invalid cache file with so'
860 876 $ f --size --hexdump --bytes=16 .hg/cache/rbc-revs*
861 877 .hg/cache/rbc-revs-v1: size=160
862 878 0000: 19 70 9c 5a 00 00 00 00 dd 6b 44 0d 00 00 00 01 |.p.Z.....kD.....|
879
863 880 cache is updated when committing
864 881 $ hg branch i-will-regret-this
865 882 marked working directory as branch i-will-regret-this
@@ -867,30 +884,17 b' cache is updated when committing'
867 884 $ f --size .hg/cache/rbc-*
868 885 .hg/cache/rbc-names-v1: size=111
869 886 .hg/cache/rbc-revs-v1: size=168
887
870 888 update after rollback - the cache will be correct but rbc-names will will still
871 889 contain the branch name even though it no longer is used
872 890 $ hg up -qr '.^'
873 891 $ hg rollback -qf
874 $ f --size --hexdump .hg/cache/rbc-*
892 $ f --size .hg/cache/rbc-names-*
875 893 .hg/cache/rbc-names-v1: size=111
876 0000: 64 65 66 61 75 6c 74 00 61 00 62 00 63 00 61 20 |default.a.b.c.a |
877 0010: 62 72 61 6e 63 68 20 6e 61 6d 65 20 6d 75 63 68 |branch name much|
878 0020: 20 6c 6f 6e 67 65 72 20 74 68 61 6e 20 74 68 65 | longer than the|
879 0030: 20 64 65 66 61 75 6c 74 20 6a 75 73 74 69 66 69 | default justifi|
880 0040: 63 61 74 69 6f 6e 20 75 73 65 64 20 62 79 20 62 |cation used by b|
881 0050: 72 61 6e 63 68 65 73 00 6d 00 6d 64 00 69 2d 77 |ranches.m.md.i-w|
882 0060: 69 6c 6c 2d 72 65 67 72 65 74 2d 74 68 69 73 |ill-regret-this|
894 $ grep "i-will-regret-this" .hg/cache/rbc-names-* > /dev/null
895 $ f --size .hg/cache/rbc-revs-*
883 896 .hg/cache/rbc-revs-v1: size=160
884 0000: 19 70 9c 5a 00 00 00 00 dd 6b 44 0d 00 00 00 01 |.p.Z.....kD.....|
885 0010: 88 1f e2 b9 00 00 00 01 ac 22 03 33 00 00 00 02 |.........".3....|
886 0020: ae e3 9c d1 00 00 00 02 d8 cb c6 1d 00 00 00 01 |................|
887 0030: 58 97 36 a2 00 00 00 03 10 ff 58 95 00 00 00 04 |X.6.......X.....|
888 0040: ee bb 94 44 00 00 00 02 5f 40 61 bb 00 00 00 02 |...D...._@a.....|
889 0050: bf be 84 1b 00 00 00 02 d3 f1 63 45 80 00 00 02 |..........cE....|
890 0060: e3 d4 9c 05 80 00 00 02 e2 3b 55 05 00 00 00 02 |.........;U.....|
891 0070: f8 94 c2 56 80 00 00 03 f3 44 76 37 00 00 00 05 |...V.....Dv7....|
892 0080: a5 8c a5 d3 00 00 00 05 df 34 3b 0d 00 00 00 05 |.........4;.....|
893 0090: c9 14 c9 9f 00 00 00 06 cd 21 a8 0b 80 00 00 05 |.........!......|
897
894 898 cache is updated/truncated when stripping - it is thus very hard to get in a
895 899 situation where the cache is out of sync and the hash check detects it
896 900 $ hg --config extensions.strip= strip -r tip --nob
@@ -902,38 +906,30 b' cache is rebuilt when corruption is dete'
902 906 $ hg log -r '5:&branch(.)' -T '{rev} ' --debug
903 907 referenced branch names not found - rebuilding revision branch cache from scratch
904 908 8 9 10 11 12 13 truncating cache/rbc-revs-v1 to 40
905 $ f --size --hexdump .hg/cache/rbc-*
909 $ f --size .hg/cache/rbc-names-*
906 910 .hg/cache/rbc-names-v1: size=84
907 0000: 62 00 61 00 63 00 61 20 62 72 61 6e 63 68 20 6e |b.a.c.a branch n|
908 0010: 61 6d 65 20 6d 75 63 68 20 6c 6f 6e 67 65 72 20 |ame much longer |
909 0020: 74 68 61 6e 20 74 68 65 20 64 65 66 61 75 6c 74 |than the default|
910 0030: 20 6a 75 73 74 69 66 69 63 61 74 69 6f 6e 20 75 | justification u|
911 0040: 73 65 64 20 62 79 20 62 72 61 6e 63 68 65 73 00 |sed by branches.|
912 0050: 6d 00 6d 64 |m.md|
911 $ grep "i-will-regret-this" .hg/cache/rbc-names-* > /dev/null
912 [1]
913 $ f --size .hg/cache/rbc-revs-*
913 914 .hg/cache/rbc-revs-v1: size=152
914 0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
915 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
916 0020: 00 00 00 00 00 00 00 00 d8 cb c6 1d 00 00 00 01 |................|
917 0030: 58 97 36 a2 00 00 00 02 10 ff 58 95 00 00 00 03 |X.6.......X.....|
918 0040: ee bb 94 44 00 00 00 00 5f 40 61 bb 00 00 00 00 |...D...._@a.....|
919 0050: bf be 84 1b 00 00 00 00 d3 f1 63 45 80 00 00 00 |..........cE....|
920 0060: e3 d4 9c 05 80 00 00 00 e2 3b 55 05 00 00 00 00 |.........;U.....|
921 0070: f8 94 c2 56 80 00 00 02 f3 44 76 37 00 00 00 04 |...V.....Dv7....|
922 0080: a5 8c a5 d3 00 00 00 04 df 34 3b 0d 00 00 00 04 |.........4;.....|
923 0090: c9 14 c9 9f 00 00 00 05 |........|
924 915
925 916 Test that cache files are created and grows correctly:
926 917
927 918 $ rm .hg/cache/rbc*
928 919 $ hg log -r "5 & branch(5)" -T "{rev}\n"
929 920 5
930 $ f --size --hexdump .hg/cache/rbc-*
921
922 (here v3 is querying branch info for heads so it warm much more of the cache)
923
924 #if v2
925 $ f --size .hg/cache/rbc-*
931 926 .hg/cache/rbc-names-v1: size=1
932 0000: 61 |a|
933 927 .hg/cache/rbc-revs-v1: size=48
934 0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
935 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
936 0020: 00 00 00 00 00 00 00 00 d8 cb c6 1d 00 00 00 00 |................|
928 #else
929 $ f --size .hg/cache/rbc-*
930 .hg/cache/rbc-names-v1: size=84
931 .hg/cache/rbc-revs-v1: size=152
932 #endif
937 933
938 934 $ cd ..
939 935
@@ -948,22 +944,20 b' Test for multiple incorrect branch cache'
948 944 $ hg branch -q branch
949 945 $ hg ci -Amf
950 946
951 $ f --size --hexdump .hg/cache/rbc-*
952 .hg/cache/rbc-names-v1: size=14
953 0000: 64 65 66 61 75 6c 74 00 62 72 61 6e 63 68 |default.branch|
954 .hg/cache/rbc-revs-v1: size=24
955 0000: 66 e5 f5 aa 00 00 00 00 fa 4c 04 e5 00 00 00 00 |f........L......|
956 0010: 56 46 78 69 00 00 00 01 |VFxi....|
947 #if v2
948
949 $ f --size --sha256 .hg/cache/rbc-*
950 .hg/cache/rbc-names-v1: size=14, sha256=d376f7eea9a7e28fac6470e78dae753c81a5543c9ad436e96999590e004a281c
951 .hg/cache/rbc-revs-v1: size=24, sha256=ec89032fd4e66e7282cb6e403848c681a855a9c36c6b44d19179218553b78779
952
957 953 $ : > .hg/cache/rbc-revs-v1
958 954
959 955 No superfluous rebuilding of cache:
960 956 $ hg log -r "branch(null)&branch(branch)" --debug
961 $ f --size --hexdump .hg/cache/rbc-*
962 .hg/cache/rbc-names-v1: size=14
963 0000: 64 65 66 61 75 6c 74 00 62 72 61 6e 63 68 |default.branch|
964 .hg/cache/rbc-revs-v1: size=24
965 0000: 66 e5 f5 aa 00 00 00 00 fa 4c 04 e5 00 00 00 00 |f........L......|
966 0010: 56 46 78 69 00 00 00 01 |VFxi....|
957 $ f --size --sha256 .hg/cache/rbc-*
958 .hg/cache/rbc-names-v1: size=14, sha256=d376f7eea9a7e28fac6470e78dae753c81a5543c9ad436e96999590e004a281c
959 .hg/cache/rbc-revs-v1: size=24, sha256=ec89032fd4e66e7282cb6e403848c681a855a9c36c6b44d19179218553b78779
960 #endif
967 961
968 962 $ cd ..
969 963
@@ -1316,9 +1310,15 b' Unbundling revision should warm the serv'
1316 1310 new changesets 2ab8003a1750:99ba08759bc7
1317 1311 updating to branch A
1318 1312 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1319 $ cat branchmap-update-01/.hg/cache/branch2-served
1313 #if v3
1314 $ cat branchmap-update-01/.hg/cache/branch3-exp-base
1315 tip-node=99ba08759bc7f6fdbe5304e83d0387f35c082479 tip-rev=1 topo-mode=pure
1316 A
1317 #else
1318 $ cat branchmap-update-01/.hg/cache/branch2-base
1320 1319 99ba08759bc7f6fdbe5304e83d0387f35c082479 1
1321 1320 99ba08759bc7f6fdbe5304e83d0387f35c082479 o A
1321 #endif
1322 1322 $ hg -R branchmap-update-01 unbundle bundle.hg
1323 1323 adding changesets
1324 1324 adding manifests
@@ -1326,9 +1326,15 b' Unbundling revision should warm the serv'
1326 1326 added 2 changesets with 0 changes to 0 files
1327 1327 new changesets a3b807b3ff0b:71ca9a6d524e (2 drafts)
1328 1328 (run 'hg update' to get a working copy)
1329 #if v3
1330 $ cat branchmap-update-01/.hg/cache/branch3-exp-served
1331 tip-node=71ca9a6d524ed3c2a215119b2086ac3b8c4c8286 tip-rev=3 topo-mode=pure
1332 A
1333 #else
1329 1334 $ cat branchmap-update-01/.hg/cache/branch2-served
1330 1335 71ca9a6d524ed3c2a215119b2086ac3b8c4c8286 3
1331 1336 71ca9a6d524ed3c2a215119b2086ac3b8c4c8286 o A
1337 #endif
1332 1338
1333 1339 aborted Unbundle should not update the on disk cache
1334 1340
@@ -1350,9 +1356,15 b' aborted Unbundle should not update the o'
1350 1356 updating to branch A
1351 1357 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1352 1358
1353 $ cat branchmap-update-02/.hg/cache/branch2-served
1359 #if v3
1360 $ cat branchmap-update-02/.hg/cache/branch3-exp-base
1361 tip-node=99ba08759bc7f6fdbe5304e83d0387f35c082479 tip-rev=1 topo-mode=pure
1362 A
1363 #else
1364 $ cat branchmap-update-02/.hg/cache/branch2-base
1354 1365 99ba08759bc7f6fdbe5304e83d0387f35c082479 1
1355 1366 99ba08759bc7f6fdbe5304e83d0387f35c082479 o A
1367 #endif
1356 1368 $ hg -R branchmap-update-02 unbundle bundle.hg --config "hooks.pretxnclose=python:$TESTTMP/simplehook.py:hook"
1357 1369 adding changesets
1358 1370 adding manifests
@@ -1361,6 +1373,12 b' aborted Unbundle should not update the o'
1361 1373 rollback completed
1362 1374 abort: pretxnclose hook failed
1363 1375 [40]
1364 $ cat branchmap-update-02/.hg/cache/branch2-served
1376 #if v3
1377 $ cat branchmap-update-02/.hg/cache/branch3-exp-base
1378 tip-node=99ba08759bc7f6fdbe5304e83d0387f35c082479 tip-rev=1 topo-mode=pure
1379 A
1380 #else
1381 $ cat branchmap-update-02/.hg/cache/branch2-base
1365 1382 99ba08759bc7f6fdbe5304e83d0387f35c082479 1
1366 1383 99ba08759bc7f6fdbe5304e83d0387f35c082479 o A
1384 #endif
This diff has been collapsed as it changes many lines, (701 lines changed) Show them Hide them
@@ -109,150 +109,18 b' Check that the clone went well'
109 109 Check uncompressed
110 110 ==================
111 111
112 Cannot stream clone when server.uncompressed is set
112 Cannot stream clone when server.uncompressed is set to false
113 ------------------------------------------------------------
114
115 When `server.uncompressed` is disabled, the client should fallback to a bundle
116 based clone with a warning.
117
113 118
114 119 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=stream_out'
115 120 200 Script output follows
116 121
117 122 1
118 123
119 #if stream-legacy
120 $ hg debugcapabilities http://localhost:$HGPORT
121 Main capabilities:
122 batch
123 branchmap
124 $USUAL_BUNDLE2_CAPS_SERVER$
125 changegroupsubset
126 compression=$BUNDLE2_COMPRESSIONS$
127 getbundle
128 httpheader=1024
129 httpmediatype=0.1rx,0.1tx,0.2tx
130 known
131 lookup
132 pushkey
133 unbundle=HG10GZ,HG10BZ,HG10UN
134 unbundlehash
135 Bundle2 capabilities:
136 HG20
137 bookmarks
138 changegroup
139 01
140 02
141 03
142 checkheads
143 related
144 digests
145 md5
146 sha1
147 sha512
148 error
149 abort
150 unsupportedcontent
151 pushraced
152 pushkey
153 hgtagsfnodes
154 listkeys
155 phases
156 heads
157 pushkey
158 remote-changegroup
159 http
160 https
161
162 $ hg clone --stream -U http://localhost:$HGPORT server-disabled
163 warning: stream clone requested but server has them disabled
164 requesting all changes
165 adding changesets
166 adding manifests
167 adding file changes
168 added 3 changesets with 1088 changes to 1088 files
169 new changesets 96ee1d7354c4:5223b5e3265f
170
171 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=getbundle' content-type --bodyfile body --hgproto 0.2 --requestheader "x-hgarg-1=bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=0&common=0000000000000000000000000000000000000000&heads=c17445101a72edac06facd130d14808dfbd5c7c2&stream=1"
172 200 Script output follows
173 content-type: application/mercurial-0.2
174
175
176 $ f --size body --hexdump --bytes 100
177 body: size=140
178 0000: 04 6e 6f 6e 65 48 47 32 30 00 00 00 00 00 00 00 |.noneHG20.......|
179 0010: 73 0b 45 52 52 4f 52 3a 41 42 4f 52 54 00 00 00 |s.ERROR:ABORT...|
180 0020: 00 01 01 07 3c 04 16 6d 65 73 73 61 67 65 73 74 |....<..messagest|
181 0030: 72 65 61 6d 20 64 61 74 61 20 72 65 71 75 65 73 |ream data reques|
182 0040: 74 65 64 20 62 75 74 20 73 65 72 76 65 72 20 64 |ted but server d|
183 0050: 6f 65 73 20 6e 6f 74 20 61 6c 6c 6f 77 20 74 68 |oes not allow th|
184 0060: 69 73 20 66 |is f|
185
186 #endif
187 #if stream-bundle2-v2
188 $ hg debugcapabilities http://localhost:$HGPORT
189 Main capabilities:
190 batch
191 branchmap
192 $USUAL_BUNDLE2_CAPS_SERVER$
193 changegroupsubset
194 compression=$BUNDLE2_COMPRESSIONS$
195 getbundle
196 httpheader=1024
197 httpmediatype=0.1rx,0.1tx,0.2tx
198 known
199 lookup
200 pushkey
201 unbundle=HG10GZ,HG10BZ,HG10UN
202 unbundlehash
203 Bundle2 capabilities:
204 HG20
205 bookmarks
206 changegroup
207 01
208 02
209 03
210 checkheads
211 related
212 digests
213 md5
214 sha1
215 sha512
216 error
217 abort
218 unsupportedcontent
219 pushraced
220 pushkey
221 hgtagsfnodes
222 listkeys
223 phases
224 heads
225 pushkey
226 remote-changegroup
227 http
228 https
229
230 $ hg clone --stream -U http://localhost:$HGPORT server-disabled
231 warning: stream clone requested but server has them disabled
232 requesting all changes
233 adding changesets
234 adding manifests
235 adding file changes
236 added 3 changesets with 1088 changes to 1088 files
237 new changesets 96ee1d7354c4:5223b5e3265f
238
239 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=getbundle' content-type --bodyfile body --hgproto 0.2 --requestheader "x-hgarg-1=bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=0&common=0000000000000000000000000000000000000000&heads=c17445101a72edac06facd130d14808dfbd5c7c2&stream=1"
240 200 Script output follows
241 content-type: application/mercurial-0.2
242
243
244 $ f --size body --hexdump --bytes 100
245 body: size=140
246 0000: 04 6e 6f 6e 65 48 47 32 30 00 00 00 00 00 00 00 |.noneHG20.......|
247 0010: 73 0b 45 52 52 4f 52 3a 41 42 4f 52 54 00 00 00 |s.ERROR:ABORT...|
248 0020: 00 01 01 07 3c 04 16 6d 65 73 73 61 67 65 73 74 |....<..messagest|
249 0030: 72 65 61 6d 20 64 61 74 61 20 72 65 71 75 65 73 |ream data reques|
250 0040: 74 65 64 20 62 75 74 20 73 65 72 76 65 72 20 64 |ted but server d|
251 0050: 6f 65 73 20 6e 6f 74 20 61 6c 6c 6f 77 20 74 68 |oes not allow th|
252 0060: 69 73 20 66 |is f|
253
254 #endif
255 #if stream-bundle2-v3
256 124 $ hg debugcapabilities http://localhost:$HGPORT
257 125 Main capabilities:
258 126 batch
@@ -304,23 +172,6 b' Cannot stream clone when server.uncompre'
304 172 added 3 changesets with 1088 changes to 1088 files
305 173 new changesets 96ee1d7354c4:5223b5e3265f
306 174
307 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=getbundle' content-type --bodyfile body --hgproto 0.2 --requestheader "x-hgarg-1=bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=0&common=0000000000000000000000000000000000000000&heads=c17445101a72edac06facd130d14808dfbd5c7c2&stream=1"
308 200 Script output follows
309 content-type: application/mercurial-0.2
310
311
312 $ f --size body --hexdump --bytes 100
313 body: size=140
314 0000: 04 6e 6f 6e 65 48 47 32 30 00 00 00 00 00 00 00 |.noneHG20.......|
315 0010: 73 0b 45 52 52 4f 52 3a 41 42 4f 52 54 00 00 00 |s.ERROR:ABORT...|
316 0020: 00 01 01 07 3c 04 16 6d 65 73 73 61 67 65 73 74 |....<..messagest|
317 0030: 72 65 61 6d 20 64 61 74 61 20 72 65 71 75 65 73 |ream data reques|
318 0040: 74 65 64 20 62 75 74 20 73 65 72 76 65 72 20 64 |ted but server d|
319 0050: 6f 65 73 20 6e 6f 74 20 61 6c 6c 6f 77 20 74 68 |oes not allow th|
320 0060: 69 73 20 66 |is f|
321
322 #endif
323
324 175 $ killdaemons.py
325 176 $ cd server
326 177 $ hg serve -p $HGPORT -d --pid-file=hg.pid --error errors.txt
@@ -328,6 +179,13 b' Cannot stream clone when server.uncompre'
328 179 $ cd ..
329 180
330 181 Basic clone
182 -----------
183
184 Check that --stream trigger a stream clone and result in a valid repositoty
185
186 We check the associated output for exact bytes on file number as changes in
187 these value implies changes in the data transfered and can detect unintended
188 changes in the process.
331 189
332 190 #if stream-legacy
333 191 $ hg clone --stream -U http://localhost:$HGPORT clone1
@@ -338,7 +196,6 b' Basic clone'
338 196 transferred 98.8 KB in * seconds (* */sec) (glob) (zstd !)
339 197 searching for changes
340 198 no changes found
341 $ cat server/errors.txt
342 199 #endif
343 200 #if stream-bundle2-v2
344 201 $ hg clone --stream -U http://localhost:$HGPORT clone1
@@ -349,20 +206,8 b' Basic clone'
349 206 transferred 98.9 KB in * seconds (* */sec) (glob) (zstd no-rust !)
350 207 1096 files to transfer, 99.0 KB of data (zstd rust !)
351 208 transferred 99.0 KB in * seconds (* */sec) (glob) (zstd rust !)
209 #endif
352 210
353 $ ls -1 clone1/.hg/cache
354 branch2-base
355 branch2-immutable
356 branch2-served
357 branch2-served.hidden
358 branch2-visible
359 branch2-visible-hidden
360 rbc-names-v1
361 rbc-revs-v1
362 tags2
363 tags2-served
364 $ cat server/errors.txt
365 #endif
366 211 #if stream-bundle2-v3
367 212 $ hg clone --stream -U http://localhost:$HGPORT clone1
368 213 streaming all changes
@@ -370,244 +215,68 b' Basic clone'
370 215 transferred 102 KB in * seconds (* */sec) (glob) (no-zstd !)
371 216 transferred 98.9 KB in * seconds (* */sec) (glob) (zstd no-rust !)
372 217 transferred 99.0 KB in * seconds (* */sec) (glob) (zstd rust !)
218 #endif
373 219
220 #if no-stream-legacy
374 221 $ ls -1 clone1/.hg/cache
375 222 branch2-base
376 branch2-immutable
377 223 branch2-served
378 branch2-served.hidden
379 branch2-visible
380 branch2-visible-hidden
381 224 rbc-names-v1
382 225 rbc-revs-v1
383 226 tags2
384 227 tags2-served
385 $ cat server/errors.txt
386 228 #endif
387 229
230 $ hg -R clone1 verify --quiet
231 $ cat server/errors.txt
232
388 233 getbundle requests with stream=1 are uncompressed
234 -------------------------------------------------
235
236 We check that `getbundle` will return a stream bundle when requested.
237
238 XXX manually building the --requestheader is fragile and will drift away from actual usage
389 239
390 240 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=getbundle' content-type --bodyfile body --hgproto '0.1 0.2 comp=zlib,none' --requestheader "x-hgarg-1=bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Astream%253Dv2&cg=0&common=0000000000000000000000000000000000000000&heads=c17445101a72edac06facd130d14808dfbd5c7c2&stream=1"
391 241 200 Script output follows
392 242 content-type: application/mercurial-0.2
393 243
394 244
395 #if no-zstd no-rust
396 $ f --size --hex --bytes 256 body
397 body: size=119140
398 0000: 04 6e 6f 6e 65 48 47 32 30 00 00 00 00 00 00 00 |.noneHG20.......|
399 0010: 62 07 53 54 52 45 41 4d 32 00 00 00 00 03 00 09 |b.STREAM2.......|
400 0020: 06 09 04 0c 26 62 79 74 65 63 6f 75 6e 74 31 30 |....&bytecount10|
401 0030: 34 31 31 35 66 69 6c 65 63 6f 75 6e 74 31 30 39 |4115filecount109|
402 0040: 34 72 65 71 75 69 72 65 6d 65 6e 74 73 67 65 6e |4requirementsgen|
403 0050: 65 72 61 6c 64 65 6c 74 61 25 32 43 72 65 76 6c |eraldelta%2Crevl|
404 0060: 6f 67 76 31 25 32 43 73 70 61 72 73 65 72 65 76 |ogv1%2Csparserev|
405 0070: 6c 6f 67 00 00 80 00 73 08 42 64 61 74 61 2f 30 |log....s.Bdata/0|
406 0080: 2e 69 00 03 00 01 00 00 00 00 00 00 00 02 00 00 |.i..............|
407 0090: 00 01 00 00 00 00 00 00 00 01 ff ff ff ff ff ff |................|
408 00a0: ff ff 80 29 63 a0 49 d3 23 87 bf ce fe 56 67 92 |...)c.I.#....Vg.|
409 00b0: 67 2c 69 d1 ec 39 00 00 00 00 00 00 00 00 00 00 |g,i..9..........|
410 00c0: 00 00 75 30 73 26 45 64 61 74 61 2f 30 30 63 68 |..u0s&Edata/00ch|
411 00d0: 61 6e 67 65 6c 6f 67 2d 61 62 33 34 39 31 38 30 |angelog-ab349180|
412 00e0: 61 30 34 30 35 30 31 30 2e 6e 64 2e 69 00 03 00 |a0405010.nd.i...|
413 00f0: 01 00 00 00 00 00 00 00 05 00 00 00 04 00 00 00 |................|
414 #endif
415 #if zstd no-rust
416 $ f --size --hex --bytes 256 body
417 body: size=116327 (no-bigendian !)
418 body: size=116322 (bigendian !)
245 $ f --size --hex --bytes 48 body
246 body: size=* (glob)
419 247 0000: 04 6e 6f 6e 65 48 47 32 30 00 00 00 00 00 00 00 |.noneHG20.......|
420 0010: 7c 07 53 54 52 45 41 4d 32 00 00 00 00 03 00 09 ||.STREAM2.......|
421 0020: 06 09 04 0c 40 62 79 74 65 63 6f 75 6e 74 31 30 |....@bytecount10|
422 0030: 31 32 37 36 66 69 6c 65 63 6f 75 6e 74 31 30 39 |1276filecount109| (no-bigendian !)
423 0030: 31 32 37 31 66 69 6c 65 63 6f 75 6e 74 31 30 39 |1271filecount109| (bigendian !)
424 0040: 34 72 65 71 75 69 72 65 6d 65 6e 74 73 67 65 6e |4requirementsgen|
425 0050: 65 72 61 6c 64 65 6c 74 61 25 32 43 72 65 76 6c |eraldelta%2Crevl|
426 0060: 6f 67 2d 63 6f 6d 70 72 65 73 73 69 6f 6e 2d 7a |og-compression-z|
427 0070: 73 74 64 25 32 43 72 65 76 6c 6f 67 76 31 25 32 |std%2Crevlogv1%2|
428 0080: 43 73 70 61 72 73 65 72 65 76 6c 6f 67 00 00 80 |Csparserevlog...|
429 0090: 00 73 08 42 64 61 74 61 2f 30 2e 69 00 03 00 01 |.s.Bdata/0.i....|
430 00a0: 00 00 00 00 00 00 00 02 00 00 00 01 00 00 00 00 |................|
431 00b0: 00 00 00 01 ff ff ff ff ff ff ff ff 80 29 63 a0 |.............)c.|
432 00c0: 49 d3 23 87 bf ce fe 56 67 92 67 2c 69 d1 ec 39 |I.#....Vg.g,i..9|
433 00d0: 00 00 00 00 00 00 00 00 00 00 00 00 75 30 73 26 |............u0s&|
434 00e0: 45 64 61 74 61 2f 30 30 63 68 61 6e 67 65 6c 6f |Edata/00changelo|
435 00f0: 67 2d 61 62 33 34 39 31 38 30 61 30 34 30 35 30 |g-ab349180a04050|
436 #endif
437 #if zstd rust no-dirstate-v2
438 $ f --size --hex --bytes 256 body
439 body: size=116310 (no-rust !)
440 body: size=116495 (rust no-stream-legacy no-bigendian !)
441 body: size=116490 (rust no-stream-legacy bigendian !)
442 body: size=116327 (rust stream-legacy no-bigendian !)
443 body: size=116322 (rust stream-legacy bigendian !)
444 0000: 04 6e 6f 6e 65 48 47 32 30 00 00 00 00 00 00 00 |.noneHG20.......|
445 0010: 7c 07 53 54 52 45 41 4d 32 00 00 00 00 03 00 09 ||.STREAM2.......|
446 0020: 06 09 04 0c 40 62 79 74 65 63 6f 75 6e 74 31 30 |....@bytecount10|
447 0030: 31 32 37 36 66 69 6c 65 63 6f 75 6e 74 31 30 39 |1276filecount109| (no-rust !)
448 0040: 33 72 65 71 75 69 72 65 6d 65 6e 74 73 67 65 6e |3requirementsgen| (no-rust !)
449 0030: 31 34 30 32 66 69 6c 65 63 6f 75 6e 74 31 30 39 |1402filecount109| (rust no-stream-legacy no-bigendian !)
450 0030: 31 33 39 37 66 69 6c 65 63 6f 75 6e 74 31 30 39 |1397filecount109| (rust no-stream-legacy bigendian !)
451 0040: 36 72 65 71 75 69 72 65 6d 65 6e 74 73 67 65 6e |6requirementsgen| (rust no-stream-legacy !)
452 0030: 31 32 37 36 66 69 6c 65 63 6f 75 6e 74 31 30 39 |1276filecount109| (rust stream-legacy no-bigendian !)
453 0030: 31 32 37 31 66 69 6c 65 63 6f 75 6e 74 31 30 39 |1271filecount109| (rust stream-legacy bigendian !)
454 0040: 34 72 65 71 75 69 72 65 6d 65 6e 74 73 67 65 6e |4requirementsgen| (rust stream-legacy !)
455 0050: 65 72 61 6c 64 65 6c 74 61 25 32 43 72 65 76 6c |eraldelta%2Crevl|
456 0060: 6f 67 2d 63 6f 6d 70 72 65 73 73 69 6f 6e 2d 7a |og-compression-z|
457 0070: 73 74 64 25 32 43 72 65 76 6c 6f 67 76 31 25 32 |std%2Crevlogv1%2|
458 0080: 43 73 70 61 72 73 65 72 65 76 6c 6f 67 00 00 80 |Csparserevlog...|
459 0090: 00 73 08 42 64 61 74 61 2f 30 2e 69 00 03 00 01 |.s.Bdata/0.i....|
460 00a0: 00 00 00 00 00 00 00 02 00 00 00 01 00 00 00 00 |................|
461 00b0: 00 00 00 01 ff ff ff ff ff ff ff ff 80 29 63 a0 |.............)c.|
462 00c0: 49 d3 23 87 bf ce fe 56 67 92 67 2c 69 d1 ec 39 |I.#....Vg.g,i..9|
463 00d0: 00 00 00 00 00 00 00 00 00 00 00 00 75 30 73 26 |............u0s&|
464 00e0: 45 64 61 74 61 2f 30 30 63 68 61 6e 67 65 6c 6f |Edata/00changelo|
465 00f0: 67 2d 61 62 33 34 39 31 38 30 61 30 34 30 35 30 |g-ab349180a04050|
466 #endif
467 #if zstd dirstate-v2
468 $ f --size --hex --bytes 256 body
469 body: size=109549
470 0000: 04 6e 6f 6e 65 48 47 32 30 00 00 00 00 00 00 00 |.noneHG20.......|
471 0010: c0 07 53 54 52 45 41 4d 32 00 00 00 00 03 00 09 |..STREAM2.......|
472 0020: 05 09 04 0c 85 62 79 74 65 63 6f 75 6e 74 39 35 |.....bytecount95|
473 0030: 38 39 37 66 69 6c 65 63 6f 75 6e 74 31 30 33 30 |897filecount1030|
474 0040: 72 65 71 75 69 72 65 6d 65 6e 74 73 64 6f 74 65 |requirementsdote|
475 0050: 6e 63 6f 64 65 25 32 43 65 78 70 2d 64 69 72 73 |ncode%2Cexp-dirs|
476 0060: 74 61 74 65 2d 76 32 25 32 43 66 6e 63 61 63 68 |tate-v2%2Cfncach|
477 0070: 65 25 32 43 67 65 6e 65 72 61 6c 64 65 6c 74 61 |e%2Cgeneraldelta|
478 0080: 25 32 43 70 65 72 73 69 73 74 65 6e 74 2d 6e 6f |%2Cpersistent-no|
479 0090: 64 65 6d 61 70 25 32 43 72 65 76 6c 6f 67 2d 63 |demap%2Crevlog-c|
480 00a0: 6f 6d 70 72 65 73 73 69 6f 6e 2d 7a 73 74 64 25 |ompression-zstd%|
481 00b0: 32 43 72 65 76 6c 6f 67 76 31 25 32 43 73 70 61 |2Crevlogv1%2Cspa|
482 00c0: 72 73 65 72 65 76 6c 6f 67 25 32 43 73 74 6f 72 |rserevlog%2Cstor|
483 00d0: 65 00 00 80 00 73 08 42 64 61 74 61 2f 30 2e 69 |e....s.Bdata/0.i|
484 00e0: 00 03 00 01 00 00 00 00 00 00 00 02 00 00 00 01 |................|
485 00f0: 00 00 00 00 00 00 00 01 ff ff ff ff ff ff ff ff |................|
486 #endif
248 0010: ?? 07 53 54 52 45 41 4d 32 00 00 00 00 03 00 09 |?.STREAM2.......| (glob)
249 0020: 06 09 04 0c ?? 62 79 74 65 63 6f 75 6e 74 31 30 |....?bytecount10| (glob)
487 250
488 251 --uncompressed is an alias to --stream
252 ---------------------------------------
489 253
490 #if stream-legacy
491 $ hg clone --uncompressed -U http://localhost:$HGPORT clone1-uncompressed
492 streaming all changes
493 1091 files to transfer, 102 KB of data (no-zstd !)
494 transferred 102 KB in * seconds (* */sec) (glob) (no-zstd !)
495 1091 files to transfer, 98.8 KB of data (zstd !)
496 transferred 98.8 KB in * seconds (* */sec) (glob) (zstd !)
497 searching for changes
498 no changes found
499 #endif
500 #if stream-bundle2-v2
254 The alias flag should trigger a stream clone too.
255
501 256 $ hg clone --uncompressed -U http://localhost:$HGPORT clone1-uncompressed
502 257 streaming all changes
503 1094 files to transfer, 102 KB of data (no-zstd !)
504 transferred 102 KB in * seconds (* */sec) (glob) (no-zstd !)
505 1094 files to transfer, 98.9 KB of data (zstd no-rust !)
506 transferred 98.9 KB in * seconds (* */sec) (glob) (zstd no-rust !)
507 1096 files to transfer, 99.0 KB of data (zstd rust !)
508 transferred 99.0 KB in * seconds (* */sec) (glob) (zstd rust !)
509 #endif
510 #if stream-bundle2-v3
511 $ hg clone --uncompressed -U http://localhost:$HGPORT clone1-uncompressed
512 streaming all changes
513 1093 entries to transfer
514 transferred 102 KB in * seconds (* */sec) (glob) (no-zstd !)
515 transferred 98.9 KB in * seconds (* */sec) (glob) (zstd no-rust !)
516 transferred 99.0 KB in * seconds (* */sec) (glob) (zstd rust !)
517 #endif
258 * files to transfer* (glob) (no-stream-bundle2-v3 !)
259 * entries to transfer (glob) (stream-bundle2-v3 !)
260 transferred * KB in * seconds (* */sec) (glob)
261 searching for changes (stream-legacy !)
262 no changes found (stream-legacy !)
518 263
519 264 Clone with background file closing enabled
265 -------------------------------------------
520 266
521 #if stream-legacy
522 $ hg --debug --config worker.backgroundclose=true --config worker.backgroundcloseminfilecount=1 clone --stream -U http://localhost:$HGPORT clone-background | grep -v adding
523 using http://localhost:$HGPORT/
524 sending capabilities command
525 sending branchmap command
526 streaming all changes
527 sending stream_out command
528 1091 files to transfer, 102 KB of data (no-zstd !)
529 1091 files to transfer, 98.8 KB of data (zstd !)
530 starting 4 threads for background file closing
531 updating the branch cache
532 transferred 102 KB in * seconds (* */sec) (glob) (no-zstd !)
533 transferred 98.8 KB in * seconds (* */sec) (glob) (zstd !)
534 query 1; heads
535 sending batch command
536 searching for changes
537 all remote heads known locally
538 no changes found
539 sending getbundle command
540 bundle2-input-bundle: with-transaction
541 bundle2-input-part: "listkeys" (params: 1 mandatory) supported
542 bundle2-input-part: "phase-heads" supported
543 bundle2-input-part: total payload size 24
544 bundle2-input-bundle: 2 parts total
545 checking for updated bookmarks
546 updating the branch cache
547 (sent 5 HTTP requests and * bytes; received * bytes in responses) (glob)
548 #endif
549 #if stream-bundle2-v2
550 $ hg --debug --config worker.backgroundclose=true --config worker.backgroundcloseminfilecount=1 clone --stream -U http://localhost:$HGPORT clone-background | grep -v adding
551 using http://localhost:$HGPORT/
552 sending capabilities command
553 query 1; heads
554 sending batch command
555 streaming all changes
556 sending getbundle command
557 bundle2-input-bundle: with-transaction
558 bundle2-input-part: "stream2" (params: 3 mandatory) supported
559 applying stream bundle
560 1094 files to transfer, 102 KB of data (no-zstd !)
561 1094 files to transfer, 98.9 KB of data (zstd no-rust !)
562 1096 files to transfer, 99.0 KB of data (zstd rust !)
563 starting 4 threads for background file closing
267 The backgound file closing logic should trigger when configured to do so, and
268 the result should be a valid repository.
269
270 $ hg --debug --config worker.backgroundclose=true --config worker.backgroundcloseminfilecount=1 clone --stream -U http://localhost:$HGPORT clone-background | grep "background file closing"
564 271 starting 4 threads for background file closing
565 updating the branch cache
566 transferred 102 KB in * seconds (* */sec) (glob) (no-zstd !)
567 bundle2-input-part: total payload size 119001 (no-zstd !)
568 transferred 98.9 KB in * seconds (* */sec) (glob) (zstd no-rust !)
569 transferred 99.0 KB in * seconds (* */sec) (glob) (zstd rust !)
570 bundle2-input-part: total payload size 116162 (zstd no-bigendian no-rust !)
571 bundle2-input-part: total payload size 116330 (zstd no-bigendian rust !)
572 bundle2-input-part: total payload size 116157 (zstd bigendian no-rust !)
573 bundle2-input-part: total payload size 116325 (zstd bigendian rust !)
574 bundle2-input-part: "listkeys" (params: 1 mandatory) supported
575 bundle2-input-bundle: 2 parts total
576 checking for updated bookmarks
577 updating the branch cache
578 (sent 3 HTTP requests and * bytes; received * bytes in responses) (glob)
579 #endif
580 #if stream-bundle2-v3
581 $ hg --debug --config worker.backgroundclose=true --config worker.backgroundcloseminfilecount=1 clone --stream -U http://localhost:$HGPORT clone-background | grep -v adding
582 using http://localhost:$HGPORT/
583 sending capabilities command
584 query 1; heads
585 sending batch command
586 streaming all changes
587 sending getbundle command
588 bundle2-input-bundle: with-transaction
589 bundle2-input-part: "stream3-exp" (params: 1 mandatory) supported
590 applying stream bundle
591 1093 entries to transfer
592 starting 4 threads for background file closing
593 starting 4 threads for background file closing
594 updating the branch cache
595 transferred 102 KB in * seconds (* */sec) (glob) (no-zstd !)
596 bundle2-input-part: total payload size 120096 (no-zstd !)
597 transferred 98.9 KB in * seconds (* */sec) (glob) (zstd no-rust !)
598 transferred 99.0 KB in * seconds (* */sec) (glob) (zstd rust !)
599 bundle2-input-part: total payload size 117257 (zstd no-rust no-bigendian !)
600 bundle2-input-part: total payload size 117425 (zstd rust no-bigendian !)
601 bundle2-input-part: total payload size 117252 (zstd bigendian no-rust !)
602 bundle2-input-part: total payload size 117420 (zstd bigendian rust !)
603 bundle2-input-part: "listkeys" (params: 1 mandatory) supported
604 bundle2-input-bundle: 2 parts total
605 checking for updated bookmarks
606 updating the branch cache
607 (sent 3 HTTP requests and * bytes; received * bytes in responses) (glob)
608 #endif
272 starting 4 threads for background file closing (no-stream-legacy !)
273 $ hg verify -R clone-background --quiet
609 274
610 275 Cannot stream clone when there are secret changesets
276 ----------------------------------------------------
277
278 If secret changeset are present the should not be cloned (by default) and the
279 clone falls back to a bundle clone.
611 280
612 281 $ hg -R server phase --force --secret -r tip
613 282 $ hg clone --stream -U http://localhost:$HGPORT secret-denied
@@ -622,44 +291,30 b' Cannot stream clone when there are secre'
622 291 $ killdaemons.py
623 292
624 293 Streaming of secrets can be overridden by server config
294 -------------------------------------------------------
295
296 Secret changeset can still be streamed if the server is configured to do so.
625 297
626 298 $ cd server
627 299 $ hg serve --config server.uncompressedallowsecret=true -p $HGPORT -d --pid-file=hg.pid
628 300 $ cat hg.pid > $DAEMON_PIDS
629 301 $ cd ..
630 302
631 #if stream-legacy
632 $ hg clone --stream -U http://localhost:$HGPORT secret-allowed
633 streaming all changes
634 1091 files to transfer, 102 KB of data (no-zstd !)
635 transferred 102 KB in * seconds (* */sec) (glob) (no-zstd !)
636 1091 files to transfer, 98.8 KB of data (zstd !)
637 transferred 98.8 KB in * seconds (* */sec) (glob) (zstd !)
638 searching for changes
639 no changes found
640 #endif
641 #if stream-bundle2-v2
642 303 $ hg clone --stream -U http://localhost:$HGPORT secret-allowed
643 304 streaming all changes
644 1094 files to transfer, 102 KB of data (no-zstd !)
645 transferred 102 KB in * seconds (* */sec) (glob) (no-zstd !)
646 1094 files to transfer, 98.9 KB of data (zstd no-rust !)
647 transferred 98.9 KB in * seconds (* */sec) (glob) (zstd no-rust !)
648 1096 files to transfer, 99.0 KB of data (zstd rust !)
649 transferred 99.0 KB in * seconds (* */sec) (glob) (zstd rust !)
650 #endif
651 #if stream-bundle2-v3
652 $ hg clone --stream -U http://localhost:$HGPORT secret-allowed
653 streaming all changes
654 1093 entries to transfer
655 transferred 102 KB in * seconds (* */sec) (glob) (no-zstd !)
656 transferred 98.9 KB in * seconds (* */sec) (glob) (zstd no-rust !)
657 transferred 99.0 KB in * seconds (* */sec) (glob) (zstd rust !)
658 #endif
305 * files to transfer* (glob) (no-stream-bundle2-v3 !)
306 * entries to transfer (glob) (stream-bundle2-v3 !)
307 transferred * KB in * seconds (* */sec) (glob)
308 searching for changes (stream-legacy !)
309 no changes found (stream-legacy !)
659 310
660 311 $ killdaemons.py
661 312
662 313 Verify interaction between preferuncompressed and secret presence
314 -----------------------------------------------------------------
315
316 Secret presence will still make the clone falls back to a normal bundle even if
317 the server prefers stream clone.
663 318
664 319 $ cd server
665 320 $ hg serve --config server.preferuncompressed=true -p $HGPORT -d --pid-file=hg.pid
@@ -677,6 +332,9 b' Verify interaction between preferuncompr'
677 332 $ killdaemons.py
678 333
679 334 Clone not allowed when full bundles disabled and can't serve secrets
335 --------------------------------------------------------------------
336
337 The clone should fail as no valid option is found.
680 338
681 339 $ cd server
682 340 $ hg serve --config server.disablefullbundle=true -p $HGPORT -d --pid-file=hg.pid
@@ -692,6 +350,8 b' Clone not allowed when full bundles disa'
692 350 [100]
693 351
694 352 Local stream clone with secrets involved
353 ----------------------------------------
354
695 355 (This is just a test over behavior: if you have access to the repo's files,
696 356 there is no security so it isn't important to prevent a clone here.)
697 357
@@ -704,12 +364,20 b" there is no security so it isn't importa"
704 364 added 2 changesets with 1025 changes to 1025 files
705 365 new changesets 96ee1d7354c4:c17445101a72
706 366
367 (revert introduction of secret changeset)
368
369 $ hg -R server phase --draft 'secret()'
370
707 371 Stream clone while repo is changing:
372 ------------------------------------
373
374 We should send a repository in a valid state, ignoring the ongoing transaction.
708 375
709 376 $ mkdir changing
710 377 $ cd changing
711 378
712 379 prepare repo with small and big file to cover both code paths in emitrevlogdata
380 (inlined revlog and non-inlined revlogs).
713 381
714 382 $ hg init repo
715 383 $ touch repo/f1
@@ -740,15 +408,14 b' actually serving file content'
740 408 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_3
741 409 $ hg -R clone id
742 410 000000000000
411 $ hg -R clone verify --quiet
743 412 $ cat errors.log
744 413 $ cd ..
745 414
746 415 Stream repository with bookmarks
747 416 --------------------------------
748 417
749 (revert introduction of secret changeset)
750
751 $ hg -R server phase --draft 'secret()'
418 The bookmark file should be send over in the stream bundle.
752 419
753 420 add a bookmark
754 421
@@ -756,40 +423,17 b' add a bookmark'
756 423
757 424 clone it
758 425
759 #if stream-legacy
760 $ hg clone --stream http://localhost:$HGPORT with-bookmarks
761 streaming all changes
762 1091 files to transfer, 102 KB of data (no-zstd !)
763 transferred 102 KB in * seconds (* */sec) (glob) (no-zstd !)
764 1091 files to transfer, 98.8 KB of data (zstd !)
765 transferred 98.8 KB in * seconds (* */sec) (glob) (zstd !)
766 searching for changes
767 no changes found
768 updating to branch default
769 1088 files updated, 0 files merged, 0 files removed, 0 files unresolved
770 #endif
771 #if stream-bundle2-v2
772 426 $ hg clone --stream http://localhost:$HGPORT with-bookmarks
773 427 streaming all changes
774 1097 files to transfer, 102 KB of data (no-zstd !)
775 transferred 102 KB in * seconds (* */sec) (glob) (no-zstd !)
776 1097 files to transfer, 99.1 KB of data (zstd no-rust !)
777 transferred 99.1 KB in * seconds (* */sec) (glob) (zstd no-rust !)
778 1099 files to transfer, 99.2 KB of data (zstd rust !)
779 transferred 99.2 KB in * seconds (* */sec) (glob) (zstd rust !)
428 1091 files to transfer, * KB of data (glob) (stream-legacy !)
429 1097 files to transfer, * KB of data (glob) (stream-bundle2-v2 no-rust !)
430 1099 files to transfer, * KB of data (glob) (stream-bundle2-v2 rust !)
431 1096 entries to transfer (stream-bundle2-v3 !)
432 transferred * KB in * seconds (* */sec) (glob)
433 searching for changes (stream-legacy !)
434 no changes found (stream-legacy !)
780 435 updating to branch default
781 436 1088 files updated, 0 files merged, 0 files removed, 0 files unresolved
782 #endif
783 #if stream-bundle2-v3
784 $ hg clone --stream http://localhost:$HGPORT with-bookmarks
785 streaming all changes
786 1096 entries to transfer
787 transferred 102 KB in * seconds (* */sec) (glob) (no-zstd !)
788 transferred 99.1 KB in * seconds (* */sec) (glob) (zstd no-rust !)
789 transferred 99.2 KB in * seconds (* */sec) (glob) (zstd rust !)
790 updating to branch default
791 1088 files updated, 0 files merged, 0 files removed, 0 files unresolved
792 #endif
793 437 $ hg verify -R with-bookmarks -q
794 438 $ hg -R with-bookmarks bookmarks
795 439 some-bookmark 2:5223b5e3265f
@@ -797,6 +441,9 b' clone it'
797 441 Stream repository with phases
798 442 -----------------------------
799 443
444 The file storing phases information (e.g. phaseroots) should be sent as part of
445 the stream bundle.
446
800 447 Clone as publishing
801 448
802 449 $ hg -R server phase -r 'all()'
@@ -804,40 +451,17 b' Clone as publishing'
804 451 1: draft
805 452 2: draft
806 453
807 #if stream-legacy
808 $ hg clone --stream http://localhost:$HGPORT phase-publish
809 streaming all changes
810 1091 files to transfer, 102 KB of data (no-zstd !)
811 transferred 102 KB in * seconds (* */sec) (glob) (no-zstd !)
812 1091 files to transfer, 98.8 KB of data (zstd !)
813 transferred 98.8 KB in * seconds (* */sec) (glob) (zstd !)
814 searching for changes
815 no changes found
816 updating to branch default
817 1088 files updated, 0 files merged, 0 files removed, 0 files unresolved
818 #endif
819 #if stream-bundle2-v2
820 454 $ hg clone --stream http://localhost:$HGPORT phase-publish
821 455 streaming all changes
822 1097 files to transfer, 102 KB of data (no-zstd !)
823 transferred 102 KB in * seconds (* */sec) (glob) (no-zstd !)
824 1097 files to transfer, 99.1 KB of data (zstd no-rust !)
825 transferred 99.1 KB in * seconds (* */sec) (glob) (zstd no-rust !)
826 1099 files to transfer, 99.2 KB of data (zstd rust !)
827 transferred 99.2 KB in * seconds (* */sec) (glob) (zstd rust !)
456 1091 files to transfer, * KB of data (glob) (stream-legacy !)
457 1097 files to transfer, * KB of data (glob) (stream-bundle2-v2 no-rust !)
458 1099 files to transfer, * KB of data (glob) (stream-bundle2-v2 rust !)
459 1096 entries to transfer (stream-bundle2-v3 !)
460 transferred * KB in * seconds (* */sec) (glob)
461 searching for changes (stream-legacy !)
462 no changes found (stream-legacy !)
828 463 updating to branch default
829 464 1088 files updated, 0 files merged, 0 files removed, 0 files unresolved
830 #endif
831 #if stream-bundle2-v3
832 $ hg clone --stream http://localhost:$HGPORT phase-publish
833 streaming all changes
834 1096 entries to transfer
835 transferred 102 KB in * seconds (* */sec) (glob) (no-zstd !)
836 transferred 99.1 KB in * seconds (* */sec) (glob) (zstd no-rust !)
837 transferred 99.2 KB in * seconds (* */sec) (glob) (zstd rust !)
838 updating to branch default
839 1088 files updated, 0 files merged, 0 files removed, 0 files unresolved
840 #endif
841 465 $ hg verify -R phase-publish -q
842 466 $ hg -R phase-publish phase -r 'all()'
843 467 0: public
@@ -854,73 +478,47 b' Clone as non publishing'
854 478 $ hg -R server serve -p $HGPORT -d --pid-file=hg.pid
855 479 $ cat hg.pid > $DAEMON_PIDS
856 480
857 #if stream-legacy
858
859 With v1 of the stream protocol, changeset are always cloned as public. It make
860 stream v1 unsuitable for non-publishing repository.
861
862 $ hg clone --stream http://localhost:$HGPORT phase-no-publish
863 streaming all changes
864 1091 files to transfer, 102 KB of data (no-zstd !)
865 transferred 102 KB in * seconds (* */sec) (glob) (no-zstd !)
866 1091 files to transfer, 98.8 KB of data (zstd !)
867 transferred 98.8 KB in * seconds (* */sec) (glob) (zstd !)
868 searching for changes
869 no changes found
870 updating to branch default
871 1088 files updated, 0 files merged, 0 files removed, 0 files unresolved
872 $ hg -R phase-no-publish phase -r 'all()'
873 0: public
874 1: public
875 2: public
876 #endif
877 #if stream-bundle2-v2
878 481 $ hg clone --stream http://localhost:$HGPORT phase-no-publish
879 482 streaming all changes
880 1098 files to transfer, 102 KB of data (no-zstd !)
881 transferred 102 KB in * seconds (* */sec) (glob) (no-zstd !)
882 1098 files to transfer, 99.1 KB of data (zstd no-rust !)
883 transferred 99.1 KB in * seconds (* */sec) (glob) (zstd no-rust !)
884 1100 files to transfer, 99.2 KB of data (zstd rust !)
885 transferred 99.2 KB in * seconds (* */sec) (glob) (zstd rust !)
483 1091 files to transfer, * KB of data (glob) (stream-legacy !)
484 1098 files to transfer, * KB of data (glob) (stream-bundle2-v2 no-rust !)
485 1100 files to transfer, * KB of data (glob) (stream-bundle2-v2 rust !)
486 1097 entries to transfer (stream-bundle2-v3 !)
487 transferred * KB in * seconds (* */sec) (glob)
488 searching for changes (stream-legacy !)
489 no changes found (stream-legacy !)
886 490 updating to branch default
887 491 1088 files updated, 0 files merged, 0 files removed, 0 files unresolved
492
493 Note: With v1 of the stream protocol, changeset are always cloned as public. It
494 make stream v1 unsuitable for non-publishing repository.
495
888 496 $ hg -R phase-no-publish phase -r 'all()'
889 0: draft
890 1: draft
891 2: draft
892 #endif
893 #if stream-bundle2-v3
894 $ hg clone --stream http://localhost:$HGPORT phase-no-publish
895 streaming all changes
896 1097 entries to transfer
897 transferred 102 KB in * seconds (* */sec) (glob) (no-zstd !)
898 transferred 99.1 KB in * seconds (* */sec) (glob) (zstd no-rust !)
899 transferred 99.2 KB in * seconds (* */sec) (glob) (zstd rust !)
900 updating to branch default
901 1088 files updated, 0 files merged, 0 files removed, 0 files unresolved
902 $ hg -R phase-no-publish phase -r 'all()'
903 0: draft
904 1: draft
905 2: draft
906 #endif
497 0: public (stream-legacy !)
498 1: public (stream-legacy !)
499 2: public (stream-legacy !)
500 0: draft (no-stream-legacy !)
501 1: draft (no-stream-legacy !)
502 2: draft (no-stream-legacy !)
907 503 $ hg verify -R phase-no-publish -q
908 504
909 505 $ killdaemons.py
910 506
507
508 Stream repository with obsolescence
509 -----------------------------------
510
911 511 #if stream-legacy
912 512
913 513 With v1 of the stream protocol, changeset are always cloned as public. There's
914 514 no obsolescence markers exchange in stream v1.
915 515
916 #endif
917 #if stream-bundle2-v2
918
919 Stream repository with obsolescence
920 -----------------------------------
516 #else
921 517
922 518 Clone non-publishing with obsolescence
923 519
520 The obsstore file should be send as part of the stream bundle
521
924 522 $ cat >> $HGRCPATH << EOF
925 523 > [experimental]
926 524 > evolution=all
@@ -943,62 +541,10 b' Clone non-publishing with obsolescence'
943 541
944 542 $ hg clone -U --stream http://localhost:$HGPORT with-obsolescence
945 543 streaming all changes
946 1099 files to transfer, 102 KB of data (no-zstd !)
947 transferred 102 KB in * seconds (* */sec) (glob) (no-zstd !)
948 1099 files to transfer, 99.5 KB of data (zstd no-rust !)
949 transferred 99.5 KB in * seconds (* */sec) (glob) (zstd no-rust !)
950 1101 files to transfer, 99.6 KB of data (zstd rust !)
951 transferred 99.6 KB in * seconds (* */sec) (glob) (zstd rust !)
952 $ hg -R with-obsolescence log -T '{rev}: {phase}\n'
953 2: draft
954 1: draft
955 0: draft
956 $ hg debugobsolete -R with-obsolescence
957 8c206a663911c1f97f2f9d7382e417ae55872cfa 0 {5223b5e3265f0df40bb743da62249413d74ac70f} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
958 $ hg verify -R with-obsolescence -q
959
960 $ hg clone -U --stream --config experimental.evolution=0 http://localhost:$HGPORT with-obsolescence-no-evolution
961 streaming all changes
962 remote: abort: server has obsolescence markers, but client cannot receive them via stream clone
963 abort: pull failed on remote
964 [100]
965
966 $ killdaemons.py
967
968 #endif
969 #if stream-bundle2-v3
970
971 Stream repository with obsolescence
972 -----------------------------------
973
974 Clone non-publishing with obsolescence
975
976 $ cat >> $HGRCPATH << EOF
977 > [experimental]
978 > evolution=all
979 > EOF
980
981 $ cd server
982 $ echo foo > foo
983 $ hg -q commit -m 'about to be pruned'
984 $ hg debugobsolete `hg log -r . -T '{node}'` -d '0 0' -u test --record-parents
985 1 new obsolescence markers
986 obsoleted 1 changesets
987 $ hg up null -q
988 $ hg log -T '{rev}: {phase}\n'
989 2: draft
990 1: draft
991 0: draft
992 $ hg serve -p $HGPORT -d --pid-file=hg.pid
993 $ cat hg.pid > $DAEMON_PIDS
994 $ cd ..
995
996 $ hg clone -U --stream http://localhost:$HGPORT with-obsolescence
997 streaming all changes
998 1098 entries to transfer
999 transferred 102 KB in * seconds (* */sec) (glob) (no-zstd !)
1000 transferred 99.5 KB in * seconds (* */sec) (glob) (zstd no-rust !)
1001 transferred 99.6 KB in * seconds (* */sec) (glob) (zstd rust !)
544 1099 files to transfer, * KB of data (glob) (stream-bundle2-v2 no-rust !)
545 1101 files to transfer, * KB of data (glob) (stream-bundle2-v2 rust !)
546 1098 entries to transfer (no-stream-bundle2-v2 !)
547 transferred * KB in * seconds (* */sec) (glob)
1002 548 $ hg -R with-obsolescence log -T '{rev}: {phase}\n'
1003 549 2: draft
1004 550 1: draft
@@ -1018,19 +564,16 b' Clone non-publishing with obsolescence'
1018 564 #endif
1019 565
1020 566 Cloning a repo with no requirements doesn't give some obscure error
567 -------------------------------------------------------------------
1021 568
1022 569 $ mkdir -p empty-repo/.hg
1023 570 $ hg clone -q --stream ssh://user@dummy/empty-repo empty-repo2
1024 571 $ hg --cwd empty-repo2 verify -q
1025 572
1026 573 Cloning a repo with an empty manifestlog doesn't give some weird error
574 ----------------------------------------------------------------------
1027 575
1028 576 $ rm -r empty-repo; hg init empty-repo
1029 577 $ (cd empty-repo; touch x; hg commit -Am empty; hg debugstrip -r 0) > /dev/null
1030 578 $ hg clone -q --stream ssh://user@dummy/empty-repo empty-repo3
1031 $ hg --cwd empty-repo3 verify -q 2>&1 | grep -v warning
1032 [1]
1033
1034 The warnings filtered out here are talking about zero-length 'orphan' data files.
1035 Those are harmless, so that's fine.
1036
579 $ hg --cwd empty-repo3 verify -q
@@ -47,11 +47,7 b' Ensure branchcache got copied over:'
47 47
48 48 $ ls .hg/cache
49 49 branch2-base
50 branch2-immutable
51 50 branch2-served
52 branch2-served.hidden
53 branch2-visible
54 branch2-visible-hidden
55 51 rbc-names-v1
56 52 rbc-revs-v1
57 53 tags2
@@ -71,42 +67,34 b' No update, with debug option:'
71 67
72 68 #if hardlink
73 69 $ hg --debug clone -U . ../c --config progress.debug=true
74 linking: 1/16 files (6.25%) (no-rust !)
75 linking: 2/16 files (12.50%) (no-rust !)
76 linking: 3/16 files (18.75%) (no-rust !)
77 linking: 4/16 files (25.00%) (no-rust !)
78 linking: 5/16 files (31.25%) (no-rust !)
79 linking: 6/16 files (37.50%) (no-rust !)
80 linking: 7/16 files (43.75%) (no-rust !)
81 linking: 8/16 files (50.00%) (no-rust !)
82 linking: 9/16 files (56.25%) (no-rust !)
83 linking: 10/16 files (62.50%) (no-rust !)
84 linking: 11/16 files (68.75%) (no-rust !)
85 linking: 12/16 files (75.00%) (no-rust !)
86 linking: 13/16 files (81.25%) (no-rust !)
87 linking: 14/16 files (87.50%) (no-rust !)
88 linking: 15/16 files (93.75%) (no-rust !)
89 linking: 16/16 files (100.00%) (no-rust !)
90 linked 16 files (no-rust !)
91 linking: 1/18 files (5.56%) (rust !)
92 linking: 2/18 files (11.11%) (rust !)
93 linking: 3/18 files (16.67%) (rust !)
94 linking: 4/18 files (22.22%) (rust !)
95 linking: 5/18 files (27.78%) (rust !)
96 linking: 6/18 files (33.33%) (rust !)
97 linking: 7/18 files (38.89%) (rust !)
98 linking: 8/18 files (44.44%) (rust !)
99 linking: 9/18 files (50.00%) (rust !)
100 linking: 10/18 files (55.56%) (rust !)
101 linking: 11/18 files (61.11%) (rust !)
102 linking: 12/18 files (66.67%) (rust !)
103 linking: 13/18 files (72.22%) (rust !)
104 linking: 14/18 files (77.78%) (rust !)
105 linking: 15/18 files (83.33%) (rust !)
106 linking: 16/18 files (88.89%) (rust !)
107 linking: 17/18 files (94.44%) (rust !)
108 linking: 18/18 files (100.00%) (rust !)
109 linked 18 files (rust !)
70 linking: 1/12 files (8.33%) (no-rust !)
71 linking: 2/12 files (16.67%) (no-rust !)
72 linking: 3/12 files (25.00%) (no-rust !)
73 linking: 4/12 files (33.33%) (no-rust !)
74 linking: 5/12 files (41.67%) (no-rust !)
75 linking: 6/12 files (50.00%) (no-rust !)
76 linking: 7/12 files (58.33%) (no-rust !)
77 linking: 8/12 files (66.67%) (no-rust !)
78 linking: 9/12 files (75.00%) (no-rust !)
79 linking: 10/12 files (83.33%) (no-rust !)
80 linking: 11/12 files (91.67%) (no-rust !)
81 linking: 12/12 files (100.00%) (no-rust !)
82 linked 12 files (no-rust !)
83 linking: 1/14 files (7.14%) (rust !)
84 linking: 2/14 files (14.29%) (rust !)
85 linking: 3/14 files (21.43%) (rust !)
86 linking: 4/14 files (28.57%) (rust !)
87 linking: 5/14 files (35.71%) (rust !)
88 linking: 6/14 files (42.86%) (rust !)
89 linking: 7/14 files (50.00%) (rust !)
90 linking: 8/14 files (57.14%) (rust !)
91 linking: 9/14 files (64.29%) (rust !)
92 linking: 10/14 files (71.43%) (rust !)
93 linking: 11/14 files (78.57%) (rust !)
94 linking: 12/14 files (85.71%) (rust !)
95 linking: 13/14 files (92.86%) (rust !)
96 linking: 14/14 files (100.00%) (rust !)
97 linked 14 files (rust !)
110 98 updating the branch cache
111 99 #else
112 100 $ hg --debug clone -U . ../c --config progress.debug=true
@@ -125,11 +113,7 b' Ensure branchcache got copied over:'
125 113
126 114 $ ls .hg/cache
127 115 branch2-base
128 branch2-immutable
129 116 branch2-served
130 branch2-served.hidden
131 branch2-visible
132 branch2-visible-hidden
133 117 rbc-names-v1
134 118 rbc-revs-v1
135 119 tags2
@@ -394,9 +394,9 b' No bundle spec should work'
394 394 $ hg clone -U http://localhost:$HGPORT stream-clone-no-spec
395 395 applying clone bundle from http://localhost:$HGPORT1/packed.hg
396 396 5 files to transfer, 613 bytes of data (no-rust !)
397 transferred 613 bytes in *.* seconds (*) (glob) (no-rust !)
397 transferred 613 bytes in * seconds (* */sec) (glob) (no-rust !)
398 398 7 files to transfer, 739 bytes of data (rust !)
399 transferred 739 bytes in *.* seconds (*) (glob) (rust !)
399 transferred 739 bytes in * seconds (* */sec) (glob) (rust !)
400 400 finished applying clone bundle
401 401 searching for changes
402 402 no changes found
@@ -409,10 +409,8 b' Bundle spec without parameters should wo'
409 409
410 410 $ hg clone -U http://localhost:$HGPORT stream-clone-vanilla-spec
411 411 applying clone bundle from http://localhost:$HGPORT1/packed.hg
412 5 files to transfer, 613 bytes of data (no-rust !)
413 transferred 613 bytes in *.* seconds (*) (glob) (no-rust !)
414 7 files to transfer, 739 bytes of data (rust !)
415 transferred 739 bytes in *.* seconds (*) (glob) (rust !)
412 * files to transfer, * bytes of data (glob)
413 transferred * bytes in * seconds (* */sec) (glob)
416 414 finished applying clone bundle
417 415 searching for changes
418 416 no changes found
@@ -425,10 +423,8 b' Bundle spec with format requirements sho'
425 423
426 424 $ hg clone -U http://localhost:$HGPORT stream-clone-supported-requirements
427 425 applying clone bundle from http://localhost:$HGPORT1/packed.hg
428 5 files to transfer, 613 bytes of data (no-rust !)
429 transferred 613 bytes in *.* seconds (*) (glob) (no-rust !)
430 7 files to transfer, 739 bytes of data (rust !)
431 transferred 739 bytes in *.* seconds (*) (glob) (rust !)
426 * files to transfer, * bytes of data (glob)
427 transferred * bytes in * seconds (* */sec) (glob)
432 428 finished applying clone bundle
433 429 searching for changes
434 430 no changes found
@@ -574,10 +570,8 b' A manifest with just a gzip bundle'
574 570 no compatible clone bundles available on server; falling back to regular clone
575 571 (you may want to report this to the server operator)
576 572 streaming all changes
577 10 files to transfer, 816 bytes of data (no-rust !)
578 transferred 816 bytes in * seconds (*) (glob) (no-rust !)
579 12 files to transfer, 942 bytes of data (rust !)
580 transferred 942 bytes in *.* seconds (*) (glob) (rust !)
573 * files to transfer, * bytes of data (glob)
574 transferred * bytes in * seconds (* */sec) (glob)
581 575
582 576 A manifest with a stream clone but no BUNDLESPEC
583 577
@@ -589,10 +583,8 b' A manifest with a stream clone but no BU'
589 583 no compatible clone bundles available on server; falling back to regular clone
590 584 (you may want to report this to the server operator)
591 585 streaming all changes
592 10 files to transfer, 816 bytes of data (no-rust !)
593 transferred 816 bytes in * seconds (*) (glob) (no-rust !)
594 12 files to transfer, 942 bytes of data (rust !)
595 transferred 942 bytes in *.* seconds (*) (glob) (rust !)
586 * files to transfer, * bytes of data (glob)
587 transferred * bytes in * seconds (* */sec) (glob)
596 588
597 589 A manifest with a gzip bundle and a stream clone
598 590
@@ -603,10 +595,8 b' A manifest with a gzip bundle and a stre'
603 595
604 596 $ hg clone -U --stream http://localhost:$HGPORT uncompressed-gzip-packed
605 597 applying clone bundle from http://localhost:$HGPORT1/packed.hg
606 5 files to transfer, 613 bytes of data (no-rust !)
607 transferred 613 bytes in *.* seconds (*) (glob) (no-rust !)
608 7 files to transfer, 739 bytes of data (rust !)
609 transferred 739 bytes in *.* seconds (*) (glob) (rust !)
598 * files to transfer, * bytes of data (glob)
599 transferred * bytes in * seconds (* */sec) (glob)
610 600 finished applying clone bundle
611 601 searching for changes
612 602 no changes found
@@ -620,10 +610,8 b' A manifest with a gzip bundle and stream'
620 610
621 611 $ hg clone -U --stream http://localhost:$HGPORT uncompressed-gzip-packed-requirements
622 612 applying clone bundle from http://localhost:$HGPORT1/packed.hg
623 5 files to transfer, 613 bytes of data (no-rust !)
624 transferred 613 bytes in *.* seconds (*) (glob) (no-rust !)
625 7 files to transfer, 739 bytes of data (rust !)
626 transferred 739 bytes in *.* seconds (*) (glob) (rust !)
613 * files to transfer, * bytes of data (glob)
614 transferred * bytes in * seconds (* */sec) (glob)
627 615 finished applying clone bundle
628 616 searching for changes
629 617 no changes found
@@ -639,10 +627,8 b' A manifest with a gzip bundle and a stre'
639 627 no compatible clone bundles available on server; falling back to regular clone
640 628 (you may want to report this to the server operator)
641 629 streaming all changes
642 10 files to transfer, 816 bytes of data (no-rust !)
643 transferred 816 bytes in * seconds (*) (glob) (no-rust !)
644 12 files to transfer, 942 bytes of data (rust !)
645 transferred 942 bytes in *.* seconds (*) (glob) (rust !)
630 * files to transfer, * bytes of data (glob)
631 transferred * bytes in * seconds (* */sec) (glob)
646 632
647 633 Test clone bundle retrieved through bundle2
648 634
@@ -284,7 +284,7 b' Show all commands + options'
284 284 debug-revlog-stats: changelog, manifest, filelogs, template
285 285 debug::stable-tail-sort: template
286 286 debug::stable-tail-sort-leaps: template, specific
287 debug::unbundle: update
287 debug::unbundle:
288 288 debugancestor:
289 289 debugantivirusrunning:
290 290 debugapplystreamclonebundle:
@@ -59,8 +59,11 b' perfstatus'
59 59 number of run to perform before starting measurement.
60 60
61 61 "profile-benchmark"
62 Enable profiling for the benchmarked section. (The first iteration is
63 benchmarked)
62 Enable profiling for the benchmarked section. (by default, the first
63 iteration is benchmarked)
64
65 "profiled-runs"
66 list of iteration to profile (starting from 0)
64 67
65 68 "run-limits"
66 69 Control the number of runs each benchmark will perform. The option value
@@ -652,12 +652,7 b' Test cache warming command'
652 652 .hg/cache/rbc-revs-v1
653 653 .hg/cache/rbc-names-v1
654 654 .hg/cache/hgtagsfnodes1
655 .hg/cache/branch2-visible-hidden
656 .hg/cache/branch2-visible
657 .hg/cache/branch2-served.hidden
658 655 .hg/cache/branch2-served
659 .hg/cache/branch2-immutable
660 .hg/cache/branch2-base
661 656
662 657 Test debug::unbundle
663 658
@@ -668,9 +663,6 b' Test debug::unbundle'
668 663 adding manifests
669 664 adding file changes
670 665 added 0 changesets with 0 changes to 1 files (no-pure !)
671 9 local changesets published (no-pure !)
672 3 local changesets published (pure !)
673 (run 'hg update' to get a working copy)
674 666
675 667 Test debugcolor
676 668
@@ -263,11 +263,7 b' r4 has hardlinks in the working dir (not'
263 263 2 r4/.hg/00changelog.i
264 264 [24] r4/.hg/branch (re)
265 265 2 r4/.hg/cache/branch2-base
266 2 r4/.hg/cache/branch2-immutable
267 266 2 r4/.hg/cache/branch2-served
268 2 r4/.hg/cache/branch2-served.hidden
269 2 r4/.hg/cache/branch2-visible
270 2 r4/.hg/cache/branch2-visible-hidden
271 267 2 r4/.hg/cache/rbc-names-v1
272 268 2 r4/.hg/cache/rbc-revs-v1
273 269 2 r4/.hg/cache/tags2
@@ -320,11 +316,7 b' Update back to revision 12 in r4 should '
320 316 2 r4/.hg/00changelog.i
321 317 1 r4/.hg/branch
322 318 2 r4/.hg/cache/branch2-base
323 2 r4/.hg/cache/branch2-immutable
324 319 2 r4/.hg/cache/branch2-served
325 2 r4/.hg/cache/branch2-served.hidden
326 2 r4/.hg/cache/branch2-visible
327 2 r4/.hg/cache/branch2-visible-hidden
328 320 2 r4/.hg/cache/rbc-names-v1
329 321 2 r4/.hg/cache/rbc-revs-v1
330 322 2 r4/.hg/cache/tags2
@@ -730,7 +730,7 b' getoldnodedrevmap() in later phabsends.'
730 730 $ hg amend --config experimental.evolution=all --config extensions.amend=
731 731 1 new orphan changesets
732 732 $ hg up 3
733 obsolete feature not enabled but 1 markers found!
733 "obsolete" feature not enabled but 1 markers found!
734 734 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
735 735 $ hg rebase --config experimental.evolution=all --config extensions.rebase=
736 736 note: not rebasing 2:832553266fe8 "two: second commit to review", already in destination as 4:0124e5474c88 tip "two: second commit to review"
@@ -741,7 +741,7 b' updated.'
741 741
742 742 $ echo y | hg phabsend --fold --confirm -r 1:: \
743 743 > --test-vcr "$VCR/phabsend-fold-updated.json"
744 obsolete feature not enabled but 2 markers found!
744 "obsolete" feature not enabled but 2 markers found!
745 745 602c4e738243 mapped to old nodes ['602c4e738243']
746 746 0124e5474c88 mapped to old nodes ['832553266fe8']
747 747 e4edb1fe3565 mapped to old nodes ['921f8265efbd']
@@ -752,11 +752,11 b' updated.'
752 752 D8387 - updated - 1:602c4e738243 "one: first commit to review"
753 753 D8387 - updated - 4:0124e5474c88 "two: second commit to review"
754 754 D8387 - updated - 5:e4edb1fe3565 tip "3: a commit with no detailed message"
755 obsolete feature not enabled but 2 markers found! (?)
755 "obsolete" feature not enabled but 2 markers found! (?)
756 756 updating local commit list for D8387
757 757 new commits: ['602c4e738243', '0124e5474c88', 'e4edb1fe3565']
758 758 $ hg log -Tcompact
759 obsolete feature not enabled but 2 markers found!
759 "obsolete" feature not enabled but 2 markers found!
760 760 5[tip] e4edb1fe3565 1970-01-01 00:00 +0000 test
761 761 3: a commit with no detailed message
762 762
@@ -773,17 +773,17 b' When nothing has changed locally since t'
773 773 updated, and nothing is changed locally afterward.
774 774
775 775 $ hg phabsend --fold -r 1:: --test-vcr "$VCR/phabsend-fold-no-changes.json"
776 obsolete feature not enabled but 2 markers found!
776 "obsolete" feature not enabled but 2 markers found!
777 777 602c4e738243 mapped to old nodes ['602c4e738243']
778 778 0124e5474c88 mapped to old nodes ['0124e5474c88']
779 779 e4edb1fe3565 mapped to old nodes ['e4edb1fe3565']
780 780 D8387 - updated - 1:602c4e738243 "one: first commit to review"
781 781 D8387 - updated - 4:0124e5474c88 "two: second commit to review"
782 782 D8387 - updated - 5:e4edb1fe3565 tip "3: a commit with no detailed message"
783 obsolete feature not enabled but 2 markers found! (?)
783 "obsolete" feature not enabled but 2 markers found! (?)
784 784 local commit list for D8387 is already up-to-date
785 785 $ hg log -Tcompact
786 obsolete feature not enabled but 2 markers found!
786 "obsolete" feature not enabled but 2 markers found!
787 787 5[tip] e4edb1fe3565 1970-01-01 00:00 +0000 test
788 788 3: a commit with no detailed message
789 789
@@ -800,7 +800,7 b' Fold will accept new revisions at the en'
800 800
801 801 $ echo 'another mod' > file2.txt
802 802 $ hg ci -m 'four: extend the fold range'
803 obsolete feature not enabled but 2 markers found!
803 "obsolete" feature not enabled but 2 markers found!
804 804 $ hg phabsend --fold -r 1:: --test-vcr "$VCR/phabsend-fold-extend-end.json" \
805 805 > --config experimental.evolution=all
806 806 602c4e738243 mapped to old nodes ['602c4e738243']
@@ -817,7 +817,7 b' Fold will accept new revisions at the en'
817 817
818 818 Differential Revision: https://phab.mercurial-scm.org/D8387
819 819 $ hg log -T'{rev} {if(phabreview, "{phabreview.url} {phabreview.id}")}\n' -r 1::
820 obsolete feature not enabled but 3 markers found!
820 "obsolete" feature not enabled but 3 markers found!
821 821 1 https://phab.mercurial-scm.org/D8387 D8387
822 822 4 https://phab.mercurial-scm.org/D8387 D8387
823 823 5 https://phab.mercurial-scm.org/D8387 D8387
@@ -846,7 +846,7 b' TODO: See if it can reuse the existing D'
846 846 new commits: ['15e9b14b4b4c', '6320b7d714cf', '3ee132d41dbc', '30682b960804', 'ac7db67f0991']
847 847
848 848 $ hg log -T '{rev}:{node|short}\n{indent(desc, " ")}\n'
849 obsolete feature not enabled but 8 markers found!
849 "obsolete" feature not enabled but 8 markers found!
850 850 12:ac7db67f0991
851 851 four: extend the fold range
852 852
@@ -962,7 +962,7 b' Test phabsend --fold with an `hg fold` a'
962 962 new commits: ['15e9b14b4b4c', '6320b7d714cf', '3ee132d41dbc', '30682b960804', 'e919cdf3d4fe']
963 963
964 964 $ hg log -r tip -v
965 obsolete feature not enabled but 12 markers found!
965 "obsolete" feature not enabled but 12 markers found!
966 966 changeset: 16:e919cdf3d4fe
967 967 tag: tip
968 968 parent: 11:30682b960804
@@ -36,12 +36,7 b' Check same result using `experimental.ex'
36 36 $ hg -R test --config experimental.extra-filter-revs='not public()' debugupdatecache
37 37 $ ls -1 test/.hg/cache/
38 38 branch2-base%89c45d2fa07e
39 branch2-immutable%89c45d2fa07e
40 39 branch2-served
41 branch2-served%89c45d2fa07e
42 branch2-served.hidden%89c45d2fa07e
43 branch2-visible%89c45d2fa07e
44 branch2-visible-hidden%89c45d2fa07e
45 40 hgtagsfnodes1
46 41 rbc-names-v1
47 42 rbc-revs-v1
@@ -63,11 +63,7 b' Cloning a shared repo should pick up the'
63 63 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
64 64 $ ls -1 ../repo2-clone/.hg/cache
65 65 branch2-base
66 branch2-immutable
67 66 branch2-served
68 branch2-served.hidden
69 branch2-visible
70 branch2-visible-hidden
71 67 rbc-names-v1
72 68 rbc-revs-v1
73 69 tags2
@@ -72,8 +72,8 b' clone bookmarks via stream'
72 72 $ hg -R local-stream book mybook
73 73 $ hg clone --stream ssh://user@dummy/local-stream stream2
74 74 streaming all changes
75 16 files to transfer, * of data (glob) (no-rust !)
76 18 files to transfer, * of data (glob) (rust !)
75 12 files to transfer, * of data (glob) (no-rust !)
76 14 files to transfer, * of data (glob) (rust !)
77 77 transferred * in * seconds (*) (glob)
78 78 updating to branch default
79 79 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -74,6 +74,23 b' The extension requires a repo (currently'
74 74 none-v2;stream=v3-exp;requirements%3Dgeneraldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog (stream-v3 zstd no-rust !)
75 75 none-v2;stream=v3-exp;requirements%3Dgeneraldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog (stream-v3 rust !)
76 76
77 $ hg bundle -a --type="none-$bundle_format" bundle.hg
78 $ hg debugbundle bundle.hg
79 Stream params: {}
80 stream2 -- {bytecount: 1693, filecount: 12, requirements: generaldelta%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 no-zstd !)
81 stream2 -- {bytecount: 1693, filecount: 12, requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 zstd no-rust !)
82 stream2 -- {bytecount: 1819, filecount: 14, requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v2 rust !)
83 stream3-exp -- {requirements: generaldelta%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v3 no-zstd !)
84 stream3-exp -- {requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v3 zstd no-rust !)
85 stream3-exp -- {requirements: generaldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog} (mandatory: True) (stream-v3 rust !)
86 $ hg debugbundle --spec bundle.hg
87 none-v2;stream=v2;requirements%3Dgeneraldelta%2Crevlogv1%2Csparserevlog (stream-v2 no-zstd !)
88 none-v2;stream=v2;requirements%3Dgeneraldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog (stream-v2 zstd no-rust !)
89 none-v2;stream=v2;requirements%3Dgeneraldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog (stream-v2 rust !)
90 none-v2;stream=v3-exp;requirements%3Dgeneraldelta%2Crevlogv1%2Csparserevlog (stream-v3 no-zstd !)
91 none-v2;stream=v3-exp;requirements%3Dgeneraldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog (stream-v3 zstd no-rust !)
92 none-v2;stream=v3-exp;requirements%3Dgeneraldelta%2Crevlog-compression-zstd%2Crevlogv1%2Csparserevlog (stream-v3 rust !)
93
77 94 Test that we can apply the bundle as a stream clone bundle
78 95
79 96 $ cat > .hg/clonebundles.manifest << EOF
@@ -1,3 +1,5 b''
1 This test cover a bug that no longer exist.
2
1 3 Define helpers.
2 4
3 5 $ hg_log () { hg log -G -T "{rev}:{node|short}"; }
@@ -18,7 +20,10 b' Setup hg repo.'
18 20
19 21 $ hg pull -q ../repo
20 22
21 $ cat .hg/cache/branch2-visible
23 $ ls -1 .hg/cache/branch?*
24 .hg/cache/branch2-base
25 .hg/cache/branch2-served
26 $ cat .hg/cache/branch?-served
22 27 222ae9789a75703f9836e44de7db179cbfd420ee 2
23 28 a3498d6e39376d2456425dd8c692367bdbf00fa2 o default
24 29 222ae9789a75703f9836e44de7db179cbfd420ee o default
@@ -33,24 +38,36 b' Setup hg repo.'
33 38
34 39 $ strip '1:'
35 40
36 The branchmap cache is not adjusted on strip.
37 Now mentions a changelog entry that has been stripped.
41 After the strip the "served" cache is now identical to the "base" one, and the
42 older one have been actively deleted.
38 43
39 $ cat .hg/cache/branch2-visible
40 222ae9789a75703f9836e44de7db179cbfd420ee 2
41 a3498d6e39376d2456425dd8c692367bdbf00fa2 o default
42 222ae9789a75703f9836e44de7db179cbfd420ee o default
44 $ ls -1 .hg/cache/branch?*
45 .hg/cache/branch2-base
46 $ cat .hg/cache/branch?-base
47 7ab0a3bd758a58b9f79557ce708533e627776cce 0
48 7ab0a3bd758a58b9f79557ce708533e627776cce o default
49
50 We do a new commit and we get a new valid branchmap for the served version
43 51
44 52 $ commit c
45
46 Not adjusted on commit, either.
53 $ ls -1 .hg/cache/branch?*
54 .hg/cache/branch2-base
55 .hg/cache/branch2-served
56 $ cat .hg/cache/branch?-served
57 a1602b357cfca067600406eb19060c7128804d72 1
58 a1602b357cfca067600406eb19060c7128804d72 o default
47 59
48 $ cat .hg/cache/branch2-visible
49 222ae9789a75703f9836e44de7db179cbfd420ee 2
50 a3498d6e39376d2456425dd8c692367bdbf00fa2 o default
51 222ae9789a75703f9836e44de7db179cbfd420ee o default
52 60
53 61 On pull we end up with the same tip, and so wrongly reuse the invalid cache and crash.
54 62
55 $ hg pull ../repo 2>&1 | grep 'ValueError:'
56 ValueError: node a3498d6e39376d2456425dd8c692367bdbf00fa2 does not exist (known-bad-output !)
63 $ hg pull ../repo --quiet
64 $ hg heads -T '{rev} {node} {branch}\n'
65 2 222ae9789a75703f9836e44de7db179cbfd420ee default
66 1 a1602b357cfca067600406eb19060c7128804d72 default
67 $ ls -1 .hg/cache/branch?*
68 .hg/cache/branch2-base
69 .hg/cache/branch2-served
70 $ cat .hg/cache/branch?-served
71 222ae9789a75703f9836e44de7db179cbfd420ee 2
72 a1602b357cfca067600406eb19060c7128804d72 o default
73 222ae9789a75703f9836e44de7db179cbfd420ee o default
@@ -792,11 +792,6 b' Missing tags2* files means the cache was'
792 792
793 793 $ ls tagsclient/.hg/cache
794 794 branch2-base
795 branch2-immutable
796 branch2-served
797 branch2-served.hidden
798 branch2-visible
799 branch2-visible-hidden
800 795 hgtagsfnodes1
801 796 rbc-names-v1
802 797 rbc-revs-v1
@@ -823,11 +818,6 b' Running hg tags should produce tags2* fi'
823 818
824 819 $ ls tagsclient/.hg/cache
825 820 branch2-base
826 branch2-immutable
827 branch2-served
828 branch2-served.hidden
829 branch2-visible
830 branch2-visible-hidden
831 821 hgtagsfnodes1
832 822 rbc-names-v1
833 823 rbc-revs-v1
@@ -761,8 +761,8 b' Stream clone with basicstore'
761 761 $ hg clone --config experimental.changegroup3=True --stream -U \
762 762 > http://localhost:$HGPORT1 stream-clone-basicstore
763 763 streaming all changes
764 29 files to transfer, * of data (glob) (no-rust !)
765 31 files to transfer, * of data (glob) (rust !)
764 24 files to transfer, * of data (glob) (no-rust !)
765 26 files to transfer, * of data (glob) (rust !)
766 766 transferred * in * seconds (*) (glob)
767 767 $ hg -R stream-clone-basicstore verify -q
768 768 $ cat port-1-errors.log
@@ -771,8 +771,8 b' Stream clone with encodedstore'
771 771 $ hg clone --config experimental.changegroup3=True --stream -U \
772 772 > http://localhost:$HGPORT2 stream-clone-encodedstore
773 773 streaming all changes
774 29 files to transfer, * of data (glob) (no-rust !)
775 31 files to transfer, * of data (glob) (rust !)
774 24 files to transfer, * of data (glob) (no-rust !)
775 26 files to transfer, * of data (glob) (rust !)
776 776 transferred * in * seconds (*) (glob)
777 777 $ hg -R stream-clone-encodedstore verify -q
778 778 $ cat port-2-errors.log
@@ -1,4 +1,4 b''
1 #if no-windows no-osx
1 #if no-windows
2 2
3 3 $ mkdir -p xdgconf/hg
4 4 $ echo '[ui]' > xdgconf/hg/hgrc
General Comments 0
You need to be logged in to leave comments. Login now