##// END OF EJS Templates
sidedata-exchange: rewrite sidedata on-the-fly whenever possible...
Raphaël Gomès -
r47452:ba8e508a default
parent child Browse files
Show More
@@ -0,0 +1,473 b''
1 ===========================
2 Tests for sidedata exchange
3 ===========================
4
5 Check simple exchange behavior
6 ==============================
7
8 Pusher and pushed have sidedata enabled
9 ---------------------------------------
10
11 $ hg init sidedata-source --config format.exp-use-side-data=yes
12 $ cat << EOF >> sidedata-source/.hg/hgrc
13 > [extensions]
14 > testsidedata=$TESTDIR/testlib/ext-sidedata-5.py
15 > EOF
16 $ hg init sidedata-target --config format.exp-use-side-data=yes
17 $ cat << EOF >> sidedata-target/.hg/hgrc
18 > [extensions]
19 > testsidedata=$TESTDIR/testlib/ext-sidedata-5.py
20 > EOF
21 $ cd sidedata-source
22 $ echo a > a
23 $ echo b > b
24 $ echo c > c
25 $ hg commit -Am "initial"
26 adding a
27 adding b
28 adding c
29 $ echo aa > a
30 $ hg commit -m "other"
31 $ hg push -r . ../sidedata-target
32 pushing to ../sidedata-target
33 searching for changes
34 adding changesets
35 adding manifests
36 adding file changes
37 added 2 changesets with 4 changes to 3 files
38 $ hg -R ../sidedata-target debugsidedata -c 0
39 2 sidedata entries
40 entry-0001 size 4
41 entry-0002 size 32
42 $ hg -R ../sidedata-target debugsidedata -c 1 -v
43 2 sidedata entries
44 entry-0001 size 4
45 '\x00\x00\x00:'
46 entry-0002 size 32
47 '\xa3\xee4v\x99\x85$\x9f\x1f\x8dKe\x0f\xc3\x9d-\xc9\xb5%[\x15=h\xe9\xf2O\xb5\xd9\x1f*\xff\xe5'
48 $ hg -R ../sidedata-target debugsidedata -m 0
49 2 sidedata entries
50 entry-0001 size 4
51 entry-0002 size 32
52 $ hg -R ../sidedata-target debugsidedata -m 1 -v
53 2 sidedata entries
54 entry-0001 size 4
55 '\x00\x00\x00\x81'
56 entry-0002 size 32
57 '-bL\xc5\xa4uu"#\xac\x1b`,\xc0\xbc\x9d\xf5\xac\xf0\x1d\x89)2\xf8N\xb1\x14m\xce\xd7\xbc\xae'
58 $ hg -R ../sidedata-target debugsidedata a 0
59 2 sidedata entries
60 entry-0001 size 4
61 entry-0002 size 32
62 $ hg -R ../sidedata-target debugsidedata a 1 -v
63 2 sidedata entries
64 entry-0001 size 4
65 '\x00\x00\x00\x03'
66 entry-0002 size 32
67 '\xd9\xcd\x81UvL5C\xf1\x0f\xad\x8aH\rt17Fo\x8dU!<\x8e\xae\xfc\xd1/\x06\xd4:\x80'
68 $ cd ..
69
70 Puller and pulled have sidedata enabled
71 ---------------------------------------
72
73 $ rm -rf sidedata-source sidedata-target
74 $ hg init sidedata-source --config format.exp-use-side-data=yes
75 $ cat << EOF >> sidedata-source/.hg/hgrc
76 > [extensions]
77 > testsidedata=$TESTDIR/testlib/ext-sidedata-5.py
78 > EOF
79 $ hg init sidedata-target --config format.exp-use-side-data=yes
80 $ cat << EOF >> sidedata-target/.hg/hgrc
81 > [extensions]
82 > testsidedata=$TESTDIR/testlib/ext-sidedata-5.py
83 > EOF
84 $ cd sidedata-source
85 $ echo a > a
86 $ echo b > b
87 $ echo c > c
88 $ hg commit -Am "initial"
89 adding a
90 adding b
91 adding c
92 $ echo aa > a
93 $ hg commit -m "other"
94 $ hg pull -R ../sidedata-target ../sidedata-source
95 pulling from ../sidedata-source
96 requesting all changes
97 adding changesets
98 adding manifests
99 adding file changes
100 added 2 changesets with 4 changes to 3 files
101 new changesets 05da661850d7:7ec8b4049447
102 (run 'hg update' to get a working copy)
103 $ hg -R ../sidedata-target debugsidedata -c 0
104 2 sidedata entries
105 entry-0001 size 4
106 entry-0002 size 32
107 $ hg -R ../sidedata-target debugsidedata -c 1 -v
108 2 sidedata entries
109 entry-0001 size 4
110 '\x00\x00\x00:'
111 entry-0002 size 32
112 '\xa3\xee4v\x99\x85$\x9f\x1f\x8dKe\x0f\xc3\x9d-\xc9\xb5%[\x15=h\xe9\xf2O\xb5\xd9\x1f*\xff\xe5'
113 $ hg -R ../sidedata-target debugsidedata -m 0
114 2 sidedata entries
115 entry-0001 size 4
116 entry-0002 size 32
117 $ hg -R ../sidedata-target debugsidedata -m 1 -v
118 2 sidedata entries
119 entry-0001 size 4
120 '\x00\x00\x00\x81'
121 entry-0002 size 32
122 '-bL\xc5\xa4uu"#\xac\x1b`,\xc0\xbc\x9d\xf5\xac\xf0\x1d\x89)2\xf8N\xb1\x14m\xce\xd7\xbc\xae'
123 $ hg -R ../sidedata-target debugsidedata a 0
124 2 sidedata entries
125 entry-0001 size 4
126 entry-0002 size 32
127 $ hg -R ../sidedata-target debugsidedata a 1 -v
128 2 sidedata entries
129 entry-0001 size 4
130 '\x00\x00\x00\x03'
131 entry-0002 size 32
132 '\xd9\xcd\x81UvL5C\xf1\x0f\xad\x8aH\rt17Fo\x8dU!<\x8e\xae\xfc\xd1/\x06\xd4:\x80'
133 $ cd ..
134
135 Now on to asymmetric configs.
136
137 Pusher has sidedata enabled, pushed does not
138 --------------------------------------------
139
140 $ rm -rf sidedata-source sidedata-target
141 $ hg init sidedata-source --config format.exp-use-side-data=yes
142 $ cat << EOF >> sidedata-source/.hg/hgrc
143 > [extensions]
144 > testsidedata=$TESTDIR/testlib/ext-sidedata-5.py
145 > EOF
146 $ hg init sidedata-target --config format.exp-use-side-data=no
147 $ cd sidedata-source
148 $ echo a > a
149 $ echo b > b
150 $ echo c > c
151 $ hg commit -Am "initial"
152 adding a
153 adding b
154 adding c
155 $ echo aa > a
156 $ hg commit -m "other"
157 $ hg push -r . ../sidedata-target --traceback
158 pushing to ../sidedata-target
159 searching for changes
160 adding changesets
161 adding manifests
162 adding file changes
163 added 2 changesets with 4 changes to 3 files
164 $ hg -R ../sidedata-target log -G
165 o changeset: 1:7ec8b4049447
166 | tag: tip
167 | user: test
168 | date: Thu Jan 01 00:00:00 1970 +0000
169 | summary: other
170 |
171 o changeset: 0:05da661850d7
172 user: test
173 date: Thu Jan 01 00:00:00 1970 +0000
174 summary: initial
175
176
177 $ hg -R ../sidedata-target debugsidedata -c 0
178 $ hg -R ../sidedata-target debugsidedata -c 1 -v
179 $ hg -R ../sidedata-target debugsidedata -m 0
180 $ hg -R ../sidedata-target debugsidedata -m 1 -v
181 $ hg -R ../sidedata-target debugsidedata a 0
182 $ hg -R ../sidedata-target debugsidedata a 1 -v
183 $ cd ..
184
185 Pulled has sidedata enabled, puller does not
186 --------------------------------------------
187
188 $ rm -rf sidedata-source sidedata-target
189 $ hg init sidedata-source --config format.exp-use-side-data=yes
190 $ cat << EOF >> sidedata-source/.hg/hgrc
191 > [extensions]
192 > testsidedata=$TESTDIR/testlib/ext-sidedata-5.py
193 > EOF
194 $ hg init sidedata-target --config format.exp-use-side-data=no
195 $ cd sidedata-source
196 $ echo a > a
197 $ echo b > b
198 $ echo c > c
199 $ hg commit -Am "initial"
200 adding a
201 adding b
202 adding c
203 $ echo aa > a
204 $ hg commit -m "other"
205 $ hg pull -R ../sidedata-target ../sidedata-source
206 pulling from ../sidedata-source
207 requesting all changes
208 adding changesets
209 adding manifests
210 adding file changes
211 added 2 changesets with 4 changes to 3 files
212 new changesets 05da661850d7:7ec8b4049447
213 (run 'hg update' to get a working copy)
214 $ hg -R ../sidedata-target log -G
215 o changeset: 1:7ec8b4049447
216 | tag: tip
217 | user: test
218 | date: Thu Jan 01 00:00:00 1970 +0000
219 | summary: other
220 |
221 o changeset: 0:05da661850d7
222 user: test
223 date: Thu Jan 01 00:00:00 1970 +0000
224 summary: initial
225
226
227 $ hg -R ../sidedata-target debugsidedata -c 0
228 $ hg -R ../sidedata-target debugsidedata -c 1 -v
229 $ hg -R ../sidedata-target debugsidedata -m 0
230 $ hg -R ../sidedata-target debugsidedata -m 1 -v
231 $ hg -R ../sidedata-target debugsidedata a 0
232 $ hg -R ../sidedata-target debugsidedata a 1 -v
233 $ cd ..
234
235
236 Check sidedata exchange with on-the-fly generation and removal
237 ==============================================================
238
239 (Push) Target has strict superset of the source
240 -----------------------------------------------
241
242 $ hg init source-repo --config format.exp-use-side-data=yes
243 $ hg init target-repo --config format.exp-use-side-data=yes
244 $ cat << EOF >> target-repo/.hg/hgrc
245 > [extensions]
246 > testsidedata=$TESTDIR/testlib/ext-sidedata.py
247 > EOF
248 $ cd source-repo
249 $ echo aaa > a
250 $ hg add a
251 $ hg commit -m a
252 $ echo aaa > b
253 $ hg add b
254 $ hg commit -m b
255 $ echo xxx >> a
256 $ hg commit -m aa
257
258 No sidedata is generated in the source
259 $ hg debugsidedata -c 0
260
261 Check that sidedata capabilities are advertised
262 $ hg debugcapabilities ../target-repo | grep sidedata
263 exp-wanted-sidedata=1,2
264
265 We expect the client to abort the push since it's not capable of generating
266 what the server is asking
267 $ hg push -r . ../target-repo
268 pushing to ../target-repo
269 abort: cannot push: required sidedata category not supported by this client: '1'
270 [255]
271
272 Add the required capabilities
273 $ cat << EOF >> .hg/hgrc
274 > [extensions]
275 > testsidedata2=$TESTDIR/testlib/ext-sidedata-2.py
276 > EOF
277
278 We expect the target to have sidedata that was generated by the source on push
279 $ hg push -r . ../target-repo
280 pushing to ../target-repo
281 searching for changes
282 adding changesets
283 adding manifests
284 adding file changes
285 added 3 changesets with 3 changes to 2 files
286 $ cd ../target-repo
287 $ hg debugsidedata -c 0
288 2 sidedata entries
289 entry-0001 size 4
290 entry-0002 size 32
291 $ hg debugsidedata -c 1 -v
292 2 sidedata entries
293 entry-0001 size 4
294 '\x00\x00\x006'
295 entry-0002 size 32
296 '\x98\t\xf9\xc4v\xf0\xc5P\x90\xf7wRf\xe8\xe27e\xfc\xc1\x93\xa4\x96\xd0\x1d\x97\xaaG\x1d\xd7t\xfa\xde'
297 $ hg debugsidedata -m 2
298 2 sidedata entries
299 entry-0001 size 4
300 entry-0002 size 32
301 $ hg debugsidedata a 1
302 2 sidedata entries
303 entry-0001 size 4
304 entry-0002 size 32
305 $ cd ..
306
307 (Push) Difference is not subset/superset
308 ----------------------------------------
309
310 Source has one in common, one missing and one more sidedata category with the
311 target.
312
313 $ rm -rf source-repo target-repo
314 $ hg init source-repo --config format.exp-use-side-data=yes
315 $ cat << EOF >> source-repo/.hg/hgrc
316 > [extensions]
317 > testsidedata3=$TESTDIR/testlib/ext-sidedata-3.py
318 > EOF
319 $ hg init target-repo --config format.exp-use-side-data=yes
320 $ cat << EOF >> target-repo/.hg/hgrc
321 > [extensions]
322 > testsidedata4=$TESTDIR/testlib/ext-sidedata-4.py
323 > EOF
324 $ cd source-repo
325 $ echo aaa > a
326 $ hg add a
327 $ hg commit -m a
328 $ echo aaa > b
329 $ hg add b
330 $ hg commit -m b
331 $ echo xxx >> a
332 $ hg commit -m aa
333
334 Check that sidedata capabilities are advertised
335 $ hg debugcapabilities . | grep sidedata
336 exp-wanted-sidedata=1,2
337 $ hg debugcapabilities ../target-repo | grep sidedata
338 exp-wanted-sidedata=2,3
339
340 Sidedata is generated in the source, but only the right categories (entry-0001 and entry-0002)
341 $ hg debugsidedata -c 0
342 2 sidedata entries
343 entry-0001 size 4
344 entry-0002 size 32
345 $ hg debugsidedata -c 1 -v
346 2 sidedata entries
347 entry-0001 size 4
348 '\x00\x00\x006'
349 entry-0002 size 32
350 '\x98\t\xf9\xc4v\xf0\xc5P\x90\xf7wRf\xe8\xe27e\xfc\xc1\x93\xa4\x96\xd0\x1d\x97\xaaG\x1d\xd7t\xfa\xde'
351 $ hg debugsidedata -m 2
352 2 sidedata entries
353 entry-0001 size 4
354 entry-0002 size 32
355 $ hg debugsidedata a 1
356 2 sidedata entries
357 entry-0001 size 4
358 entry-0002 size 32
359
360
361 We expect the target to have sidedata that was generated by the source on push,
362 and also removed the sidedata categories that are not supported by the target.
363 Namely, we expect entry-0002 (only exchanged) and entry-0003 (generated),
364 but not entry-0001.
365
366 $ hg push -r . ../target-repo --traceback
367 pushing to ../target-repo
368 searching for changes
369 adding changesets
370 adding manifests
371 adding file changes
372 added 3 changesets with 3 changes to 2 files
373 $ cd ../target-repo
374 $ hg log -G
375 o changeset: 2:40f977031323
376 | tag: tip
377 | user: test
378 | date: Thu Jan 01 00:00:00 1970 +0000
379 | summary: aa
380 |
381 o changeset: 1:2707720c6597
382 | user: test
383 | date: Thu Jan 01 00:00:00 1970 +0000
384 | summary: b
385 |
386 o changeset: 0:7049e48789d7
387 user: test
388 date: Thu Jan 01 00:00:00 1970 +0000
389 summary: a
390
391 $ hg debugsidedata -c 0
392 2 sidedata entries
393 entry-0002 size 32
394 entry-0003 size 48
395 $ hg debugsidedata -c 1 -v
396 2 sidedata entries
397 entry-0002 size 32
398 '\x98\t\xf9\xc4v\xf0\xc5P\x90\xf7wRf\xe8\xe27e\xfc\xc1\x93\xa4\x96\xd0\x1d\x97\xaaG\x1d\xd7t\xfa\xde'
399 entry-0003 size 48
400 '\x87\xcf\xdfI/\xb5\xed\xeaC\xc1\xf0S\xf3X\x1c\xcc\x00m\xee\xe6#\xc1\xe3\xcaB8Fk\x82e\xfc\xc01\xf6\xb7\xb9\xb3([\xf6D\xa6\xcf\x9b\xea\x11{\x08'
401 $ hg debugsidedata -m 2
402 2 sidedata entries
403 entry-0002 size 32
404 entry-0003 size 48
405 $ hg debugsidedata a 1
406 2 sidedata entries
407 entry-0002 size 32
408 entry-0003 size 48
409 $ cd ..
410
411 (Pull) Target has strict superset of the source
412 -----------------------------------------------
413
414 $ rm -rf source-repo target-repo
415 $ hg init source-repo --config format.exp-use-side-data=yes
416 $ hg init target-repo --config format.exp-use-side-data=yes
417 $ cat << EOF >> target-repo/.hg/hgrc
418 > [extensions]
419 > testsidedata=$TESTDIR/testlib/ext-sidedata.py
420 > EOF
421 $ cd source-repo
422 $ echo aaa > a
423 $ hg add a
424 $ hg commit -m a
425 $ echo aaa > b
426 $ hg add b
427 $ hg commit -m b
428 $ echo xxx >> a
429 $ hg commit -m aa
430
431 No sidedata is generated in the source
432 $ hg debugsidedata -c 0
433
434 Check that sidedata capabilities are advertised
435 $ hg debugcapabilities ../target-repo | grep sidedata
436 exp-wanted-sidedata=1,2
437
438 $ cd ../target-repo
439
440 Add the required capabilities
441 $ cat << EOF >> .hg/hgrc
442 > [extensions]
443 > testsidedata2=$TESTDIR/testlib/ext-sidedata-2.py
444 > EOF
445
446 We expect the target to have sidedata that it generated on-the-fly during pull
447 $ hg pull -r . ../source-repo --traceback
448 pulling from ../source-repo
449 adding changesets
450 adding manifests
451 adding file changes
452 added 3 changesets with 3 changes to 2 files
453 new changesets 7049e48789d7:40f977031323
454 (run 'hg update' to get a working copy)
455 $ hg debugsidedata -c 0 --traceback
456 2 sidedata entries
457 entry-0001 size 4
458 entry-0002 size 32
459 $ hg debugsidedata -c 1 -v --traceback
460 2 sidedata entries
461 entry-0001 size 4
462 '\x00\x00\x006'
463 entry-0002 size 32
464 '\x98\t\xf9\xc4v\xf0\xc5P\x90\xf7wRf\xe8\xe27e\xfc\xc1\x93\xa4\x96\xd0\x1d\x97\xaaG\x1d\xd7t\xfa\xde'
465 $ hg debugsidedata -m 2
466 2 sidedata entries
467 entry-0001 size 4
468 entry-0002 size 32
469 $ hg debugsidedata a 1
470 2 sidedata entries
471 entry-0001 size 4
472 entry-0002 size 32
473 $ cd ..
@@ -0,0 +1,81 b''
1 # coding: utf8
2 # ext-sidedata-5.py - small extension to test (differently still) the sidedata
3 # logic
4 #
5 # Simulates a server for a simple sidedata exchange.
6 #
7 # Copyright 2021 Raphaël Gomès <rgomes@octobus.net>
8 #
9 # This software may be used and distributed according to the terms of the
10 # GNU General Public License version 2 or any later version.
11
12 from __future__ import absolute_import
13
14 import hashlib
15 import struct
16
17 from mercurial import (
18 extensions,
19 revlog,
20 )
21
22
23 from mercurial.revlogutils import sidedata as sidedatamod
24
25
26 def compute_sidedata_1(repo, revlog, rev, sidedata, text=None):
27 sidedata = sidedata.copy()
28 if text is None:
29 text = revlog.revision(rev)
30 sidedata[sidedatamod.SD_TEST1] = struct.pack('>I', len(text))
31 return sidedata
32
33
34 def compute_sidedata_2(repo, revlog, rev, sidedata, text=None):
35 sidedata = sidedata.copy()
36 if text is None:
37 text = revlog.revision(rev)
38 sha256 = hashlib.sha256(text).digest()
39 sidedata[sidedatamod.SD_TEST2] = struct.pack('>32s', sha256)
40 return sidedata
41
42
43 def reposetup(ui, repo):
44 # Sidedata keys happen to be the same as the categories, easier for testing.
45 for kind in (b'changelog', b'manifest', b'filelog'):
46 repo.register_sidedata_computer(
47 kind,
48 sidedatamod.SD_TEST1,
49 (sidedatamod.SD_TEST1,),
50 compute_sidedata_1,
51 )
52 repo.register_sidedata_computer(
53 kind,
54 sidedatamod.SD_TEST2,
55 (sidedatamod.SD_TEST2,),
56 compute_sidedata_2,
57 )
58
59 # We don't register sidedata computers because we don't care within these
60 # tests
61 repo.register_wanted_sidedata(sidedatamod.SD_TEST1)
62 repo.register_wanted_sidedata(sidedatamod.SD_TEST2)
63
64
65 def wrapaddrevision(
66 orig, self, text, transaction, link, p1, p2, *args, **kwargs
67 ):
68 if kwargs.get('sidedata') is None:
69 kwargs['sidedata'] = {}
70 sd = kwargs['sidedata']
71 ## let's store some arbitrary data just for testing
72 # text length
73 sd[sidedatamod.SD_TEST1] = struct.pack('>I', len(text))
74 # and sha2 hashes
75 sha256 = hashlib.sha256(text).digest()
76 sd[sidedatamod.SD_TEST2] = struct.pack('>32s', sha256)
77 return orig(self, text, transaction, link, p1, p2, *args, **kwargs)
78
79
80 def extsetup(ui):
81 extensions.wrapfunction(revlog.revlog, 'addrevision', wrapaddrevision)
@@ -7,6 +7,7 b''
7 7
8 8 from __future__ import absolute_import
9 9
10 import collections
10 11 import os
11 12 import struct
12 13 import weakref
@@ -252,7 +253,7 b' class cg1unpacker(object):'
252 253 pos = next
253 254 yield closechunk()
254 255
255 def _unpackmanifests(self, repo, revmap, trp, prog):
256 def _unpackmanifests(self, repo, revmap, trp, prog, addrevisioncb=None):
256 257 self.callback = prog.increment
257 258 # no need to check for empty manifest group here:
258 259 # if the result of the merge of 1 and 2 is the same in 3 and 4,
@@ -260,7 +261,8 b' class cg1unpacker(object):'
260 261 # be empty during the pull
261 262 self.manifestheader()
262 263 deltas = self.deltaiter()
263 repo.manifestlog.getstorage(b'').addgroup(deltas, revmap, trp)
264 storage = repo.manifestlog.getstorage(b'')
265 storage.addgroup(deltas, revmap, trp, addrevisioncb=addrevisioncb)
264 266 prog.complete()
265 267 self.callback = None
266 268
@@ -369,6 +371,13 b' class cg1unpacker(object):'
369 371 efilesset = None
370 372 self.callback = None
371 373
374 # Keep track of the (non-changelog) revlogs we've updated and their
375 # range of new revisions for sidedata rewrite.
376 # TODO do something more efficient than keeping the reference to
377 # the revlogs, especially memory-wise.
378 touched_manifests = {}
379 touched_filelogs = {}
380
372 381 # pull off the manifest group
373 382 repo.ui.status(_(b"adding manifests\n"))
374 383 # We know that we'll never have more manifests than we had
@@ -376,7 +385,24 b' class cg1unpacker(object):'
376 385 progress = repo.ui.makeprogress(
377 386 _(b'manifests'), unit=_(b'chunks'), total=changesets
378 387 )
379 self._unpackmanifests(repo, revmap, trp, progress)
388 on_manifest_rev = None
389 if sidedata_helpers and b'manifest' in sidedata_helpers[1]:
390
391 def on_manifest_rev(manifest, rev):
392 range = touched_manifests.get(manifest)
393 if not range:
394 touched_manifests[manifest] = (rev, rev)
395 else:
396 assert rev == range[1] + 1
397 touched_manifests[manifest] = (range[0], rev)
398
399 self._unpackmanifests(
400 repo,
401 revmap,
402 trp,
403 progress,
404 addrevisioncb=on_manifest_rev,
405 )
380 406
381 407 needfiles = {}
382 408 if repo.ui.configbool(b'server', b'validate'):
@@ -390,12 +416,37 b' class cg1unpacker(object):'
390 416 for f, n in pycompat.iteritems(mfest):
391 417 needfiles.setdefault(f, set()).add(n)
392 418
419 on_filelog_rev = None
420 if sidedata_helpers and b'filelog' in sidedata_helpers[1]:
421
422 def on_filelog_rev(filelog, rev):
423 range = touched_filelogs.get(filelog)
424 if not range:
425 touched_filelogs[filelog] = (rev, rev)
426 else:
427 assert rev == range[1] + 1
428 touched_filelogs[filelog] = (range[0], rev)
429
393 430 # process the files
394 431 repo.ui.status(_(b"adding file changes\n"))
395 432 newrevs, newfiles = _addchangegroupfiles(
396 repo, self, revmap, trp, efiles, needfiles
433 repo,
434 self,
435 revmap,
436 trp,
437 efiles,
438 needfiles,
439 addrevisioncb=on_filelog_rev,
397 440 )
398 441
442 if sidedata_helpers:
443 if b'changelog' in sidedata_helpers[1]:
444 cl.rewrite_sidedata(sidedata_helpers, clstart, clend - 1)
445 for mf, (startrev, endrev) in touched_manifests.items():
446 mf.rewrite_sidedata(sidedata_helpers, startrev, endrev)
447 for fl, (startrev, endrev) in touched_filelogs.items():
448 fl.rewrite_sidedata(sidedata_helpers, startrev, endrev)
449
399 450 # making sure the value exists
400 451 tr.changes.setdefault(b'changegroup-count-changesets', 0)
401 452 tr.changes.setdefault(b'changegroup-count-revisions', 0)
@@ -559,14 +610,18 b' class cg3unpacker(cg2unpacker):'
559 610 node, p1, p2, deltabase, cs, flags = headertuple
560 611 return node, p1, p2, deltabase, cs, flags
561 612
562 def _unpackmanifests(self, repo, revmap, trp, prog):
563 super(cg3unpacker, self)._unpackmanifests(repo, revmap, trp, prog)
613 def _unpackmanifests(self, repo, revmap, trp, prog, addrevisioncb=None):
614 super(cg3unpacker, self)._unpackmanifests(
615 repo, revmap, trp, prog, addrevisioncb=addrevisioncb
616 )
564 617 for chunkdata in iter(self.filelogheader, {}):
565 618 # If we get here, there are directory manifests in the changegroup
566 619 d = chunkdata[b"filename"]
567 620 repo.ui.debug(b"adding %s revisions\n" % d)
568 621 deltas = self.deltaiter()
569 if not repo.manifestlog.getstorage(d).addgroup(deltas, revmap, trp):
622 if not repo.manifestlog.getstorage(d).addgroup(
623 deltas, revmap, trp, addrevisioncb=addrevisioncb
624 ):
570 625 raise error.Abort(_(b"received dir revlog group is empty"))
571 626
572 627
@@ -1793,7 +1848,15 b' def makestream('
1793 1848 return bundler.generate(commonrevs, csets, fastpathlinkrev, source)
1794 1849
1795 1850
1796 def _addchangegroupfiles(repo, source, revmap, trp, expectedfiles, needfiles):
1851 def _addchangegroupfiles(
1852 repo,
1853 source,
1854 revmap,
1855 trp,
1856 expectedfiles,
1857 needfiles,
1858 addrevisioncb=None,
1859 ):
1797 1860 revisions = 0
1798 1861 files = 0
1799 1862 progress = repo.ui.makeprogress(
@@ -1808,7 +1871,13 b' def _addchangegroupfiles(repo, source, r'
1808 1871 o = len(fl)
1809 1872 try:
1810 1873 deltas = source.deltaiter()
1811 if not fl.addgroup(deltas, revmap, trp):
1874 added = fl.addgroup(
1875 deltas,
1876 revmap,
1877 trp,
1878 addrevisioncb=addrevisioncb,
1879 )
1880 if not added:
1812 1881 raise error.Abort(_(b"received file revlog group is empty"))
1813 1882 except error.CensoredBaseError as e:
1814 1883 raise error.Abort(_(b"received delta base is censored: %s") % e)
@@ -3205,3 +3205,54 b' class revlog(object):'
3205 3205 )
3206 3206
3207 3207 return d
3208
3209 def rewrite_sidedata(self, helpers, startrev, endrev):
3210 if self.version & 0xFFFF != REVLOGV2:
3211 return
3212 # inline are not yet supported because they suffer from an issue when
3213 # rewriting them (since it's not an append-only operation).
3214 # See issue6485.
3215 assert not self._inline
3216 if not helpers[1] and not helpers[2]:
3217 # Nothing to generate or remove
3218 return
3219
3220 new_entries = []
3221 # append the new sidedata
3222 with self._datafp(b'a+') as fp:
3223 # Maybe this bug still exists, see revlog._writeentry
3224 fp.seek(0, os.SEEK_END)
3225 current_offset = fp.tell()
3226 for rev in range(startrev, endrev + 1):
3227 entry = self.index[rev]
3228 new_sidedata = storageutil.run_sidedata_helpers(
3229 store=self,
3230 sidedata_helpers=helpers,
3231 sidedata={},
3232 rev=rev,
3233 )
3234
3235 serialized_sidedata = sidedatautil.serialize_sidedata(
3236 new_sidedata
3237 )
3238 if entry[8] != 0 or entry[9] != 0:
3239 # rewriting entries that already have sidedata is not
3240 # supported yet, because it introduces garbage data in the
3241 # revlog.
3242 msg = "Rewriting existing sidedata is not supported yet"
3243 raise error.Abort(msg)
3244 entry = entry[:8]
3245 entry += (current_offset, len(serialized_sidedata))
3246
3247 fp.write(serialized_sidedata)
3248 new_entries.append(entry)
3249 current_offset += len(serialized_sidedata)
3250
3251 # rewrite the new index entries
3252 with self._indexfp(b'w+') as fp:
3253 fp.seek(startrev * self._io.size)
3254 for i, entry in enumerate(new_entries):
3255 rev = startrev + i
3256 self.index.replace_sidedata_info(rev, entry[8], entry[9])
3257 packed = self._io.packentry(entry, self.node, self.version, rev)
3258 fp.write(packed)
@@ -271,13 +271,12 b' copy information on to the filelog'
271 271 $ hg ci --amend -m 'copy a to j, v2'
272 272 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-*-amend.hg (glob)
273 273 $ hg debugsidedata -c -v -- -1
274 1 sidedata entries (missing-correct-output !)
275 entry-0014 size 24 (missing-correct-output !)
276 '\x00\x00\x00\x02\x00\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00aj' (missing-correct-output !)
274 1 sidedata entries
275 entry-0014 size 24
276 '\x00\x00\x00\x02\x00\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00aj'
277 277 #endif
278 278 $ hg showcopies --config experimental.copies.read-from=filelog-only
279 a -> j (sidedata missing-correct-output !)
280 a -> j (no-sidedata !)
279 a -> j
281 280 The entries should be written to extras even if they're empty (so the client
282 281 won't have to fall back to reading from filelogs)
283 282 $ echo x >> j
@@ -355,8 +354,7 b' with no fallback.'
355 354 saved backup bundle to $TESTTMP/rebase-rename/.hg/strip-backup/*-*-rebase.hg (glob)
356 355 $ hg st --change . --copies
357 356 A b
358 a (sidedata missing-correct-output !)
359 a (no-sidedata !)
357 a
360 358 R a
361 359 $ cd ..
362 360
@@ -1,6 +1,9 b''
1 # ext-sidedata.py - small extension to test the sidedata logic
1 # coding: utf8
2 # ext-sidedata-2.py - small extension to test (differently) the sidedata logic
2 3 #
3 # Copyright 2019 Pierre-Yves David <pierre-yves.david@octobus.net)
4 # Simulates a client for a complex sidedata exchange.
5 #
6 # Copyright 2021 Raphaël Gomès <rgomes@octobus.net>
4 7 #
5 8 # This software may be used and distributed according to the terms of the
6 9 # GNU General Public License version 2 or any later version.
@@ -10,79 +13,38 b' from __future__ import absolute_import'
10 13 import hashlib
11 14 import struct
12 15
13 from mercurial.node import (
14 nullid,
15 nullrev,
16 )
17 from mercurial import (
18 extensions,
19 requirements,
20 revlog,
21 )
22
23 from mercurial.upgrade_utils import engine as upgrade_engine
24
25 from mercurial.revlogutils import sidedata
16 from mercurial.revlogutils import sidedata as sidedatamod
26 17
27 18
28 def wrapaddrevision(
29 orig, self, text, transaction, link, p1, p2, *args, **kwargs
30 ):
31 if kwargs.get('sidedata') is None:
32 kwargs['sidedata'] = {}
33 sd = kwargs['sidedata']
34 ## let's store some arbitrary data just for testing
35 # text length
36 sd[sidedata.SD_TEST1] = struct.pack('>I', len(text))
37 # and sha2 hashes
38 sha256 = hashlib.sha256(text).digest()
39 sd[sidedata.SD_TEST2] = struct.pack('>32s', sha256)
40 return orig(self, text, transaction, link, p1, p2, *args, **kwargs)
19 def compute_sidedata_1(repo, revlog, rev, sidedata, text=None):
20 sidedata = sidedata.copy()
21 if text is None:
22 text = revlog.revision(rev)
23 sidedata[sidedatamod.SD_TEST1] = struct.pack('>I', len(text))
24 return sidedata
41 25
42 26
43 def wrap_revisiondata(orig, self, nodeorrev, *args, **kwargs):
44 text, sd = orig(self, nodeorrev, *args, **kwargs)
45 if getattr(self, 'sidedatanocheck', False):
46 return text, sd
47 if self.version & 0xFFFF != 2:
48 return text, sd
49 if nodeorrev != nullrev and nodeorrev != nullid:
50 if len(text) != struct.unpack('>I', sd[sidedata.SD_TEST1])[0]:
51 raise RuntimeError('text size mismatch')
52 expected = sd[sidedata.SD_TEST2]
53 got = hashlib.sha256(text).digest()
54 if got != expected:
55 raise RuntimeError('sha256 mismatch')
56 return text, sd
27 def compute_sidedata_2(repo, revlog, rev, sidedata, text=None):
28 sidedata = sidedata.copy()
29 if text is None:
30 text = revlog.revision(rev)
31 sha256 = hashlib.sha256(text).digest()
32 sidedata[sidedatamod.SD_TEST2] = struct.pack('>32s', sha256)
33 return sidedata
57 34
58 35
59 def wrapgetsidedatacompanion(orig, srcrepo, dstrepo):
60 sidedatacompanion = orig(srcrepo, dstrepo)
61 addedreqs = dstrepo.requirements - srcrepo.requirements
62 if requirements.SIDEDATA_REQUIREMENT in addedreqs:
63 assert sidedatacompanion is None # deal with composition later
64
65 def sidedatacompanion(revlog, rev):
66 update = {}
67 revlog.sidedatanocheck = True
68 try:
69 text = revlog.revision(rev)
70 finally:
71 del revlog.sidedatanocheck
72 ## let's store some arbitrary data just for testing
73 # text length
74 update[sidedata.SD_TEST1] = struct.pack('>I', len(text))
75 # and sha2 hashes
76 sha256 = hashlib.sha256(text).digest()
77 update[sidedata.SD_TEST2] = struct.pack('>32s', sha256)
78 return False, (), update, 0, 0
79
80 return sidedatacompanion
81
82
83 def extsetup(ui):
84 extensions.wrapfunction(revlog.revlog, 'addrevision', wrapaddrevision)
85 extensions.wrapfunction(revlog.revlog, '_revisiondata', wrap_revisiondata)
86 extensions.wrapfunction(
87 upgrade_engine, 'getsidedatacompanion', wrapgetsidedatacompanion
88 )
36 def reposetup(ui, repo):
37 # Sidedata keys happen to be the same as the categories, easier for testing.
38 for kind in (b'changelog', b'manifest', b'filelog'):
39 repo.register_sidedata_computer(
40 kind,
41 sidedatamod.SD_TEST1,
42 (sidedatamod.SD_TEST1,),
43 compute_sidedata_1,
44 )
45 repo.register_sidedata_computer(
46 kind,
47 sidedatamod.SD_TEST2,
48 (sidedatamod.SD_TEST2,),
49 compute_sidedata_2,
50 )
@@ -1,6 +1,10 b''
1 # ext-sidedata.py - small extension to test the sidedata logic
1 # coding: utf8
2 # ext-sidedata-3.py - small extension to test (differently still) the sidedata
3 # logic
2 4 #
3 # Copyright 2019 Pierre-Yves David <pierre-yves.david@octobus.net)
5 # Simulates a client for a complex sidedata exchange.
6 #
7 # Copyright 2021 Raphaël Gomès <rgomes@octobus.net>
4 8 #
5 9 # This software may be used and distributed according to the terms of the
6 10 # GNU General Public License version 2 or any later version.
@@ -10,19 +14,38 b' from __future__ import absolute_import'
10 14 import hashlib
11 15 import struct
12 16
13 from mercurial.node import (
14 nullid,
15 nullrev,
16 )
17 17 from mercurial import (
18 18 extensions,
19 requirements,
20 19 revlog,
21 20 )
22 21
23 from mercurial.upgrade_utils import engine as upgrade_engine
22 from mercurial.revlogutils import sidedata as sidedatamod
23
24
25 def compute_sidedata_1(repo, revlog, rev, sidedata, text=None):
26 sidedata = sidedata.copy()
27 if text is None:
28 text = revlog.revision(rev)
29 sidedata[sidedatamod.SD_TEST1] = struct.pack('>I', len(text))
30 return sidedata
31
24 32
25 from mercurial.revlogutils import sidedata
33 def compute_sidedata_2(repo, revlog, rev, sidedata, text=None):
34 sidedata = sidedata.copy()
35 if text is None:
36 text = revlog.revision(rev)
37 sha256 = hashlib.sha256(text).digest()
38 sidedata[sidedatamod.SD_TEST2] = struct.pack('>32s', sha256)
39 return sidedata
40
41
42 def compute_sidedata_3(repo, revlog, rev, sidedata, text=None):
43 sidedata = sidedata.copy()
44 if text is None:
45 text = revlog.revision(rev)
46 sha384 = hashlib.sha384(text).digest()
47 sidedata[sidedatamod.SD_TEST3] = struct.pack('>48s', sha384)
48 return sidedata
26 49
27 50
28 51 def wrapaddrevision(
@@ -31,58 +54,35 b' def wrapaddrevision('
31 54 if kwargs.get('sidedata') is None:
32 55 kwargs['sidedata'] = {}
33 56 sd = kwargs['sidedata']
34 ## let's store some arbitrary data just for testing
35 # text length
36 sd[sidedata.SD_TEST1] = struct.pack('>I', len(text))
37 # and sha2 hashes
38 sha256 = hashlib.sha256(text).digest()
39 sd[sidedata.SD_TEST2] = struct.pack('>32s', sha256)
57 sd = compute_sidedata_1(None, self, None, sd, text=text)
58 kwargs['sidedata'] = compute_sidedata_2(None, self, None, sd, text=text)
40 59 return orig(self, text, transaction, link, p1, p2, *args, **kwargs)
41 60
42 61
43 def wrap_revisiondata(orig, self, nodeorrev, *args, **kwargs):
44 text, sd = orig(self, nodeorrev, *args, **kwargs)
45 if getattr(self, 'sidedatanocheck', False):
46 return text, sd
47 if self.version & 0xFFFF != 2:
48 return text, sd
49 if nodeorrev != nullrev and nodeorrev != nullid:
50 if len(text) != struct.unpack('>I', sd[sidedata.SD_TEST1])[0]:
51 raise RuntimeError('text size mismatch')
52 expected = sd[sidedata.SD_TEST2]
53 got = hashlib.sha256(text).digest()
54 if got != expected:
55 raise RuntimeError('sha256 mismatch')
56 return text, sd
57
58
59 def wrapgetsidedatacompanion(orig, srcrepo, dstrepo):
60 sidedatacompanion = orig(srcrepo, dstrepo)
61 addedreqs = dstrepo.requirements - srcrepo.requirements
62 if requirements.SIDEDATA_REQUIREMENT in addedreqs:
63 assert sidedatacompanion is None # deal with composition later
64
65 def sidedatacompanion(revlog, rev):
66 update = {}
67 revlog.sidedatanocheck = True
68 try:
69 text = revlog.revision(rev)
70 finally:
71 del revlog.sidedatanocheck
72 ## let's store some arbitrary data just for testing
73 # text length
74 update[sidedata.SD_TEST1] = struct.pack('>I', len(text))
75 # and sha2 hashes
76 sha256 = hashlib.sha256(text).digest()
77 update[sidedata.SD_TEST2] = struct.pack('>32s', sha256)
78 return False, (), update, 0, 0
79
80 return sidedatacompanion
81
82
83 62 def extsetup(ui):
84 63 extensions.wrapfunction(revlog.revlog, 'addrevision', wrapaddrevision)
85 extensions.wrapfunction(revlog.revlog, '_revisiondata', wrap_revisiondata)
86 extensions.wrapfunction(
87 upgrade_engine, 'getsidedatacompanion', wrapgetsidedatacompanion
88 )
64
65
66 def reposetup(ui, repo):
67 # Sidedata keys happen to be the same as the categories, easier for testing.
68 for kind in (b'changelog', b'manifest', b'filelog'):
69 repo.register_sidedata_computer(
70 kind,
71 sidedatamod.SD_TEST1,
72 (sidedatamod.SD_TEST1,),
73 compute_sidedata_1,
74 )
75 repo.register_sidedata_computer(
76 kind,
77 sidedatamod.SD_TEST2,
78 (sidedatamod.SD_TEST2,),
79 compute_sidedata_2,
80 )
81 repo.register_sidedata_computer(
82 kind,
83 sidedatamod.SD_TEST3,
84 (sidedatamod.SD_TEST3,),
85 compute_sidedata_3,
86 )
87 repo.register_wanted_sidedata(sidedatamod.SD_TEST1)
88 repo.register_wanted_sidedata(sidedatamod.SD_TEST2)
@@ -1,88 +1,19 b''
1 # ext-sidedata.py - small extension to test the sidedata logic
1 # coding: utf8
2 # ext-sidedata-4.py - small extension to test (differently still) the sidedata
3 # logic
2 4 #
3 # Copyright 2019 Pierre-Yves David <pierre-yves.david@octobus.net)
5 # Simulates a server for a complex sidedata exchange.
6 #
7 # Copyright 2021 Raphaël Gomès <rgomes@octobus.net>
4 8 #
5 9 # This software may be used and distributed according to the terms of the
6 10 # GNU General Public License version 2 or any later version.
7 11
8 12 from __future__ import absolute_import
9 13
10 import hashlib
11 import struct
12
13 from mercurial.node import (
14 nullid,
15 nullrev,
16 )
17 from mercurial import (
18 extensions,
19 requirements,
20 revlog,
21 )
22
23 from mercurial.upgrade_utils import engine as upgrade_engine
24
25 14 from mercurial.revlogutils import sidedata
26 15
27 16
28 def wrapaddrevision(
29 orig, self, text, transaction, link, p1, p2, *args, **kwargs
30 ):
31 if kwargs.get('sidedata') is None:
32 kwargs['sidedata'] = {}
33 sd = kwargs['sidedata']
34 ## let's store some arbitrary data just for testing
35 # text length
36 sd[sidedata.SD_TEST1] = struct.pack('>I', len(text))
37 # and sha2 hashes
38 sha256 = hashlib.sha256(text).digest()
39 sd[sidedata.SD_TEST2] = struct.pack('>32s', sha256)
40 return orig(self, text, transaction, link, p1, p2, *args, **kwargs)
41
42
43 def wrap_revisiondata(orig, self, nodeorrev, *args, **kwargs):
44 text, sd = orig(self, nodeorrev, *args, **kwargs)
45 if getattr(self, 'sidedatanocheck', False):
46 return text, sd
47 if self.version & 0xFFFF != 2:
48 return text, sd
49 if nodeorrev != nullrev and nodeorrev != nullid:
50 if len(text) != struct.unpack('>I', sd[sidedata.SD_TEST1])[0]:
51 raise RuntimeError('text size mismatch')
52 expected = sd[sidedata.SD_TEST2]
53 got = hashlib.sha256(text).digest()
54 if got != expected:
55 raise RuntimeError('sha256 mismatch')
56 return text, sd
57
58
59 def wrapgetsidedatacompanion(orig, srcrepo, dstrepo):
60 sidedatacompanion = orig(srcrepo, dstrepo)
61 addedreqs = dstrepo.requirements - srcrepo.requirements
62 if requirements.SIDEDATA_REQUIREMENT in addedreqs:
63 assert sidedatacompanion is None # deal with composition later
64
65 def sidedatacompanion(revlog, rev):
66 update = {}
67 revlog.sidedatanocheck = True
68 try:
69 text = revlog.revision(rev)
70 finally:
71 del revlog.sidedatanocheck
72 ## let's store some arbitrary data just for testing
73 # text length
74 update[sidedata.SD_TEST1] = struct.pack('>I', len(text))
75 # and sha2 hashes
76 sha256 = hashlib.sha256(text).digest()
77 update[sidedata.SD_TEST2] = struct.pack('>32s', sha256)
78 return False, (), update, 0, 0
79
80 return sidedatacompanion
81
82
83 def extsetup(ui):
84 extensions.wrapfunction(revlog.revlog, 'addrevision', wrapaddrevision)
85 extensions.wrapfunction(revlog.revlog, '_revisiondata', wrap_revisiondata)
86 extensions.wrapfunction(
87 upgrade_engine, 'getsidedatacompanion', wrapgetsidedatacompanion
88 )
17 def reposetup(ui, repo):
18 repo.register_wanted_sidedata(sidedata.SD_TEST2)
19 repo.register_wanted_sidedata(sidedata.SD_TEST3)
@@ -1,6 +1,6 b''
1 1 # ext-sidedata.py - small extension to test the sidedata logic
2 2 #
3 # Copyright 2019 Pierre-Yves David <pierre-yves.david@octobus.net)
3 # Copyright 2019 Pierre-Yves David <pierre-yves.david@octobus.net>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
@@ -47,11 +47,12 b' def wrap_revisiondata(orig, self, nodeor'
47 47 if self.version & 0xFFFF != 2:
48 48 return text, sd
49 49 if nodeorrev != nullrev and nodeorrev != nullid:
50 if len(text) != struct.unpack('>I', sd[sidedata.SD_TEST1])[0]:
50 cat1 = sd.get(sidedata.SD_TEST1)
51 if cat1 is not None and len(text) != struct.unpack('>I', cat1)[0]:
51 52 raise RuntimeError('text size mismatch')
52 expected = sd[sidedata.SD_TEST2]
53 expected = sd.get(sidedata.SD_TEST2)
53 54 got = hashlib.sha256(text).digest()
54 if got != expected:
55 if expected is not None and got != expected:
55 56 raise RuntimeError('sha256 mismatch')
56 57 return text, sd
57 58
@@ -86,3 +87,10 b' def extsetup(ui):'
86 87 extensions.wrapfunction(
87 88 upgrade_engine, 'getsidedatacompanion', wrapgetsidedatacompanion
88 89 )
90
91
92 def reposetup(ui, repo):
93 # We don't register sidedata computers because we don't care within these
94 # tests
95 repo.register_wanted_sidedata(sidedata.SD_TEST1)
96 repo.register_wanted_sidedata(sidedata.SD_TEST2)
General Comments 0
You need to be logged in to leave comments. Login now