##// END OF EJS Templates
dirstate-v2: Add `hg debugupgraderepo` command support...
Simon Sapin -
r48111:a43d256c default
parent child Browse files
Show More
@@ -1,1979 +1,1980
1 1 # dirstate.py - working directory tracking for mercurial
2 2 #
3 3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
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.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import collections
11 11 import contextlib
12 12 import errno
13 13 import os
14 14 import stat
15 15
16 16 from .i18n import _
17 17 from .pycompat import delattr
18 18
19 19 from hgdemandimport import tracing
20 20
21 21 from . import (
22 22 encoding,
23 23 error,
24 24 match as matchmod,
25 25 pathutil,
26 26 policy,
27 27 pycompat,
28 28 scmutil,
29 29 sparse,
30 30 txnutil,
31 31 util,
32 32 )
33 33
34 34 from .interfaces import (
35 35 dirstate as intdirstate,
36 36 util as interfaceutil,
37 37 )
38 38
39 39 parsers = policy.importmod('parsers')
40 40 rustmod = policy.importrust('dirstate')
41 41
42 42 SUPPORTS_DIRSTATE_V2 = rustmod is not None
43 43
44 44 propertycache = util.propertycache
45 45 filecache = scmutil.filecache
46 46 _rangemask = 0x7FFFFFFF
47 47
48 48 dirstatetuple = parsers.dirstatetuple
49 49
50 50
51 51 class repocache(filecache):
52 52 """filecache for files in .hg/"""
53 53
54 54 def join(self, obj, fname):
55 55 return obj._opener.join(fname)
56 56
57 57
58 58 class rootcache(filecache):
59 59 """filecache for files in the repository root"""
60 60
61 61 def join(self, obj, fname):
62 62 return obj._join(fname)
63 63
64 64
65 65 def _getfsnow(vfs):
66 66 '''Get "now" timestamp on filesystem'''
67 67 tmpfd, tmpname = vfs.mkstemp()
68 68 try:
69 69 return os.fstat(tmpfd)[stat.ST_MTIME]
70 70 finally:
71 71 os.close(tmpfd)
72 72 vfs.unlink(tmpname)
73 73
74 74
75 75 @interfaceutil.implementer(intdirstate.idirstate)
76 76 class dirstate(object):
77 77 def __init__(
78 78 self,
79 79 opener,
80 80 ui,
81 81 root,
82 82 validate,
83 83 sparsematchfn,
84 84 nodeconstants,
85 85 use_dirstate_v2,
86 86 ):
87 87 """Create a new dirstate object.
88 88
89 89 opener is an open()-like callable that can be used to open the
90 90 dirstate file; root is the root of the directory tracked by
91 91 the dirstate.
92 92 """
93 93 self._use_dirstate_v2 = use_dirstate_v2
94 94 self._nodeconstants = nodeconstants
95 95 self._opener = opener
96 96 self._validate = validate
97 97 self._root = root
98 98 self._sparsematchfn = sparsematchfn
99 99 # ntpath.join(root, '') of Python 2.7.9 does not add sep if root is
100 100 # UNC path pointing to root share (issue4557)
101 101 self._rootdir = pathutil.normasprefix(root)
102 102 self._dirty = False
103 103 self._lastnormaltime = 0
104 104 self._ui = ui
105 105 self._filecache = {}
106 106 self._parentwriters = 0
107 107 self._filename = b'dirstate'
108 108 self._pendingfilename = b'%s.pending' % self._filename
109 109 self._plchangecallbacks = {}
110 110 self._origpl = None
111 111 self._updatedfiles = set()
112 112 self._mapcls = dirstatemap
113 113 # Access and cache cwd early, so we don't access it for the first time
114 114 # after a working-copy update caused it to not exist (accessing it then
115 115 # raises an exception).
116 116 self._cwd
117 117
118 118 def prefetch_parents(self):
119 119 """make sure the parents are loaded
120 120
121 121 Used to avoid a race condition.
122 122 """
123 123 self._pl
124 124
125 125 @contextlib.contextmanager
126 126 def parentchange(self):
127 127 """Context manager for handling dirstate parents.
128 128
129 129 If an exception occurs in the scope of the context manager,
130 130 the incoherent dirstate won't be written when wlock is
131 131 released.
132 132 """
133 133 self._parentwriters += 1
134 134 yield
135 135 # Typically we want the "undo" step of a context manager in a
136 136 # finally block so it happens even when an exception
137 137 # occurs. In this case, however, we only want to decrement
138 138 # parentwriters if the code in the with statement exits
139 139 # normally, so we don't have a try/finally here on purpose.
140 140 self._parentwriters -= 1
141 141
142 142 def pendingparentchange(self):
143 143 """Returns true if the dirstate is in the middle of a set of changes
144 144 that modify the dirstate parent.
145 145 """
146 146 return self._parentwriters > 0
147 147
148 148 @propertycache
149 149 def _map(self):
150 150 """Return the dirstate contents (see documentation for dirstatemap)."""
151 151 self._map = self._mapcls(
152 152 self._ui,
153 153 self._opener,
154 154 self._root,
155 155 self._nodeconstants,
156 156 self._use_dirstate_v2,
157 157 )
158 158 return self._map
159 159
160 160 @property
161 161 def _sparsematcher(self):
162 162 """The matcher for the sparse checkout.
163 163
164 164 The working directory may not include every file from a manifest. The
165 165 matcher obtained by this property will match a path if it is to be
166 166 included in the working directory.
167 167 """
168 168 # TODO there is potential to cache this property. For now, the matcher
169 169 # is resolved on every access. (But the called function does use a
170 170 # cache to keep the lookup fast.)
171 171 return self._sparsematchfn()
172 172
173 173 @repocache(b'branch')
174 174 def _branch(self):
175 175 try:
176 176 return self._opener.read(b"branch").strip() or b"default"
177 177 except IOError as inst:
178 178 if inst.errno != errno.ENOENT:
179 179 raise
180 180 return b"default"
181 181
182 182 @property
183 183 def _pl(self):
184 184 return self._map.parents()
185 185
186 186 def hasdir(self, d):
187 187 return self._map.hastrackeddir(d)
188 188
189 189 @rootcache(b'.hgignore')
190 190 def _ignore(self):
191 191 files = self._ignorefiles()
192 192 if not files:
193 193 return matchmod.never()
194 194
195 195 pats = [b'include:%s' % f for f in files]
196 196 return matchmod.match(self._root, b'', [], pats, warn=self._ui.warn)
197 197
198 198 @propertycache
199 199 def _slash(self):
200 200 return self._ui.configbool(b'ui', b'slash') and pycompat.ossep != b'/'
201 201
202 202 @propertycache
203 203 def _checklink(self):
204 204 return util.checklink(self._root)
205 205
206 206 @propertycache
207 207 def _checkexec(self):
208 208 return bool(util.checkexec(self._root))
209 209
210 210 @propertycache
211 211 def _checkcase(self):
212 212 return not util.fscasesensitive(self._join(b'.hg'))
213 213
214 214 def _join(self, f):
215 215 # much faster than os.path.join()
216 216 # it's safe because f is always a relative path
217 217 return self._rootdir + f
218 218
219 219 def flagfunc(self, buildfallback):
220 220 if self._checklink and self._checkexec:
221 221
222 222 def f(x):
223 223 try:
224 224 st = os.lstat(self._join(x))
225 225 if util.statislink(st):
226 226 return b'l'
227 227 if util.statisexec(st):
228 228 return b'x'
229 229 except OSError:
230 230 pass
231 231 return b''
232 232
233 233 return f
234 234
235 235 fallback = buildfallback()
236 236 if self._checklink:
237 237
238 238 def f(x):
239 239 if os.path.islink(self._join(x)):
240 240 return b'l'
241 241 if b'x' in fallback(x):
242 242 return b'x'
243 243 return b''
244 244
245 245 return f
246 246 if self._checkexec:
247 247
248 248 def f(x):
249 249 if b'l' in fallback(x):
250 250 return b'l'
251 251 if util.isexec(self._join(x)):
252 252 return b'x'
253 253 return b''
254 254
255 255 return f
256 256 else:
257 257 return fallback
258 258
259 259 @propertycache
260 260 def _cwd(self):
261 261 # internal config: ui.forcecwd
262 262 forcecwd = self._ui.config(b'ui', b'forcecwd')
263 263 if forcecwd:
264 264 return forcecwd
265 265 return encoding.getcwd()
266 266
267 267 def getcwd(self):
268 268 """Return the path from which a canonical path is calculated.
269 269
270 270 This path should be used to resolve file patterns or to convert
271 271 canonical paths back to file paths for display. It shouldn't be
272 272 used to get real file paths. Use vfs functions instead.
273 273 """
274 274 cwd = self._cwd
275 275 if cwd == self._root:
276 276 return b''
277 277 # self._root ends with a path separator if self._root is '/' or 'C:\'
278 278 rootsep = self._root
279 279 if not util.endswithsep(rootsep):
280 280 rootsep += pycompat.ossep
281 281 if cwd.startswith(rootsep):
282 282 return cwd[len(rootsep) :]
283 283 else:
284 284 # we're outside the repo. return an absolute path.
285 285 return cwd
286 286
287 287 def pathto(self, f, cwd=None):
288 288 if cwd is None:
289 289 cwd = self.getcwd()
290 290 path = util.pathto(self._root, cwd, f)
291 291 if self._slash:
292 292 return util.pconvert(path)
293 293 return path
294 294
295 295 def __getitem__(self, key):
296 296 """Return the current state of key (a filename) in the dirstate.
297 297
298 298 States are:
299 299 n normal
300 300 m needs merging
301 301 r marked for removal
302 302 a marked for addition
303 303 ? not tracked
304 304 """
305 305 return self._map.get(key, (b"?",))[0]
306 306
307 307 def __contains__(self, key):
308 308 return key in self._map
309 309
310 310 def __iter__(self):
311 311 return iter(sorted(self._map))
312 312
313 313 def items(self):
314 314 return pycompat.iteritems(self._map)
315 315
316 316 iteritems = items
317 317
318 318 def parents(self):
319 319 return [self._validate(p) for p in self._pl]
320 320
321 321 def p1(self):
322 322 return self._validate(self._pl[0])
323 323
324 324 def p2(self):
325 325 return self._validate(self._pl[1])
326 326
327 327 def branch(self):
328 328 return encoding.tolocal(self._branch)
329 329
330 330 def setparents(self, p1, p2=None):
331 331 """Set dirstate parents to p1 and p2.
332 332
333 333 When moving from two parents to one, 'm' merged entries a
334 334 adjusted to normal and previous copy records discarded and
335 335 returned by the call.
336 336
337 337 See localrepo.setparents()
338 338 """
339 339 if p2 is None:
340 340 p2 = self._nodeconstants.nullid
341 341 if self._parentwriters == 0:
342 342 raise ValueError(
343 343 b"cannot set dirstate parent outside of "
344 344 b"dirstate.parentchange context manager"
345 345 )
346 346
347 347 self._dirty = True
348 348 oldp2 = self._pl[1]
349 349 if self._origpl is None:
350 350 self._origpl = self._pl
351 351 self._map.setparents(p1, p2)
352 352 copies = {}
353 353 if (
354 354 oldp2 != self._nodeconstants.nullid
355 355 and p2 == self._nodeconstants.nullid
356 356 ):
357 357 candidatefiles = self._map.non_normal_or_other_parent_paths()
358 358
359 359 for f in candidatefiles:
360 360 s = self._map.get(f)
361 361 if s is None:
362 362 continue
363 363
364 364 # Discard 'm' markers when moving away from a merge state
365 365 if s[0] == b'm':
366 366 source = self._map.copymap.get(f)
367 367 if source:
368 368 copies[f] = source
369 369 self.normallookup(f)
370 370 # Also fix up otherparent markers
371 371 elif s[0] == b'n' and s[2] == -2:
372 372 source = self._map.copymap.get(f)
373 373 if source:
374 374 copies[f] = source
375 375 self.add(f)
376 376 return copies
377 377
378 378 def setbranch(self, branch):
379 379 self.__class__._branch.set(self, encoding.fromlocal(branch))
380 380 f = self._opener(b'branch', b'w', atomictemp=True, checkambig=True)
381 381 try:
382 382 f.write(self._branch + b'\n')
383 383 f.close()
384 384
385 385 # make sure filecache has the correct stat info for _branch after
386 386 # replacing the underlying file
387 387 ce = self._filecache[b'_branch']
388 388 if ce:
389 389 ce.refresh()
390 390 except: # re-raises
391 391 f.discard()
392 392 raise
393 393
394 394 def invalidate(self):
395 395 """Causes the next access to reread the dirstate.
396 396
397 397 This is different from localrepo.invalidatedirstate() because it always
398 398 rereads the dirstate. Use localrepo.invalidatedirstate() if you want to
399 399 check whether the dirstate has changed before rereading it."""
400 400
401 401 for a in ("_map", "_branch", "_ignore"):
402 402 if a in self.__dict__:
403 403 delattr(self, a)
404 404 self._lastnormaltime = 0
405 405 self._dirty = False
406 406 self._updatedfiles.clear()
407 407 self._parentwriters = 0
408 408 self._origpl = None
409 409
410 410 def copy(self, source, dest):
411 411 """Mark dest as a copy of source. Unmark dest if source is None."""
412 412 if source == dest:
413 413 return
414 414 self._dirty = True
415 415 if source is not None:
416 416 self._map.copymap[dest] = source
417 417 self._updatedfiles.add(source)
418 418 self._updatedfiles.add(dest)
419 419 elif self._map.copymap.pop(dest, None):
420 420 self._updatedfiles.add(dest)
421 421
422 422 def copied(self, file):
423 423 return self._map.copymap.get(file, None)
424 424
425 425 def copies(self):
426 426 return self._map.copymap
427 427
428 428 def _addpath(self, f, state, mode, size, mtime):
429 429 oldstate = self[f]
430 430 if state == b'a' or oldstate == b'r':
431 431 scmutil.checkfilename(f)
432 432 if self._map.hastrackeddir(f):
433 433 raise error.Abort(
434 434 _(b'directory %r already in dirstate') % pycompat.bytestr(f)
435 435 )
436 436 # shadows
437 437 for d in pathutil.finddirs(f):
438 438 if self._map.hastrackeddir(d):
439 439 break
440 440 entry = self._map.get(d)
441 441 if entry is not None and entry[0] != b'r':
442 442 raise error.Abort(
443 443 _(b'file %r in dirstate clashes with %r')
444 444 % (pycompat.bytestr(d), pycompat.bytestr(f))
445 445 )
446 446 self._dirty = True
447 447 self._updatedfiles.add(f)
448 448 self._map.addfile(f, oldstate, state, mode, size, mtime)
449 449
450 450 def normal(self, f, parentfiledata=None):
451 451 """Mark a file normal and clean.
452 452
453 453 parentfiledata: (mode, size, mtime) of the clean file
454 454
455 455 parentfiledata should be computed from memory (for mode,
456 456 size), as or close as possible from the point where we
457 457 determined the file was clean, to limit the risk of the
458 458 file having been changed by an external process between the
459 459 moment where the file was determined to be clean and now."""
460 460 if parentfiledata:
461 461 (mode, size, mtime) = parentfiledata
462 462 else:
463 463 s = os.lstat(self._join(f))
464 464 mode = s.st_mode
465 465 size = s.st_size
466 466 mtime = s[stat.ST_MTIME]
467 467 self._addpath(f, b'n', mode, size & _rangemask, mtime & _rangemask)
468 468 self._map.copymap.pop(f, None)
469 469 if f in self._map.nonnormalset:
470 470 self._map.nonnormalset.remove(f)
471 471 if mtime > self._lastnormaltime:
472 472 # Remember the most recent modification timeslot for status(),
473 473 # to make sure we won't miss future size-preserving file content
474 474 # modifications that happen within the same timeslot.
475 475 self._lastnormaltime = mtime
476 476
477 477 def normallookup(self, f):
478 478 '''Mark a file normal, but possibly dirty.'''
479 479 if self._pl[1] != self._nodeconstants.nullid:
480 480 # if there is a merge going on and the file was either
481 481 # in state 'm' (-1) or coming from other parent (-2) before
482 482 # being removed, restore that state.
483 483 entry = self._map.get(f)
484 484 if entry is not None:
485 485 if entry[0] == b'r' and entry[2] in (-1, -2):
486 486 source = self._map.copymap.get(f)
487 487 if entry[2] == -1:
488 488 self.merge(f)
489 489 elif entry[2] == -2:
490 490 self.otherparent(f)
491 491 if source:
492 492 self.copy(source, f)
493 493 return
494 494 if entry[0] == b'm' or entry[0] == b'n' and entry[2] == -2:
495 495 return
496 496 self._addpath(f, b'n', 0, -1, -1)
497 497 self._map.copymap.pop(f, None)
498 498
499 499 def otherparent(self, f):
500 500 '''Mark as coming from the other parent, always dirty.'''
501 501 if self._pl[1] == self._nodeconstants.nullid:
502 502 raise error.Abort(
503 503 _(b"setting %r to other parent only allowed in merges") % f
504 504 )
505 505 if f in self and self[f] == b'n':
506 506 # merge-like
507 507 self._addpath(f, b'm', 0, -2, -1)
508 508 else:
509 509 # add-like
510 510 self._addpath(f, b'n', 0, -2, -1)
511 511 self._map.copymap.pop(f, None)
512 512
513 513 def add(self, f):
514 514 '''Mark a file added.'''
515 515 self._addpath(f, b'a', 0, -1, -1)
516 516 self._map.copymap.pop(f, None)
517 517
518 518 def remove(self, f):
519 519 '''Mark a file removed.'''
520 520 self._dirty = True
521 521 oldstate = self[f]
522 522 size = 0
523 523 if self._pl[1] != self._nodeconstants.nullid:
524 524 entry = self._map.get(f)
525 525 if entry is not None:
526 526 # backup the previous state
527 527 if entry[0] == b'm': # merge
528 528 size = -1
529 529 elif entry[0] == b'n' and entry[2] == -2: # other parent
530 530 size = -2
531 531 self._map.otherparentset.add(f)
532 532 self._updatedfiles.add(f)
533 533 self._map.removefile(f, oldstate, size)
534 534 if size == 0:
535 535 self._map.copymap.pop(f, None)
536 536
537 537 def merge(self, f):
538 538 '''Mark a file merged.'''
539 539 if self._pl[1] == self._nodeconstants.nullid:
540 540 return self.normallookup(f)
541 541 return self.otherparent(f)
542 542
543 543 def drop(self, f):
544 544 '''Drop a file from the dirstate'''
545 545 oldstate = self[f]
546 546 if self._map.dropfile(f, oldstate):
547 547 self._dirty = True
548 548 self._updatedfiles.add(f)
549 549 self._map.copymap.pop(f, None)
550 550
551 551 def _discoverpath(self, path, normed, ignoremissing, exists, storemap):
552 552 if exists is None:
553 553 exists = os.path.lexists(os.path.join(self._root, path))
554 554 if not exists:
555 555 # Maybe a path component exists
556 556 if not ignoremissing and b'/' in path:
557 557 d, f = path.rsplit(b'/', 1)
558 558 d = self._normalize(d, False, ignoremissing, None)
559 559 folded = d + b"/" + f
560 560 else:
561 561 # No path components, preserve original case
562 562 folded = path
563 563 else:
564 564 # recursively normalize leading directory components
565 565 # against dirstate
566 566 if b'/' in normed:
567 567 d, f = normed.rsplit(b'/', 1)
568 568 d = self._normalize(d, False, ignoremissing, True)
569 569 r = self._root + b"/" + d
570 570 folded = d + b"/" + util.fspath(f, r)
571 571 else:
572 572 folded = util.fspath(normed, self._root)
573 573 storemap[normed] = folded
574 574
575 575 return folded
576 576
577 577 def _normalizefile(self, path, isknown, ignoremissing=False, exists=None):
578 578 normed = util.normcase(path)
579 579 folded = self._map.filefoldmap.get(normed, None)
580 580 if folded is None:
581 581 if isknown:
582 582 folded = path
583 583 else:
584 584 folded = self._discoverpath(
585 585 path, normed, ignoremissing, exists, self._map.filefoldmap
586 586 )
587 587 return folded
588 588
589 589 def _normalize(self, path, isknown, ignoremissing=False, exists=None):
590 590 normed = util.normcase(path)
591 591 folded = self._map.filefoldmap.get(normed, None)
592 592 if folded is None:
593 593 folded = self._map.dirfoldmap.get(normed, None)
594 594 if folded is None:
595 595 if isknown:
596 596 folded = path
597 597 else:
598 598 # store discovered result in dirfoldmap so that future
599 599 # normalizefile calls don't start matching directories
600 600 folded = self._discoverpath(
601 601 path, normed, ignoremissing, exists, self._map.dirfoldmap
602 602 )
603 603 return folded
604 604
605 605 def normalize(self, path, isknown=False, ignoremissing=False):
606 606 """
607 607 normalize the case of a pathname when on a casefolding filesystem
608 608
609 609 isknown specifies whether the filename came from walking the
610 610 disk, to avoid extra filesystem access.
611 611
612 612 If ignoremissing is True, missing path are returned
613 613 unchanged. Otherwise, we try harder to normalize possibly
614 614 existing path components.
615 615
616 616 The normalized case is determined based on the following precedence:
617 617
618 618 - version of name already stored in the dirstate
619 619 - version of name stored on disk
620 620 - version provided via command arguments
621 621 """
622 622
623 623 if self._checkcase:
624 624 return self._normalize(path, isknown, ignoremissing)
625 625 return path
626 626
627 627 def clear(self):
628 628 self._map.clear()
629 629 self._lastnormaltime = 0
630 630 self._updatedfiles.clear()
631 631 self._dirty = True
632 632
633 633 def rebuild(self, parent, allfiles, changedfiles=None):
634 634 if changedfiles is None:
635 635 # Rebuild entire dirstate
636 636 to_lookup = allfiles
637 637 to_drop = []
638 638 lastnormaltime = self._lastnormaltime
639 639 self.clear()
640 640 self._lastnormaltime = lastnormaltime
641 641 elif len(changedfiles) < 10:
642 642 # Avoid turning allfiles into a set, which can be expensive if it's
643 643 # large.
644 644 to_lookup = []
645 645 to_drop = []
646 646 for f in changedfiles:
647 647 if f in allfiles:
648 648 to_lookup.append(f)
649 649 else:
650 650 to_drop.append(f)
651 651 else:
652 652 changedfilesset = set(changedfiles)
653 653 to_lookup = changedfilesset & set(allfiles)
654 654 to_drop = changedfilesset - to_lookup
655 655
656 656 if self._origpl is None:
657 657 self._origpl = self._pl
658 658 self._map.setparents(parent, self._nodeconstants.nullid)
659 659
660 660 for f in to_lookup:
661 661 self.normallookup(f)
662 662 for f in to_drop:
663 663 self.drop(f)
664 664
665 665 self._dirty = True
666 666
667 667 def identity(self):
668 668 """Return identity of dirstate itself to detect changing in storage
669 669
670 670 If identity of previous dirstate is equal to this, writing
671 671 changes based on the former dirstate out can keep consistency.
672 672 """
673 673 return self._map.identity
674 674
675 675 def write(self, tr):
676 676 if not self._dirty:
677 677 return
678 678
679 679 filename = self._filename
680 680 if tr:
681 681 # 'dirstate.write()' is not only for writing in-memory
682 682 # changes out, but also for dropping ambiguous timestamp.
683 683 # delayed writing re-raise "ambiguous timestamp issue".
684 684 # See also the wiki page below for detail:
685 685 # https://www.mercurial-scm.org/wiki/DirstateTransactionPlan
686 686
687 687 # emulate dropping timestamp in 'parsers.pack_dirstate'
688 688 now = _getfsnow(self._opener)
689 689 self._map.clearambiguoustimes(self._updatedfiles, now)
690 690
691 691 # emulate that all 'dirstate.normal' results are written out
692 692 self._lastnormaltime = 0
693 693 self._updatedfiles.clear()
694 694
695 695 # delay writing in-memory changes out
696 696 tr.addfilegenerator(
697 697 b'dirstate',
698 698 (self._filename,),
699 699 self._writedirstate,
700 700 location=b'plain',
701 701 )
702 702 return
703 703
704 704 st = self._opener(filename, b"w", atomictemp=True, checkambig=True)
705 705 self._writedirstate(st)
706 706
707 707 def addparentchangecallback(self, category, callback):
708 708 """add a callback to be called when the wd parents are changed
709 709
710 710 Callback will be called with the following arguments:
711 711 dirstate, (oldp1, oldp2), (newp1, newp2)
712 712
713 713 Category is a unique identifier to allow overwriting an old callback
714 714 with a newer callback.
715 715 """
716 716 self._plchangecallbacks[category] = callback
717 717
718 718 def _writedirstate(self, st):
719 719 # notify callbacks about parents change
720 720 if self._origpl is not None and self._origpl != self._pl:
721 721 for c, callback in sorted(
722 722 pycompat.iteritems(self._plchangecallbacks)
723 723 ):
724 724 callback(self, self._origpl, self._pl)
725 725 self._origpl = None
726 726 # use the modification time of the newly created temporary file as the
727 727 # filesystem's notion of 'now'
728 728 now = util.fstat(st)[stat.ST_MTIME] & _rangemask
729 729
730 730 # enough 'delaywrite' prevents 'pack_dirstate' from dropping
731 731 # timestamp of each entries in dirstate, because of 'now > mtime'
732 732 delaywrite = self._ui.configint(b'debug', b'dirstate.delaywrite')
733 733 if delaywrite > 0:
734 734 # do we have any files to delay for?
735 735 for f, e in pycompat.iteritems(self._map):
736 736 if e[0] == b'n' and e[3] == now:
737 737 import time # to avoid useless import
738 738
739 739 # rather than sleep n seconds, sleep until the next
740 740 # multiple of n seconds
741 741 clock = time.time()
742 742 start = int(clock) - (int(clock) % delaywrite)
743 743 end = start + delaywrite
744 744 time.sleep(end - clock)
745 745 now = end # trust our estimate that the end is near now
746 746 break
747 747
748 748 self._map.write(st, now)
749 749 self._lastnormaltime = 0
750 750 self._dirty = False
751 751
752 752 def _dirignore(self, f):
753 753 if self._ignore(f):
754 754 return True
755 755 for p in pathutil.finddirs(f):
756 756 if self._ignore(p):
757 757 return True
758 758 return False
759 759
760 760 def _ignorefiles(self):
761 761 files = []
762 762 if os.path.exists(self._join(b'.hgignore')):
763 763 files.append(self._join(b'.hgignore'))
764 764 for name, path in self._ui.configitems(b"ui"):
765 765 if name == b'ignore' or name.startswith(b'ignore.'):
766 766 # we need to use os.path.join here rather than self._join
767 767 # because path is arbitrary and user-specified
768 768 files.append(os.path.join(self._rootdir, util.expandpath(path)))
769 769 return files
770 770
771 771 def _ignorefileandline(self, f):
772 772 files = collections.deque(self._ignorefiles())
773 773 visited = set()
774 774 while files:
775 775 i = files.popleft()
776 776 patterns = matchmod.readpatternfile(
777 777 i, self._ui.warn, sourceinfo=True
778 778 )
779 779 for pattern, lineno, line in patterns:
780 780 kind, p = matchmod._patsplit(pattern, b'glob')
781 781 if kind == b"subinclude":
782 782 if p not in visited:
783 783 files.append(p)
784 784 continue
785 785 m = matchmod.match(
786 786 self._root, b'', [], [pattern], warn=self._ui.warn
787 787 )
788 788 if m(f):
789 789 return (i, lineno, line)
790 790 visited.add(i)
791 791 return (None, -1, b"")
792 792
793 793 def _walkexplicit(self, match, subrepos):
794 794 """Get stat data about the files explicitly specified by match.
795 795
796 796 Return a triple (results, dirsfound, dirsnotfound).
797 797 - results is a mapping from filename to stat result. It also contains
798 798 listings mapping subrepos and .hg to None.
799 799 - dirsfound is a list of files found to be directories.
800 800 - dirsnotfound is a list of files that the dirstate thinks are
801 801 directories and that were not found."""
802 802
803 803 def badtype(mode):
804 804 kind = _(b'unknown')
805 805 if stat.S_ISCHR(mode):
806 806 kind = _(b'character device')
807 807 elif stat.S_ISBLK(mode):
808 808 kind = _(b'block device')
809 809 elif stat.S_ISFIFO(mode):
810 810 kind = _(b'fifo')
811 811 elif stat.S_ISSOCK(mode):
812 812 kind = _(b'socket')
813 813 elif stat.S_ISDIR(mode):
814 814 kind = _(b'directory')
815 815 return _(b'unsupported file type (type is %s)') % kind
816 816
817 817 badfn = match.bad
818 818 dmap = self._map
819 819 lstat = os.lstat
820 820 getkind = stat.S_IFMT
821 821 dirkind = stat.S_IFDIR
822 822 regkind = stat.S_IFREG
823 823 lnkkind = stat.S_IFLNK
824 824 join = self._join
825 825 dirsfound = []
826 826 foundadd = dirsfound.append
827 827 dirsnotfound = []
828 828 notfoundadd = dirsnotfound.append
829 829
830 830 if not match.isexact() and self._checkcase:
831 831 normalize = self._normalize
832 832 else:
833 833 normalize = None
834 834
835 835 files = sorted(match.files())
836 836 subrepos.sort()
837 837 i, j = 0, 0
838 838 while i < len(files) and j < len(subrepos):
839 839 subpath = subrepos[j] + b"/"
840 840 if files[i] < subpath:
841 841 i += 1
842 842 continue
843 843 while i < len(files) and files[i].startswith(subpath):
844 844 del files[i]
845 845 j += 1
846 846
847 847 if not files or b'' in files:
848 848 files = [b'']
849 849 # constructing the foldmap is expensive, so don't do it for the
850 850 # common case where files is ['']
851 851 normalize = None
852 852 results = dict.fromkeys(subrepos)
853 853 results[b'.hg'] = None
854 854
855 855 for ff in files:
856 856 if normalize:
857 857 nf = normalize(ff, False, True)
858 858 else:
859 859 nf = ff
860 860 if nf in results:
861 861 continue
862 862
863 863 try:
864 864 st = lstat(join(nf))
865 865 kind = getkind(st.st_mode)
866 866 if kind == dirkind:
867 867 if nf in dmap:
868 868 # file replaced by dir on disk but still in dirstate
869 869 results[nf] = None
870 870 foundadd((nf, ff))
871 871 elif kind == regkind or kind == lnkkind:
872 872 results[nf] = st
873 873 else:
874 874 badfn(ff, badtype(kind))
875 875 if nf in dmap:
876 876 results[nf] = None
877 877 except OSError as inst: # nf not found on disk - it is dirstate only
878 878 if nf in dmap: # does it exactly match a missing file?
879 879 results[nf] = None
880 880 else: # does it match a missing directory?
881 881 if self._map.hasdir(nf):
882 882 notfoundadd(nf)
883 883 else:
884 884 badfn(ff, encoding.strtolocal(inst.strerror))
885 885
886 886 # match.files() may contain explicitly-specified paths that shouldn't
887 887 # be taken; drop them from the list of files found. dirsfound/notfound
888 888 # aren't filtered here because they will be tested later.
889 889 if match.anypats():
890 890 for f in list(results):
891 891 if f == b'.hg' or f in subrepos:
892 892 # keep sentinel to disable further out-of-repo walks
893 893 continue
894 894 if not match(f):
895 895 del results[f]
896 896
897 897 # Case insensitive filesystems cannot rely on lstat() failing to detect
898 898 # a case-only rename. Prune the stat object for any file that does not
899 899 # match the case in the filesystem, if there are multiple files that
900 900 # normalize to the same path.
901 901 if match.isexact() and self._checkcase:
902 902 normed = {}
903 903
904 904 for f, st in pycompat.iteritems(results):
905 905 if st is None:
906 906 continue
907 907
908 908 nc = util.normcase(f)
909 909 paths = normed.get(nc)
910 910
911 911 if paths is None:
912 912 paths = set()
913 913 normed[nc] = paths
914 914
915 915 paths.add(f)
916 916
917 917 for norm, paths in pycompat.iteritems(normed):
918 918 if len(paths) > 1:
919 919 for path in paths:
920 920 folded = self._discoverpath(
921 921 path, norm, True, None, self._map.dirfoldmap
922 922 )
923 923 if path != folded:
924 924 results[path] = None
925 925
926 926 return results, dirsfound, dirsnotfound
927 927
928 928 def walk(self, match, subrepos, unknown, ignored, full=True):
929 929 """
930 930 Walk recursively through the directory tree, finding all files
931 931 matched by match.
932 932
933 933 If full is False, maybe skip some known-clean files.
934 934
935 935 Return a dict mapping filename to stat-like object (either
936 936 mercurial.osutil.stat instance or return value of os.stat()).
937 937
938 938 """
939 939 # full is a flag that extensions that hook into walk can use -- this
940 940 # implementation doesn't use it at all. This satisfies the contract
941 941 # because we only guarantee a "maybe".
942 942
943 943 if ignored:
944 944 ignore = util.never
945 945 dirignore = util.never
946 946 elif unknown:
947 947 ignore = self._ignore
948 948 dirignore = self._dirignore
949 949 else:
950 950 # if not unknown and not ignored, drop dir recursion and step 2
951 951 ignore = util.always
952 952 dirignore = util.always
953 953
954 954 matchfn = match.matchfn
955 955 matchalways = match.always()
956 956 matchtdir = match.traversedir
957 957 dmap = self._map
958 958 listdir = util.listdir
959 959 lstat = os.lstat
960 960 dirkind = stat.S_IFDIR
961 961 regkind = stat.S_IFREG
962 962 lnkkind = stat.S_IFLNK
963 963 join = self._join
964 964
965 965 exact = skipstep3 = False
966 966 if match.isexact(): # match.exact
967 967 exact = True
968 968 dirignore = util.always # skip step 2
969 969 elif match.prefix(): # match.match, no patterns
970 970 skipstep3 = True
971 971
972 972 if not exact and self._checkcase:
973 973 normalize = self._normalize
974 974 normalizefile = self._normalizefile
975 975 skipstep3 = False
976 976 else:
977 977 normalize = self._normalize
978 978 normalizefile = None
979 979
980 980 # step 1: find all explicit files
981 981 results, work, dirsnotfound = self._walkexplicit(match, subrepos)
982 982 if matchtdir:
983 983 for d in work:
984 984 matchtdir(d[0])
985 985 for d in dirsnotfound:
986 986 matchtdir(d)
987 987
988 988 skipstep3 = skipstep3 and not (work or dirsnotfound)
989 989 work = [d for d in work if not dirignore(d[0])]
990 990
991 991 # step 2: visit subdirectories
992 992 def traverse(work, alreadynormed):
993 993 wadd = work.append
994 994 while work:
995 995 tracing.counter('dirstate.walk work', len(work))
996 996 nd = work.pop()
997 997 visitentries = match.visitchildrenset(nd)
998 998 if not visitentries:
999 999 continue
1000 1000 if visitentries == b'this' or visitentries == b'all':
1001 1001 visitentries = None
1002 1002 skip = None
1003 1003 if nd != b'':
1004 1004 skip = b'.hg'
1005 1005 try:
1006 1006 with tracing.log('dirstate.walk.traverse listdir %s', nd):
1007 1007 entries = listdir(join(nd), stat=True, skip=skip)
1008 1008 except OSError as inst:
1009 1009 if inst.errno in (errno.EACCES, errno.ENOENT):
1010 1010 match.bad(
1011 1011 self.pathto(nd), encoding.strtolocal(inst.strerror)
1012 1012 )
1013 1013 continue
1014 1014 raise
1015 1015 for f, kind, st in entries:
1016 1016 # Some matchers may return files in the visitentries set,
1017 1017 # instead of 'this', if the matcher explicitly mentions them
1018 1018 # and is not an exactmatcher. This is acceptable; we do not
1019 1019 # make any hard assumptions about file-or-directory below
1020 1020 # based on the presence of `f` in visitentries. If
1021 1021 # visitchildrenset returned a set, we can always skip the
1022 1022 # entries *not* in the set it provided regardless of whether
1023 1023 # they're actually a file or a directory.
1024 1024 if visitentries and f not in visitentries:
1025 1025 continue
1026 1026 if normalizefile:
1027 1027 # even though f might be a directory, we're only
1028 1028 # interested in comparing it to files currently in the
1029 1029 # dmap -- therefore normalizefile is enough
1030 1030 nf = normalizefile(
1031 1031 nd and (nd + b"/" + f) or f, True, True
1032 1032 )
1033 1033 else:
1034 1034 nf = nd and (nd + b"/" + f) or f
1035 1035 if nf not in results:
1036 1036 if kind == dirkind:
1037 1037 if not ignore(nf):
1038 1038 if matchtdir:
1039 1039 matchtdir(nf)
1040 1040 wadd(nf)
1041 1041 if nf in dmap and (matchalways or matchfn(nf)):
1042 1042 results[nf] = None
1043 1043 elif kind == regkind or kind == lnkkind:
1044 1044 if nf in dmap:
1045 1045 if matchalways or matchfn(nf):
1046 1046 results[nf] = st
1047 1047 elif (matchalways or matchfn(nf)) and not ignore(
1048 1048 nf
1049 1049 ):
1050 1050 # unknown file -- normalize if necessary
1051 1051 if not alreadynormed:
1052 1052 nf = normalize(nf, False, True)
1053 1053 results[nf] = st
1054 1054 elif nf in dmap and (matchalways or matchfn(nf)):
1055 1055 results[nf] = None
1056 1056
1057 1057 for nd, d in work:
1058 1058 # alreadynormed means that processwork doesn't have to do any
1059 1059 # expensive directory normalization
1060 1060 alreadynormed = not normalize or nd == d
1061 1061 traverse([d], alreadynormed)
1062 1062
1063 1063 for s in subrepos:
1064 1064 del results[s]
1065 1065 del results[b'.hg']
1066 1066
1067 1067 # step 3: visit remaining files from dmap
1068 1068 if not skipstep3 and not exact:
1069 1069 # If a dmap file is not in results yet, it was either
1070 1070 # a) not matching matchfn b) ignored, c) missing, or d) under a
1071 1071 # symlink directory.
1072 1072 if not results and matchalways:
1073 1073 visit = [f for f in dmap]
1074 1074 else:
1075 1075 visit = [f for f in dmap if f not in results and matchfn(f)]
1076 1076 visit.sort()
1077 1077
1078 1078 if unknown:
1079 1079 # unknown == True means we walked all dirs under the roots
1080 1080 # that wasn't ignored, and everything that matched was stat'ed
1081 1081 # and is already in results.
1082 1082 # The rest must thus be ignored or under a symlink.
1083 1083 audit_path = pathutil.pathauditor(self._root, cached=True)
1084 1084
1085 1085 for nf in iter(visit):
1086 1086 # If a stat for the same file was already added with a
1087 1087 # different case, don't add one for this, since that would
1088 1088 # make it appear as if the file exists under both names
1089 1089 # on disk.
1090 1090 if (
1091 1091 normalizefile
1092 1092 and normalizefile(nf, True, True) in results
1093 1093 ):
1094 1094 results[nf] = None
1095 1095 # Report ignored items in the dmap as long as they are not
1096 1096 # under a symlink directory.
1097 1097 elif audit_path.check(nf):
1098 1098 try:
1099 1099 results[nf] = lstat(join(nf))
1100 1100 # file was just ignored, no links, and exists
1101 1101 except OSError:
1102 1102 # file doesn't exist
1103 1103 results[nf] = None
1104 1104 else:
1105 1105 # It's either missing or under a symlink directory
1106 1106 # which we in this case report as missing
1107 1107 results[nf] = None
1108 1108 else:
1109 1109 # We may not have walked the full directory tree above,
1110 1110 # so stat and check everything we missed.
1111 1111 iv = iter(visit)
1112 1112 for st in util.statfiles([join(i) for i in visit]):
1113 1113 results[next(iv)] = st
1114 1114 return results
1115 1115
1116 1116 def _rust_status(self, matcher, list_clean, list_ignored, list_unknown):
1117 1117 # Force Rayon (Rust parallelism library) to respect the number of
1118 1118 # workers. This is a temporary workaround until Rust code knows
1119 1119 # how to read the config file.
1120 1120 numcpus = self._ui.configint(b"worker", b"numcpus")
1121 1121 if numcpus is not None:
1122 1122 encoding.environ.setdefault(b'RAYON_NUM_THREADS', b'%d' % numcpus)
1123 1123
1124 1124 workers_enabled = self._ui.configbool(b"worker", b"enabled", True)
1125 1125 if not workers_enabled:
1126 1126 encoding.environ[b"RAYON_NUM_THREADS"] = b"1"
1127 1127
1128 1128 (
1129 1129 lookup,
1130 1130 modified,
1131 1131 added,
1132 1132 removed,
1133 1133 deleted,
1134 1134 clean,
1135 1135 ignored,
1136 1136 unknown,
1137 1137 warnings,
1138 1138 bad,
1139 1139 traversed,
1140 1140 ) = rustmod.status(
1141 1141 self._map._rustmap,
1142 1142 matcher,
1143 1143 self._rootdir,
1144 1144 self._ignorefiles(),
1145 1145 self._checkexec,
1146 1146 self._lastnormaltime,
1147 1147 bool(list_clean),
1148 1148 bool(list_ignored),
1149 1149 bool(list_unknown),
1150 1150 bool(matcher.traversedir),
1151 1151 )
1152 1152
1153 1153 if matcher.traversedir:
1154 1154 for dir in traversed:
1155 1155 matcher.traversedir(dir)
1156 1156
1157 1157 if self._ui.warn:
1158 1158 for item in warnings:
1159 1159 if isinstance(item, tuple):
1160 1160 file_path, syntax = item
1161 1161 msg = _(b"%s: ignoring invalid syntax '%s'\n") % (
1162 1162 file_path,
1163 1163 syntax,
1164 1164 )
1165 1165 self._ui.warn(msg)
1166 1166 else:
1167 1167 msg = _(b"skipping unreadable pattern file '%s': %s\n")
1168 1168 self._ui.warn(
1169 1169 msg
1170 1170 % (
1171 1171 pathutil.canonpath(
1172 1172 self._rootdir, self._rootdir, item
1173 1173 ),
1174 1174 b"No such file or directory",
1175 1175 )
1176 1176 )
1177 1177
1178 1178 for (fn, message) in bad:
1179 1179 matcher.bad(fn, encoding.strtolocal(message))
1180 1180
1181 1181 status = scmutil.status(
1182 1182 modified=modified,
1183 1183 added=added,
1184 1184 removed=removed,
1185 1185 deleted=deleted,
1186 1186 unknown=unknown,
1187 1187 ignored=ignored,
1188 1188 clean=clean,
1189 1189 )
1190 1190 return (lookup, status)
1191 1191
1192 1192 def status(self, match, subrepos, ignored, clean, unknown):
1193 1193 """Determine the status of the working copy relative to the
1194 1194 dirstate and return a pair of (unsure, status), where status is of type
1195 1195 scmutil.status and:
1196 1196
1197 1197 unsure:
1198 1198 files that might have been modified since the dirstate was
1199 1199 written, but need to be read to be sure (size is the same
1200 1200 but mtime differs)
1201 1201 status.modified:
1202 1202 files that have definitely been modified since the dirstate
1203 1203 was written (different size or mode)
1204 1204 status.clean:
1205 1205 files that have definitely not been modified since the
1206 1206 dirstate was written
1207 1207 """
1208 1208 listignored, listclean, listunknown = ignored, clean, unknown
1209 1209 lookup, modified, added, unknown, ignored = [], [], [], [], []
1210 1210 removed, deleted, clean = [], [], []
1211 1211
1212 1212 dmap = self._map
1213 1213 dmap.preload()
1214 1214
1215 1215 use_rust = True
1216 1216
1217 1217 allowed_matchers = (
1218 1218 matchmod.alwaysmatcher,
1219 1219 matchmod.exactmatcher,
1220 1220 matchmod.includematcher,
1221 1221 )
1222 1222
1223 1223 if rustmod is None:
1224 1224 use_rust = False
1225 1225 elif self._checkcase:
1226 1226 # Case-insensitive filesystems are not handled yet
1227 1227 use_rust = False
1228 1228 elif subrepos:
1229 1229 use_rust = False
1230 1230 elif sparse.enabled:
1231 1231 use_rust = False
1232 1232 elif not isinstance(match, allowed_matchers):
1233 1233 # Some matchers have yet to be implemented
1234 1234 use_rust = False
1235 1235
1236 1236 if use_rust:
1237 1237 try:
1238 1238 return self._rust_status(
1239 1239 match, listclean, listignored, listunknown
1240 1240 )
1241 1241 except rustmod.FallbackError:
1242 1242 pass
1243 1243
1244 1244 def noop(f):
1245 1245 pass
1246 1246
1247 1247 dcontains = dmap.__contains__
1248 1248 dget = dmap.__getitem__
1249 1249 ladd = lookup.append # aka "unsure"
1250 1250 madd = modified.append
1251 1251 aadd = added.append
1252 1252 uadd = unknown.append if listunknown else noop
1253 1253 iadd = ignored.append if listignored else noop
1254 1254 radd = removed.append
1255 1255 dadd = deleted.append
1256 1256 cadd = clean.append if listclean else noop
1257 1257 mexact = match.exact
1258 1258 dirignore = self._dirignore
1259 1259 checkexec = self._checkexec
1260 1260 copymap = self._map.copymap
1261 1261 lastnormaltime = self._lastnormaltime
1262 1262
1263 1263 # We need to do full walks when either
1264 1264 # - we're listing all clean files, or
1265 1265 # - match.traversedir does something, because match.traversedir should
1266 1266 # be called for every dir in the working dir
1267 1267 full = listclean or match.traversedir is not None
1268 1268 for fn, st in pycompat.iteritems(
1269 1269 self.walk(match, subrepos, listunknown, listignored, full=full)
1270 1270 ):
1271 1271 if not dcontains(fn):
1272 1272 if (listignored or mexact(fn)) and dirignore(fn):
1273 1273 if listignored:
1274 1274 iadd(fn)
1275 1275 else:
1276 1276 uadd(fn)
1277 1277 continue
1278 1278
1279 1279 # This is equivalent to 'state, mode, size, time = dmap[fn]' but not
1280 1280 # written like that for performance reasons. dmap[fn] is not a
1281 1281 # Python tuple in compiled builds. The CPython UNPACK_SEQUENCE
1282 1282 # opcode has fast paths when the value to be unpacked is a tuple or
1283 1283 # a list, but falls back to creating a full-fledged iterator in
1284 1284 # general. That is much slower than simply accessing and storing the
1285 1285 # tuple members one by one.
1286 1286 t = dget(fn)
1287 1287 state = t[0]
1288 1288 mode = t[1]
1289 1289 size = t[2]
1290 1290 time = t[3]
1291 1291
1292 1292 if not st and state in b"nma":
1293 1293 dadd(fn)
1294 1294 elif state == b'n':
1295 1295 if (
1296 1296 size >= 0
1297 1297 and (
1298 1298 (size != st.st_size and size != st.st_size & _rangemask)
1299 1299 or ((mode ^ st.st_mode) & 0o100 and checkexec)
1300 1300 )
1301 1301 or size == -2 # other parent
1302 1302 or fn in copymap
1303 1303 ):
1304 1304 if stat.S_ISLNK(st.st_mode) and size != st.st_size:
1305 1305 # issue6456: Size returned may be longer due to
1306 1306 # encryption on EXT-4 fscrypt, undecided.
1307 1307 ladd(fn)
1308 1308 else:
1309 1309 madd(fn)
1310 1310 elif (
1311 1311 time != st[stat.ST_MTIME]
1312 1312 and time != st[stat.ST_MTIME] & _rangemask
1313 1313 ):
1314 1314 ladd(fn)
1315 1315 elif st[stat.ST_MTIME] == lastnormaltime:
1316 1316 # fn may have just been marked as normal and it may have
1317 1317 # changed in the same second without changing its size.
1318 1318 # This can happen if we quickly do multiple commits.
1319 1319 # Force lookup, so we don't miss such a racy file change.
1320 1320 ladd(fn)
1321 1321 elif listclean:
1322 1322 cadd(fn)
1323 1323 elif state == b'm':
1324 1324 madd(fn)
1325 1325 elif state == b'a':
1326 1326 aadd(fn)
1327 1327 elif state == b'r':
1328 1328 radd(fn)
1329 1329 status = scmutil.status(
1330 1330 modified, added, removed, deleted, unknown, ignored, clean
1331 1331 )
1332 1332 return (lookup, status)
1333 1333
1334 1334 def matches(self, match):
1335 1335 """
1336 1336 return files in the dirstate (in whatever state) filtered by match
1337 1337 """
1338 1338 dmap = self._map
1339 1339 if rustmod is not None:
1340 1340 dmap = self._map._rustmap
1341 1341
1342 1342 if match.always():
1343 1343 return dmap.keys()
1344 1344 files = match.files()
1345 1345 if match.isexact():
1346 1346 # fast path -- filter the other way around, since typically files is
1347 1347 # much smaller than dmap
1348 1348 return [f for f in files if f in dmap]
1349 1349 if match.prefix() and all(fn in dmap for fn in files):
1350 1350 # fast path -- all the values are known to be files, so just return
1351 1351 # that
1352 1352 return list(files)
1353 1353 return [f for f in dmap if match(f)]
1354 1354
1355 1355 def _actualfilename(self, tr):
1356 1356 if tr:
1357 1357 return self._pendingfilename
1358 1358 else:
1359 1359 return self._filename
1360 1360
1361 1361 def savebackup(self, tr, backupname):
1362 1362 '''Save current dirstate into backup file'''
1363 1363 filename = self._actualfilename(tr)
1364 1364 assert backupname != filename
1365 1365
1366 1366 # use '_writedirstate' instead of 'write' to write changes certainly,
1367 1367 # because the latter omits writing out if transaction is running.
1368 1368 # output file will be used to create backup of dirstate at this point.
1369 1369 if self._dirty or not self._opener.exists(filename):
1370 1370 self._writedirstate(
1371 1371 self._opener(filename, b"w", atomictemp=True, checkambig=True)
1372 1372 )
1373 1373
1374 1374 if tr:
1375 1375 # ensure that subsequent tr.writepending returns True for
1376 1376 # changes written out above, even if dirstate is never
1377 1377 # changed after this
1378 1378 tr.addfilegenerator(
1379 1379 b'dirstate',
1380 1380 (self._filename,),
1381 1381 self._writedirstate,
1382 1382 location=b'plain',
1383 1383 )
1384 1384
1385 1385 # ensure that pending file written above is unlinked at
1386 1386 # failure, even if tr.writepending isn't invoked until the
1387 1387 # end of this transaction
1388 1388 tr.registertmp(filename, location=b'plain')
1389 1389
1390 1390 self._opener.tryunlink(backupname)
1391 1391 # hardlink backup is okay because _writedirstate is always called
1392 1392 # with an "atomictemp=True" file.
1393 1393 util.copyfile(
1394 1394 self._opener.join(filename),
1395 1395 self._opener.join(backupname),
1396 1396 hardlink=True,
1397 1397 )
1398 1398
1399 1399 def restorebackup(self, tr, backupname):
1400 1400 '''Restore dirstate by backup file'''
1401 1401 # this "invalidate()" prevents "wlock.release()" from writing
1402 1402 # changes of dirstate out after restoring from backup file
1403 1403 self.invalidate()
1404 1404 filename = self._actualfilename(tr)
1405 1405 o = self._opener
1406 1406 if util.samefile(o.join(backupname), o.join(filename)):
1407 1407 o.unlink(backupname)
1408 1408 else:
1409 1409 o.rename(backupname, filename, checkambig=True)
1410 1410
1411 1411 def clearbackup(self, tr, backupname):
1412 1412 '''Clear backup file'''
1413 1413 self._opener.unlink(backupname)
1414 1414
1415 1415
1416 1416 class dirstatemap(object):
1417 1417 """Map encapsulating the dirstate's contents.
1418 1418
1419 1419 The dirstate contains the following state:
1420 1420
1421 1421 - `identity` is the identity of the dirstate file, which can be used to
1422 1422 detect when changes have occurred to the dirstate file.
1423 1423
1424 1424 - `parents` is a pair containing the parents of the working copy. The
1425 1425 parents are updated by calling `setparents`.
1426 1426
1427 1427 - the state map maps filenames to tuples of (state, mode, size, mtime),
1428 1428 where state is a single character representing 'normal', 'added',
1429 1429 'removed', or 'merged'. It is read by treating the dirstate as a
1430 1430 dict. File state is updated by calling the `addfile`, `removefile` and
1431 1431 `dropfile` methods.
1432 1432
1433 1433 - `copymap` maps destination filenames to their source filename.
1434 1434
1435 1435 The dirstate also provides the following views onto the state:
1436 1436
1437 1437 - `nonnormalset` is a set of the filenames that have state other
1438 1438 than 'normal', or are normal but have an mtime of -1 ('normallookup').
1439 1439
1440 1440 - `otherparentset` is a set of the filenames that are marked as coming
1441 1441 from the second parent when the dirstate is currently being merged.
1442 1442
1443 1443 - `filefoldmap` is a dict mapping normalized filenames to the denormalized
1444 1444 form that they appear as in the dirstate.
1445 1445
1446 1446 - `dirfoldmap` is a dict mapping normalized directory names to the
1447 1447 denormalized form that they appear as in the dirstate.
1448 1448 """
1449 1449
1450 1450 def __init__(self, ui, opener, root, nodeconstants, use_dirstate_v2):
1451 1451 self._ui = ui
1452 1452 self._opener = opener
1453 1453 self._root = root
1454 1454 self._filename = b'dirstate'
1455 1455 self._nodelen = 20
1456 1456 self._nodeconstants = nodeconstants
1457 1457 assert (
1458 1458 not use_dirstate_v2
1459 1459 ), "should have detected unsupported requirement"
1460 1460
1461 1461 self._parents = None
1462 1462 self._dirtyparents = False
1463 1463
1464 1464 # for consistent view between _pl() and _read() invocations
1465 1465 self._pendingmode = None
1466 1466
1467 1467 @propertycache
1468 1468 def _map(self):
1469 1469 self._map = {}
1470 1470 self.read()
1471 1471 return self._map
1472 1472
1473 1473 @propertycache
1474 1474 def copymap(self):
1475 1475 self.copymap = {}
1476 1476 self._map
1477 1477 return self.copymap
1478 1478
1479 1479 def clear(self):
1480 1480 self._map.clear()
1481 1481 self.copymap.clear()
1482 1482 self.setparents(self._nodeconstants.nullid, self._nodeconstants.nullid)
1483 1483 util.clearcachedproperty(self, b"_dirs")
1484 1484 util.clearcachedproperty(self, b"_alldirs")
1485 1485 util.clearcachedproperty(self, b"filefoldmap")
1486 1486 util.clearcachedproperty(self, b"dirfoldmap")
1487 1487 util.clearcachedproperty(self, b"nonnormalset")
1488 1488 util.clearcachedproperty(self, b"otherparentset")
1489 1489
1490 1490 def items(self):
1491 1491 return pycompat.iteritems(self._map)
1492 1492
1493 1493 # forward for python2,3 compat
1494 1494 iteritems = items
1495 1495
1496 1496 def __len__(self):
1497 1497 return len(self._map)
1498 1498
1499 1499 def __iter__(self):
1500 1500 return iter(self._map)
1501 1501
1502 1502 def get(self, key, default=None):
1503 1503 return self._map.get(key, default)
1504 1504
1505 1505 def __contains__(self, key):
1506 1506 return key in self._map
1507 1507
1508 1508 def __getitem__(self, key):
1509 1509 return self._map[key]
1510 1510
1511 1511 def keys(self):
1512 1512 return self._map.keys()
1513 1513
1514 1514 def preload(self):
1515 1515 """Loads the underlying data, if it's not already loaded"""
1516 1516 self._map
1517 1517
1518 1518 def addfile(self, f, oldstate, state, mode, size, mtime):
1519 1519 """Add a tracked file to the dirstate."""
1520 1520 if oldstate in b"?r" and "_dirs" in self.__dict__:
1521 1521 self._dirs.addpath(f)
1522 1522 if oldstate == b"?" and "_alldirs" in self.__dict__:
1523 1523 self._alldirs.addpath(f)
1524 1524 self._map[f] = dirstatetuple(state, mode, size, mtime)
1525 1525 if state != b'n' or mtime == -1:
1526 1526 self.nonnormalset.add(f)
1527 1527 if size == -2:
1528 1528 self.otherparentset.add(f)
1529 1529
1530 1530 def removefile(self, f, oldstate, size):
1531 1531 """
1532 1532 Mark a file as removed in the dirstate.
1533 1533
1534 1534 The `size` parameter is used to store sentinel values that indicate
1535 1535 the file's previous state. In the future, we should refactor this
1536 1536 to be more explicit about what that state is.
1537 1537 """
1538 1538 if oldstate not in b"?r" and "_dirs" in self.__dict__:
1539 1539 self._dirs.delpath(f)
1540 1540 if oldstate == b"?" and "_alldirs" in self.__dict__:
1541 1541 self._alldirs.addpath(f)
1542 1542 if "filefoldmap" in self.__dict__:
1543 1543 normed = util.normcase(f)
1544 1544 self.filefoldmap.pop(normed, None)
1545 1545 self._map[f] = dirstatetuple(b'r', 0, size, 0)
1546 1546 self.nonnormalset.add(f)
1547 1547
1548 1548 def dropfile(self, f, oldstate):
1549 1549 """
1550 1550 Remove a file from the dirstate. Returns True if the file was
1551 1551 previously recorded.
1552 1552 """
1553 1553 exists = self._map.pop(f, None) is not None
1554 1554 if exists:
1555 1555 if oldstate != b"r" and "_dirs" in self.__dict__:
1556 1556 self._dirs.delpath(f)
1557 1557 if "_alldirs" in self.__dict__:
1558 1558 self._alldirs.delpath(f)
1559 1559 if "filefoldmap" in self.__dict__:
1560 1560 normed = util.normcase(f)
1561 1561 self.filefoldmap.pop(normed, None)
1562 1562 self.nonnormalset.discard(f)
1563 1563 return exists
1564 1564
1565 1565 def clearambiguoustimes(self, files, now):
1566 1566 for f in files:
1567 1567 e = self.get(f)
1568 1568 if e is not None and e[0] == b'n' and e[3] == now:
1569 1569 self._map[f] = dirstatetuple(e[0], e[1], e[2], -1)
1570 1570 self.nonnormalset.add(f)
1571 1571
1572 1572 def nonnormalentries(self):
1573 1573 '''Compute the nonnormal dirstate entries from the dmap'''
1574 1574 try:
1575 1575 return parsers.nonnormalotherparententries(self._map)
1576 1576 except AttributeError:
1577 1577 nonnorm = set()
1578 1578 otherparent = set()
1579 1579 for fname, e in pycompat.iteritems(self._map):
1580 1580 if e[0] != b'n' or e[3] == -1:
1581 1581 nonnorm.add(fname)
1582 1582 if e[0] == b'n' and e[2] == -2:
1583 1583 otherparent.add(fname)
1584 1584 return nonnorm, otherparent
1585 1585
1586 1586 @propertycache
1587 1587 def filefoldmap(self):
1588 1588 """Returns a dictionary mapping normalized case paths to their
1589 1589 non-normalized versions.
1590 1590 """
1591 1591 try:
1592 1592 makefilefoldmap = parsers.make_file_foldmap
1593 1593 except AttributeError:
1594 1594 pass
1595 1595 else:
1596 1596 return makefilefoldmap(
1597 1597 self._map, util.normcasespec, util.normcasefallback
1598 1598 )
1599 1599
1600 1600 f = {}
1601 1601 normcase = util.normcase
1602 1602 for name, s in pycompat.iteritems(self._map):
1603 1603 if s[0] != b'r':
1604 1604 f[normcase(name)] = name
1605 1605 f[b'.'] = b'.' # prevents useless util.fspath() invocation
1606 1606 return f
1607 1607
1608 1608 def hastrackeddir(self, d):
1609 1609 """
1610 1610 Returns True if the dirstate contains a tracked (not removed) file
1611 1611 in this directory.
1612 1612 """
1613 1613 return d in self._dirs
1614 1614
1615 1615 def hasdir(self, d):
1616 1616 """
1617 1617 Returns True if the dirstate contains a file (tracked or removed)
1618 1618 in this directory.
1619 1619 """
1620 1620 return d in self._alldirs
1621 1621
1622 1622 @propertycache
1623 1623 def _dirs(self):
1624 1624 return pathutil.dirs(self._map, b'r')
1625 1625
1626 1626 @propertycache
1627 1627 def _alldirs(self):
1628 1628 return pathutil.dirs(self._map)
1629 1629
1630 1630 def _opendirstatefile(self):
1631 1631 fp, mode = txnutil.trypending(self._root, self._opener, self._filename)
1632 1632 if self._pendingmode is not None and self._pendingmode != mode:
1633 1633 fp.close()
1634 1634 raise error.Abort(
1635 1635 _(b'working directory state may be changed parallelly')
1636 1636 )
1637 1637 self._pendingmode = mode
1638 1638 return fp
1639 1639
1640 1640 def parents(self):
1641 1641 if not self._parents:
1642 1642 try:
1643 1643 fp = self._opendirstatefile()
1644 1644 st = fp.read(2 * self._nodelen)
1645 1645 fp.close()
1646 1646 except IOError as err:
1647 1647 if err.errno != errno.ENOENT:
1648 1648 raise
1649 1649 # File doesn't exist, so the current state is empty
1650 1650 st = b''
1651 1651
1652 1652 l = len(st)
1653 1653 if l == self._nodelen * 2:
1654 1654 self._parents = (
1655 1655 st[: self._nodelen],
1656 1656 st[self._nodelen : 2 * self._nodelen],
1657 1657 )
1658 1658 elif l == 0:
1659 1659 self._parents = (
1660 1660 self._nodeconstants.nullid,
1661 1661 self._nodeconstants.nullid,
1662 1662 )
1663 1663 else:
1664 1664 raise error.Abort(
1665 1665 _(b'working directory state appears damaged!')
1666 1666 )
1667 1667
1668 1668 return self._parents
1669 1669
1670 1670 def setparents(self, p1, p2):
1671 1671 self._parents = (p1, p2)
1672 1672 self._dirtyparents = True
1673 1673
1674 1674 def read(self):
1675 1675 # ignore HG_PENDING because identity is used only for writing
1676 1676 self.identity = util.filestat.frompath(
1677 1677 self._opener.join(self._filename)
1678 1678 )
1679 1679
1680 1680 try:
1681 1681 fp = self._opendirstatefile()
1682 1682 try:
1683 1683 st = fp.read()
1684 1684 finally:
1685 1685 fp.close()
1686 1686 except IOError as err:
1687 1687 if err.errno != errno.ENOENT:
1688 1688 raise
1689 1689 return
1690 1690 if not st:
1691 1691 return
1692 1692
1693 1693 if util.safehasattr(parsers, b'dict_new_presized'):
1694 1694 # Make an estimate of the number of files in the dirstate based on
1695 1695 # its size. This trades wasting some memory for avoiding costly
1696 1696 # resizes. Each entry have a prefix of 17 bytes followed by one or
1697 1697 # two path names. Studies on various large-scale real-world repositories
1698 1698 # found 54 bytes a reasonable upper limit for the average path names.
1699 1699 # Copy entries are ignored for the sake of this estimate.
1700 1700 self._map = parsers.dict_new_presized(len(st) // 71)
1701 1701
1702 1702 # Python's garbage collector triggers a GC each time a certain number
1703 1703 # of container objects (the number being defined by
1704 1704 # gc.get_threshold()) are allocated. parse_dirstate creates a tuple
1705 1705 # for each file in the dirstate. The C version then immediately marks
1706 1706 # them as not to be tracked by the collector. However, this has no
1707 1707 # effect on when GCs are triggered, only on what objects the GC looks
1708 1708 # into. This means that O(number of files) GCs are unavoidable.
1709 1709 # Depending on when in the process's lifetime the dirstate is parsed,
1710 1710 # this can get very expensive. As a workaround, disable GC while
1711 1711 # parsing the dirstate.
1712 1712 #
1713 1713 # (we cannot decorate the function directly since it is in a C module)
1714 1714 parse_dirstate = util.nogc(parsers.parse_dirstate)
1715 1715 p = parse_dirstate(self._map, self.copymap, st)
1716 1716 if not self._dirtyparents:
1717 1717 self.setparents(*p)
1718 1718
1719 1719 # Avoid excess attribute lookups by fast pathing certain checks
1720 1720 self.__contains__ = self._map.__contains__
1721 1721 self.__getitem__ = self._map.__getitem__
1722 1722 self.get = self._map.get
1723 1723
1724 1724 def write(self, st, now):
1725 1725 st.write(
1726 1726 parsers.pack_dirstate(self._map, self.copymap, self.parents(), now)
1727 1727 )
1728 1728 st.close()
1729 1729 self._dirtyparents = False
1730 1730 self.nonnormalset, self.otherparentset = self.nonnormalentries()
1731 1731
1732 1732 @propertycache
1733 1733 def nonnormalset(self):
1734 1734 nonnorm, otherparents = self.nonnormalentries()
1735 1735 self.otherparentset = otherparents
1736 1736 return nonnorm
1737 1737
1738 1738 @propertycache
1739 1739 def otherparentset(self):
1740 1740 nonnorm, otherparents = self.nonnormalentries()
1741 1741 self.nonnormalset = nonnorm
1742 1742 return otherparents
1743 1743
1744 1744 def non_normal_or_other_parent_paths(self):
1745 1745 return self.nonnormalset.union(self.otherparentset)
1746 1746
1747 1747 @propertycache
1748 1748 def identity(self):
1749 1749 self._map
1750 1750 return self.identity
1751 1751
1752 1752 @propertycache
1753 1753 def dirfoldmap(self):
1754 1754 f = {}
1755 1755 normcase = util.normcase
1756 1756 for name in self._dirs:
1757 1757 f[normcase(name)] = name
1758 1758 return f
1759 1759
1760 1760
1761 1761 if rustmod is not None:
1762 1762
1763 1763 class dirstatemap(object):
1764 1764 def __init__(self, ui, opener, root, nodeconstants, use_dirstate_v2):
1765 1765 self._use_dirstate_v2 = use_dirstate_v2
1766 1766 self._nodeconstants = nodeconstants
1767 1767 self._ui = ui
1768 1768 self._opener = opener
1769 1769 self._root = root
1770 1770 self._filename = b'dirstate'
1771 1771 self._nodelen = 20 # Also update Rust code when changing this!
1772 1772 self._parents = None
1773 1773 self._dirtyparents = False
1774 1774
1775 1775 # for consistent view between _pl() and _read() invocations
1776 1776 self._pendingmode = None
1777 1777
1778 self._use_dirstate_tree = self._ui.configbool(
1779 b"experimental",
1780 b"dirstate-tree.in-memory",
1781 False,
1782 )
1783
1778 1784 def addfile(self, *args, **kwargs):
1779 1785 return self._rustmap.addfile(*args, **kwargs)
1780 1786
1781 1787 def removefile(self, *args, **kwargs):
1782 1788 return self._rustmap.removefile(*args, **kwargs)
1783 1789
1784 1790 def dropfile(self, *args, **kwargs):
1785 1791 return self._rustmap.dropfile(*args, **kwargs)
1786 1792
1787 1793 def clearambiguoustimes(self, *args, **kwargs):
1788 1794 return self._rustmap.clearambiguoustimes(*args, **kwargs)
1789 1795
1790 1796 def nonnormalentries(self):
1791 1797 return self._rustmap.nonnormalentries()
1792 1798
1793 1799 def get(self, *args, **kwargs):
1794 1800 return self._rustmap.get(*args, **kwargs)
1795 1801
1796 1802 @property
1797 1803 def copymap(self):
1798 1804 return self._rustmap.copymap()
1799 1805
1800 1806 def preload(self):
1801 1807 self._rustmap
1802 1808
1803 1809 def clear(self):
1804 1810 self._rustmap.clear()
1805 1811 self.setparents(
1806 1812 self._nodeconstants.nullid, self._nodeconstants.nullid
1807 1813 )
1808 1814 util.clearcachedproperty(self, b"_dirs")
1809 1815 util.clearcachedproperty(self, b"_alldirs")
1810 1816 util.clearcachedproperty(self, b"dirfoldmap")
1811 1817
1812 1818 def items(self):
1813 1819 return self._rustmap.items()
1814 1820
1815 1821 def keys(self):
1816 1822 return iter(self._rustmap)
1817 1823
1818 1824 def __contains__(self, key):
1819 1825 return key in self._rustmap
1820 1826
1821 1827 def __getitem__(self, item):
1822 1828 return self._rustmap[item]
1823 1829
1824 1830 def __len__(self):
1825 1831 return len(self._rustmap)
1826 1832
1827 1833 def __iter__(self):
1828 1834 return iter(self._rustmap)
1829 1835
1830 1836 # forward for python2,3 compat
1831 1837 iteritems = items
1832 1838
1833 1839 def _opendirstatefile(self):
1834 1840 fp, mode = txnutil.trypending(
1835 1841 self._root, self._opener, self._filename
1836 1842 )
1837 1843 if self._pendingmode is not None and self._pendingmode != mode:
1838 1844 fp.close()
1839 1845 raise error.Abort(
1840 1846 _(b'working directory state may be changed parallelly')
1841 1847 )
1842 1848 self._pendingmode = mode
1843 1849 return fp
1844 1850
1845 1851 def setparents(self, p1, p2):
1846 1852 self._parents = (p1, p2)
1847 1853 self._dirtyparents = True
1848 1854
1849 1855 def parents(self):
1850 1856 if not self._parents:
1851 1857 if self._use_dirstate_v2:
1852 1858 offset = len(rustmod.V2_FORMAT_MARKER)
1853 1859 else:
1854 1860 offset = 0
1855 1861 read_len = offset + self._nodelen * 2
1856 1862 try:
1857 1863 fp = self._opendirstatefile()
1858 1864 st = fp.read(read_len)
1859 1865 fp.close()
1860 1866 except IOError as err:
1861 1867 if err.errno != errno.ENOENT:
1862 1868 raise
1863 1869 # File doesn't exist, so the current state is empty
1864 1870 st = b''
1865 1871
1866 1872 l = len(st)
1867 1873 if l == read_len:
1868 1874 st = st[offset:]
1869 1875 self._parents = (
1870 1876 st[: self._nodelen],
1871 1877 st[self._nodelen : 2 * self._nodelen],
1872 1878 )
1873 1879 elif l == 0:
1874 1880 self._parents = (
1875 1881 self._nodeconstants.nullid,
1876 1882 self._nodeconstants.nullid,
1877 1883 )
1878 1884 else:
1879 1885 raise error.Abort(
1880 1886 _(b'working directory state appears damaged!')
1881 1887 )
1882 1888
1883 1889 return self._parents
1884 1890
1885 1891 @propertycache
1886 1892 def _rustmap(self):
1887 1893 """
1888 1894 Fills the Dirstatemap when called.
1889 1895 """
1890 1896 # ignore HG_PENDING because identity is used only for writing
1891 1897 self.identity = util.filestat.frompath(
1892 1898 self._opener.join(self._filename)
1893 1899 )
1894 1900
1895 1901 try:
1896 1902 fp = self._opendirstatefile()
1897 1903 try:
1898 1904 st = fp.read()
1899 1905 finally:
1900 1906 fp.close()
1901 1907 except IOError as err:
1902 1908 if err.errno != errno.ENOENT:
1903 1909 raise
1904 1910 st = b''
1905 1911
1906 use_dirstate_tree = self._ui.configbool(
1907 b"experimental",
1908 b"dirstate-tree.in-memory",
1909 False,
1910 )
1911 1912 self._rustmap, parents = rustmod.DirstateMap.new(
1912 use_dirstate_tree, self._use_dirstate_v2, st
1913 self._use_dirstate_tree, self._use_dirstate_v2, st
1913 1914 )
1914 1915
1915 1916 if parents and not self._dirtyparents:
1916 1917 self.setparents(*parents)
1917 1918
1918 1919 self.__contains__ = self._rustmap.__contains__
1919 1920 self.__getitem__ = self._rustmap.__getitem__
1920 1921 self.get = self._rustmap.get
1921 1922 return self._rustmap
1922 1923
1923 1924 def write(self, st, now):
1924 1925 parents = self.parents()
1925 1926 packed = self._rustmap.write(
1926 1927 self._use_dirstate_v2, parents[0], parents[1], now
1927 1928 )
1928 1929 st.write(packed)
1929 1930 st.close()
1930 1931 self._dirtyparents = False
1931 1932
1932 1933 @propertycache
1933 1934 def filefoldmap(self):
1934 1935 """Returns a dictionary mapping normalized case paths to their
1935 1936 non-normalized versions.
1936 1937 """
1937 1938 return self._rustmap.filefoldmapasdict()
1938 1939
1939 1940 def hastrackeddir(self, d):
1940 1941 self._dirs # Trigger Python's propertycache
1941 1942 return self._rustmap.hastrackeddir(d)
1942 1943
1943 1944 def hasdir(self, d):
1944 1945 self._dirs # Trigger Python's propertycache
1945 1946 return self._rustmap.hasdir(d)
1946 1947
1947 1948 @propertycache
1948 1949 def _dirs(self):
1949 1950 return self._rustmap.getdirs()
1950 1951
1951 1952 @propertycache
1952 1953 def _alldirs(self):
1953 1954 return self._rustmap.getalldirs()
1954 1955
1955 1956 @propertycache
1956 1957 def identity(self):
1957 1958 self._rustmap
1958 1959 return self.identity
1959 1960
1960 1961 @property
1961 1962 def nonnormalset(self):
1962 1963 nonnorm = self._rustmap.non_normal_entries()
1963 1964 return nonnorm
1964 1965
1965 1966 @propertycache
1966 1967 def otherparentset(self):
1967 1968 otherparents = self._rustmap.other_parent_entries()
1968 1969 return otherparents
1969 1970
1970 1971 def non_normal_or_other_parent_paths(self):
1971 1972 return self._rustmap.non_normal_or_other_parent_paths()
1972 1973
1973 1974 @propertycache
1974 1975 def dirfoldmap(self):
1975 1976 f = {}
1976 1977 normcase = util.normcase
1977 1978 for name in self._dirs:
1978 1979 f[normcase(name)] = name
1979 1980 return f
@@ -1,1008 +1,1049
1 1 # upgrade.py - functions for in place upgrade of Mercurial repository
2 2 #
3 3 # Copyright (c) 2016-present, Gregory Szorc
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.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 from ..i18n import _
11 11 from .. import (
12 12 error,
13 13 localrepo,
14 14 pycompat,
15 15 requirements,
16 16 revlog,
17 17 util,
18 18 )
19 19
20 20 from ..utils import compression
21 21
22 22 if pycompat.TYPE_CHECKING:
23 23 from typing import (
24 24 List,
25 25 Type,
26 26 )
27 27
28 28
29 29 # list of requirements that request a clone of all revlog if added/removed
30 30 RECLONES_REQUIREMENTS = {
31 31 requirements.GENERALDELTA_REQUIREMENT,
32 32 requirements.SPARSEREVLOG_REQUIREMENT,
33 33 requirements.REVLOGV2_REQUIREMENT,
34 34 requirements.CHANGELOGV2_REQUIREMENT,
35 35 }
36 36
37 37
38 38 def preservedrequirements(repo):
39 39 return set()
40 40
41 41
42 42 FORMAT_VARIANT = b'deficiency'
43 43 OPTIMISATION = b'optimization'
44 44
45 45
46 46 class improvement(object):
47 47 """Represents an improvement that can be made as part of an upgrade."""
48 48
49 49 ### The following attributes should be defined for each subclass:
50 50
51 51 # Either ``FORMAT_VARIANT`` or ``OPTIMISATION``.
52 52 # A format variant is where we change the storage format. Not all format
53 53 # variant changes are an obvious problem.
54 54 # An optimization is an action (sometimes optional) that
55 55 # can be taken to further improve the state of the repository.
56 56 type = None
57 57
58 58 # machine-readable string uniquely identifying this improvement. it will be
59 59 # mapped to an action later in the upgrade process.
60 60 name = None
61 61
62 62 # message intended for humans explaining the improvement in more detail,
63 63 # including the implications of it ``FORMAT_VARIANT`` types, should be
64 64 # worded
65 65 # in the present tense.
66 66 description = None
67 67
68 68 # message intended for humans explaining what an upgrade addressing this
69 69 # issue will do. should be worded in the future tense.
70 70 upgrademessage = None
71 71
72 72 # value of current Mercurial default for new repository
73 73 default = None
74 74
75 75 # Message intended for humans which will be shown post an upgrade
76 76 # operation when the improvement will be added
77 77 postupgrademessage = None
78 78
79 79 # Message intended for humans which will be shown post an upgrade
80 80 # operation in which this improvement was removed
81 81 postdowngrademessage = None
82 82
83 # By default for now, we assume every improvement touches all the things
83 # By default we assume that every improvement touches requirements and all revlogs
84 84
85 85 # Whether this improvement touches filelogs
86 86 touches_filelogs = True
87 87
88 88 # Whether this improvement touches manifests
89 89 touches_manifests = True
90 90
91 91 # Whether this improvement touches changelog
92 92 touches_changelog = True
93 93
94 94 # Whether this improvement changes repository requirements
95 95 touches_requirements = True
96 96
97 # Whether this improvement touches the dirstate
98 touches_dirstate = False
99
97 100
98 101 allformatvariant = [] # type: List[Type['formatvariant']]
99 102
100 103
101 104 def registerformatvariant(cls):
102 105 allformatvariant.append(cls)
103 106 return cls
104 107
105 108
106 109 class formatvariant(improvement):
107 110 """an improvement subclass dedicated to repository format"""
108 111
109 112 type = FORMAT_VARIANT
110 113
111 114 @staticmethod
112 115 def fromrepo(repo):
113 116 """current value of the variant in the repository"""
114 117 raise NotImplementedError()
115 118
116 119 @staticmethod
117 120 def fromconfig(repo):
118 121 """current value of the variant in the configuration"""
119 122 raise NotImplementedError()
120 123
121 124
122 125 class requirementformatvariant(formatvariant):
123 126 """formatvariant based on a 'requirement' name.
124 127
125 128 Many format variant are controlled by a 'requirement'. We define a small
126 129 subclass to factor the code.
127 130 """
128 131
129 132 # the requirement that control this format variant
130 133 _requirement = None
131 134
132 135 @staticmethod
133 136 def _newreporequirements(ui):
134 137 return localrepo.newreporequirements(
135 138 ui, localrepo.defaultcreateopts(ui)
136 139 )
137 140
138 141 @classmethod
139 142 def fromrepo(cls, repo):
140 143 assert cls._requirement is not None
141 144 return cls._requirement in repo.requirements
142 145
143 146 @classmethod
144 147 def fromconfig(cls, repo):
145 148 assert cls._requirement is not None
146 149 return cls._requirement in cls._newreporequirements(repo.ui)
147 150
148 151
149 152 @registerformatvariant
150 153 class fncache(requirementformatvariant):
151 154 name = b'fncache'
152 155
153 156 _requirement = requirements.FNCACHE_REQUIREMENT
154 157
155 158 default = True
156 159
157 160 description = _(
158 161 b'long and reserved filenames may not work correctly; '
159 162 b'repository performance is sub-optimal'
160 163 )
161 164
162 165 upgrademessage = _(
163 166 b'repository will be more resilient to storing '
164 167 b'certain paths and performance of certain '
165 168 b'operations should be improved'
166 169 )
167 170
168 171
169 172 @registerformatvariant
173 class dirstatev2(requirementformatvariant):
174 name = b'dirstate-v2'
175 _requirement = requirements.DIRSTATE_V2_REQUIREMENT
176
177 default = False
178
179 description = _(
180 b'version 1 of the dirstate file format requires '
181 b'reading and parsing it all at once.'
182 )
183
184 upgrademessage = _(b'"hg status" will be faster')
185
186 touches_filelogs = False
187 touches_manifests = False
188 touches_changelog = False
189 touches_requirements = True
190 touches_dirstate = True
191
192
193 @registerformatvariant
170 194 class dotencode(requirementformatvariant):
171 195 name = b'dotencode'
172 196
173 197 _requirement = requirements.DOTENCODE_REQUIREMENT
174 198
175 199 default = True
176 200
177 201 description = _(
178 202 b'storage of filenames beginning with a period or '
179 203 b'space may not work correctly'
180 204 )
181 205
182 206 upgrademessage = _(
183 207 b'repository will be better able to store files '
184 208 b'beginning with a space or period'
185 209 )
186 210
187 211
188 212 @registerformatvariant
189 213 class generaldelta(requirementformatvariant):
190 214 name = b'generaldelta'
191 215
192 216 _requirement = requirements.GENERALDELTA_REQUIREMENT
193 217
194 218 default = True
195 219
196 220 description = _(
197 221 b'deltas within internal storage are unable to '
198 222 b'choose optimal revisions; repository is larger and '
199 223 b'slower than it could be; interaction with other '
200 224 b'repositories may require extra network and CPU '
201 225 b'resources, making "hg push" and "hg pull" slower'
202 226 )
203 227
204 228 upgrademessage = _(
205 229 b'repository storage will be able to create '
206 230 b'optimal deltas; new repository data will be '
207 231 b'smaller and read times should decrease; '
208 232 b'interacting with other repositories using this '
209 233 b'storage model should require less network and '
210 234 b'CPU resources, making "hg push" and "hg pull" '
211 235 b'faster'
212 236 )
213 237
214 238
215 239 @registerformatvariant
216 240 class sharesafe(requirementformatvariant):
217 241 name = b'share-safe'
218 242 _requirement = requirements.SHARESAFE_REQUIREMENT
219 243
220 244 default = False
221 245
222 246 description = _(
223 247 b'old shared repositories do not share source repository '
224 248 b'requirements and config. This leads to various problems '
225 249 b'when the source repository format is upgraded or some new '
226 250 b'extensions are enabled.'
227 251 )
228 252
229 253 upgrademessage = _(
230 254 b'Upgrades a repository to share-safe format so that future '
231 255 b'shares of this repository share its requirements and configs.'
232 256 )
233 257
234 258 postdowngrademessage = _(
235 259 b'repository downgraded to not use share safe mode, '
236 260 b'existing shares will not work and needs to'
237 261 b' be reshared.'
238 262 )
239 263
240 264 postupgrademessage = _(
241 265 b'repository upgraded to share safe mode, existing'
242 266 b' shares will still work in old non-safe mode. '
243 267 b'Re-share existing shares to use them in safe mode'
244 268 b' New shares will be created in safe mode.'
245 269 )
246 270
247 271 # upgrade only needs to change the requirements
248 272 touches_filelogs = False
249 273 touches_manifests = False
250 274 touches_changelog = False
251 275 touches_requirements = True
252 276
253 277
254 278 @registerformatvariant
255 279 class sparserevlog(requirementformatvariant):
256 280 name = b'sparserevlog'
257 281
258 282 _requirement = requirements.SPARSEREVLOG_REQUIREMENT
259 283
260 284 default = True
261 285
262 286 description = _(
263 287 b'in order to limit disk reading and memory usage on older '
264 288 b'version, the span of a delta chain from its root to its '
265 289 b'end is limited, whatever the relevant data in this span. '
266 290 b'This can severly limit Mercurial ability to build good '
267 291 b'chain of delta resulting is much more storage space being '
268 292 b'taken and limit reusability of on disk delta during '
269 293 b'exchange.'
270 294 )
271 295
272 296 upgrademessage = _(
273 297 b'Revlog supports delta chain with more unused data '
274 298 b'between payload. These gaps will be skipped at read '
275 299 b'time. This allows for better delta chains, making a '
276 300 b'better compression and faster exchange with server.'
277 301 )
278 302
279 303
280 304 @registerformatvariant
281 305 class persistentnodemap(requirementformatvariant):
282 306 name = b'persistent-nodemap'
283 307
284 308 _requirement = requirements.NODEMAP_REQUIREMENT
285 309
286 310 default = False
287 311
288 312 description = _(
289 313 b'persist the node -> rev mapping on disk to speedup lookup'
290 314 )
291 315
292 316 upgrademessage = _(b'Speedup revision lookup by node id.')
293 317
294 318
295 319 @registerformatvariant
296 320 class copiessdc(requirementformatvariant):
297 321 name = b'copies-sdc'
298 322
299 323 _requirement = requirements.COPIESSDC_REQUIREMENT
300 324
301 325 default = False
302 326
303 327 description = _(b'Stores copies information alongside changesets.')
304 328
305 329 upgrademessage = _(
306 330 b'Allows to use more efficient algorithm to deal with ' b'copy tracing.'
307 331 )
308 332
309 333
310 334 @registerformatvariant
311 335 class revlogv2(requirementformatvariant):
312 336 name = b'revlog-v2'
313 337 _requirement = requirements.REVLOGV2_REQUIREMENT
314 338 default = False
315 339 description = _(b'Version 2 of the revlog.')
316 340 upgrademessage = _(b'very experimental')
317 341
318 342
319 343 @registerformatvariant
320 344 class changelogv2(requirementformatvariant):
321 345 name = b'changelog-v2'
322 346 _requirement = requirements.CHANGELOGV2_REQUIREMENT
323 347 default = False
324 348 description = _(b'An iteration of the revlog focussed on changelog needs.')
325 349 upgrademessage = _(b'quite experimental')
326 350
327 351
328 352 @registerformatvariant
329 353 class removecldeltachain(formatvariant):
330 354 name = b'plain-cl-delta'
331 355
332 356 default = True
333 357
334 358 description = _(
335 359 b'changelog storage is using deltas instead of '
336 360 b'raw entries; changelog reading and any '
337 361 b'operation relying on changelog data are slower '
338 362 b'than they could be'
339 363 )
340 364
341 365 upgrademessage = _(
342 366 b'changelog storage will be reformated to '
343 367 b'store raw entries; changelog reading will be '
344 368 b'faster; changelog size may be reduced'
345 369 )
346 370
347 371 @staticmethod
348 372 def fromrepo(repo):
349 373 # Mercurial 4.0 changed changelogs to not use delta chains. Search for
350 374 # changelogs with deltas.
351 375 cl = repo.changelog
352 376 chainbase = cl.chainbase
353 377 return all(rev == chainbase(rev) for rev in cl)
354 378
355 379 @staticmethod
356 380 def fromconfig(repo):
357 381 return True
358 382
359 383
360 384 _has_zstd = (
361 385 b'zstd' in util.compengines
362 386 and util.compengines[b'zstd'].available()
363 387 and util.compengines[b'zstd'].revlogheader()
364 388 )
365 389
366 390
367 391 @registerformatvariant
368 392 class compressionengine(formatvariant):
369 393 name = b'compression'
370 394
371 395 if _has_zstd:
372 396 default = b'zstd'
373 397 else:
374 398 default = b'zlib'
375 399
376 400 description = _(
377 401 b'Compresion algorithm used to compress data. '
378 402 b'Some engine are faster than other'
379 403 )
380 404
381 405 upgrademessage = _(
382 406 b'revlog content will be recompressed with the new algorithm.'
383 407 )
384 408
385 409 @classmethod
386 410 def fromrepo(cls, repo):
387 411 # we allow multiple compression engine requirement to co-exist because
388 412 # strickly speaking, revlog seems to support mixed compression style.
389 413 #
390 414 # The compression used for new entries will be "the last one"
391 415 compression = b'zlib'
392 416 for req in repo.requirements:
393 417 prefix = req.startswith
394 418 if prefix(b'revlog-compression-') or prefix(b'exp-compression-'):
395 419 compression = req.split(b'-', 2)[2]
396 420 return compression
397 421
398 422 @classmethod
399 423 def fromconfig(cls, repo):
400 424 compengines = repo.ui.configlist(b'format', b'revlog-compression')
401 425 # return the first valid value as the selection code would do
402 426 for comp in compengines:
403 427 if comp in util.compengines:
404 428 e = util.compengines[comp]
405 429 if e.available() and e.revlogheader():
406 430 return comp
407 431
408 432 # no valide compression found lets display it all for clarity
409 433 return b','.join(compengines)
410 434
411 435
412 436 @registerformatvariant
413 437 class compressionlevel(formatvariant):
414 438 name = b'compression-level'
415 439 default = b'default'
416 440
417 441 description = _(b'compression level')
418 442
419 443 upgrademessage = _(b'revlog content will be recompressed')
420 444
421 445 @classmethod
422 446 def fromrepo(cls, repo):
423 447 comp = compressionengine.fromrepo(repo)
424 448 level = None
425 449 if comp == b'zlib':
426 450 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
427 451 elif comp == b'zstd':
428 452 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
429 453 if level is None:
430 454 return b'default'
431 455 return bytes(level)
432 456
433 457 @classmethod
434 458 def fromconfig(cls, repo):
435 459 comp = compressionengine.fromconfig(repo)
436 460 level = None
437 461 if comp == b'zlib':
438 462 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
439 463 elif comp == b'zstd':
440 464 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
441 465 if level is None:
442 466 return b'default'
443 467 return bytes(level)
444 468
445 469
446 470 def find_format_upgrades(repo):
447 471 """returns a list of format upgrades which can be perform on the repo"""
448 472 upgrades = []
449 473
450 474 # We could detect lack of revlogv1 and store here, but they were added
451 475 # in 0.9.2 and we don't support upgrading repos without these
452 476 # requirements, so let's not bother.
453 477
454 478 for fv in allformatvariant:
455 479 if not fv.fromrepo(repo):
456 480 upgrades.append(fv)
457 481
458 482 return upgrades
459 483
460 484
461 485 def find_format_downgrades(repo):
462 486 """returns a list of format downgrades which will be performed on the repo
463 487 because of disabled config option for them"""
464 488
465 489 downgrades = []
466 490
467 491 for fv in allformatvariant:
468 492 if fv.name == b'compression':
469 493 # If there is a compression change between repository
470 494 # and config, destination repository compression will change
471 495 # and current compression will be removed.
472 496 if fv.fromrepo(repo) != fv.fromconfig(repo):
473 497 downgrades.append(fv)
474 498 continue
475 499 # format variant exist in repo but does not exist in new repository
476 500 # config
477 501 if fv.fromrepo(repo) and not fv.fromconfig(repo):
478 502 downgrades.append(fv)
479 503
480 504 return downgrades
481 505
482 506
483 507 ALL_OPTIMISATIONS = []
484 508
485 509
486 510 def register_optimization(obj):
487 511 ALL_OPTIMISATIONS.append(obj)
488 512 return obj
489 513
490 514
491 515 class optimization(improvement):
492 516 """an improvement subclass dedicated to optimizations"""
493 517
494 518 type = OPTIMISATION
495 519
496 520
497 521 @register_optimization
498 522 class redeltaparents(optimization):
499 523 name = b're-delta-parent'
500 524
501 525 type = OPTIMISATION
502 526
503 527 description = _(
504 528 b'deltas within internal storage will be recalculated to '
505 529 b'choose an optimal base revision where this was not '
506 530 b'already done; the size of the repository may shrink and '
507 531 b'various operations may become faster; the first time '
508 532 b'this optimization is performed could slow down upgrade '
509 533 b'execution considerably; subsequent invocations should '
510 534 b'not run noticeably slower'
511 535 )
512 536
513 537 upgrademessage = _(
514 538 b'deltas within internal storage will choose a new '
515 539 b'base revision if needed'
516 540 )
517 541
518 542
519 543 @register_optimization
520 544 class redeltamultibase(optimization):
521 545 name = b're-delta-multibase'
522 546
523 547 type = OPTIMISATION
524 548
525 549 description = _(
526 550 b'deltas within internal storage will be recalculated '
527 551 b'against multiple base revision and the smallest '
528 552 b'difference will be used; the size of the repository may '
529 553 b'shrink significantly when there are many merges; this '
530 554 b'optimization will slow down execution in proportion to '
531 555 b'the number of merges in the repository and the amount '
532 556 b'of files in the repository; this slow down should not '
533 557 b'be significant unless there are tens of thousands of '
534 558 b'files and thousands of merges'
535 559 )
536 560
537 561 upgrademessage = _(
538 562 b'deltas within internal storage will choose an '
539 563 b'optimal delta by computing deltas against multiple '
540 564 b'parents; may slow down execution time '
541 565 b'significantly'
542 566 )
543 567
544 568
545 569 @register_optimization
546 570 class redeltaall(optimization):
547 571 name = b're-delta-all'
548 572
549 573 type = OPTIMISATION
550 574
551 575 description = _(
552 576 b'deltas within internal storage will always be '
553 577 b'recalculated without reusing prior deltas; this will '
554 578 b'likely make execution run several times slower; this '
555 579 b'optimization is typically not needed'
556 580 )
557 581
558 582 upgrademessage = _(
559 583 b'deltas within internal storage will be fully '
560 584 b'recomputed; this will likely drastically slow down '
561 585 b'execution time'
562 586 )
563 587
564 588
565 589 @register_optimization
566 590 class redeltafulladd(optimization):
567 591 name = b're-delta-fulladd'
568 592
569 593 type = OPTIMISATION
570 594
571 595 description = _(
572 596 b'every revision will be re-added as if it was new '
573 597 b'content. It will go through the full storage '
574 598 b'mechanism giving extensions a chance to process it '
575 599 b'(eg. lfs). This is similar to "re-delta-all" but even '
576 600 b'slower since more logic is involved.'
577 601 )
578 602
579 603 upgrademessage = _(
580 604 b'each revision will be added as new content to the '
581 605 b'internal storage; this will likely drastically slow '
582 606 b'down execution time, but some extensions might need '
583 607 b'it'
584 608 )
585 609
586 610
587 611 def findoptimizations(repo):
588 612 """Determine optimisation that could be used during upgrade"""
589 613 # These are unconditionally added. There is logic later that figures out
590 614 # which ones to apply.
591 615 return list(ALL_OPTIMISATIONS)
592 616
593 617
594 618 def determine_upgrade_actions(
595 619 repo, format_upgrades, optimizations, sourcereqs, destreqs
596 620 ):
597 621 """Determine upgrade actions that will be performed.
598 622
599 623 Given a list of improvements as returned by ``find_format_upgrades`` and
600 624 ``findoptimizations``, determine the list of upgrade actions that
601 625 will be performed.
602 626
603 627 The role of this function is to filter improvements if needed, apply
604 628 recommended optimizations from the improvements list that make sense,
605 629 etc.
606 630
607 631 Returns a list of action names.
608 632 """
609 633 newactions = []
610 634
611 635 for d in format_upgrades:
612 636 name = d._requirement
613 637
614 638 # If the action is a requirement that doesn't show up in the
615 639 # destination requirements, prune the action.
616 640 if name is not None and name not in destreqs:
617 641 continue
618 642
619 643 newactions.append(d)
620 644
621 645 newactions.extend(o for o in sorted(optimizations) if o not in newactions)
622 646
623 647 # FUTURE consider adding some optimizations here for certain transitions.
624 648 # e.g. adding generaldelta could schedule parent redeltas.
625 649
626 650 return newactions
627 651
628 652
629 653 class UpgradeOperation(object):
630 654 """represent the work to be done during an upgrade"""
631 655
632 656 def __init__(
633 657 self,
634 658 ui,
635 659 new_requirements,
636 660 current_requirements,
637 661 upgrade_actions,
638 662 removed_actions,
639 663 revlogs_to_process,
640 664 backup_store,
641 665 ):
642 666 self.ui = ui
643 667 self.new_requirements = new_requirements
644 668 self.current_requirements = current_requirements
645 669 # list of upgrade actions the operation will perform
646 670 self.upgrade_actions = upgrade_actions
647 self._upgrade_actions_names = set([a.name for a in upgrade_actions])
648 671 self.removed_actions = removed_actions
649 672 self.revlogs_to_process = revlogs_to_process
650 673 # requirements which will be added by the operation
651 674 self._added_requirements = (
652 675 self.new_requirements - self.current_requirements
653 676 )
654 677 # requirements which will be removed by the operation
655 678 self._removed_requirements = (
656 679 self.current_requirements - self.new_requirements
657 680 )
658 681 # requirements which will be preserved by the operation
659 682 self._preserved_requirements = (
660 683 self.current_requirements & self.new_requirements
661 684 )
662 685 # optimizations which are not used and it's recommended that they
663 686 # should use them
664 687 all_optimizations = findoptimizations(None)
665 688 self.unused_optimizations = [
666 689 i for i in all_optimizations if i not in self.upgrade_actions
667 690 ]
668 691
669 692 # delta reuse mode of this upgrade operation
693 upgrade_actions_names = self.upgrade_actions_names
670 694 self.delta_reuse_mode = revlog.revlog.DELTAREUSEALWAYS
671 if b're-delta-all' in self._upgrade_actions_names:
695 if b're-delta-all' in upgrade_actions_names:
672 696 self.delta_reuse_mode = revlog.revlog.DELTAREUSENEVER
673 elif b're-delta-parent' in self._upgrade_actions_names:
697 elif b're-delta-parent' in upgrade_actions_names:
674 698 self.delta_reuse_mode = revlog.revlog.DELTAREUSESAMEREVS
675 elif b're-delta-multibase' in self._upgrade_actions_names:
699 elif b're-delta-multibase' in upgrade_actions_names:
676 700 self.delta_reuse_mode = revlog.revlog.DELTAREUSESAMEREVS
677 elif b're-delta-fulladd' in self._upgrade_actions_names:
701 elif b're-delta-fulladd' in upgrade_actions_names:
678 702 self.delta_reuse_mode = revlog.revlog.DELTAREUSEFULLADD
679 703
680 704 # should this operation force re-delta of both parents
681 705 self.force_re_delta_both_parents = (
682 b're-delta-multibase' in self._upgrade_actions_names
706 b're-delta-multibase' in upgrade_actions_names
683 707 )
684 708
685 709 # should this operation create a backup of the store
686 710 self.backup_store = backup_store
687 711
688 # whether the operation touches different revlogs at all or not
689 self.touches_filelogs = self._touches_filelogs()
690 self.touches_manifests = self._touches_manifests()
691 self.touches_changelog = self._touches_changelog()
692 # whether the operation touches requirements file or not
693 self.touches_requirements = self._touches_requirements()
694 self.touches_store = (
695 self.touches_filelogs
696 or self.touches_manifests
697 or self.touches_changelog
698 )
712 @property
713 def upgrade_actions_names(self):
714 return set([a.name for a in self.upgrade_actions])
715
716 @property
717 def requirements_only(self):
699 718 # does the operation only touches repository requirement
700 self.requirements_only = (
701 self.touches_requirements and not self.touches_store
719 return (
720 self.touches_requirements
721 and not self.touches_filelogs
722 and not self.touches_manifests
723 and not self.touches_changelog
724 and not self.touches_dirstate
702 725 )
703 726
704 def _touches_filelogs(self):
727 @property
728 def touches_filelogs(self):
705 729 for a in self.upgrade_actions:
706 730 # in optimisations, we re-process the revlogs again
707 731 if a.type == OPTIMISATION:
708 732 return True
709 733 elif a.touches_filelogs:
710 734 return True
711 735 for a in self.removed_actions:
712 736 if a.touches_filelogs:
713 737 return True
714 738 return False
715 739
716 def _touches_manifests(self):
740 @property
741 def touches_manifests(self):
717 742 for a in self.upgrade_actions:
718 743 # in optimisations, we re-process the revlogs again
719 744 if a.type == OPTIMISATION:
720 745 return True
721 746 elif a.touches_manifests:
722 747 return True
723 748 for a in self.removed_actions:
724 749 if a.touches_manifests:
725 750 return True
726 751 return False
727 752
728 def _touches_changelog(self):
753 @property
754 def touches_changelog(self):
729 755 for a in self.upgrade_actions:
730 756 # in optimisations, we re-process the revlogs again
731 757 if a.type == OPTIMISATION:
732 758 return True
733 759 elif a.touches_changelog:
734 760 return True
735 761 for a in self.removed_actions:
736 762 if a.touches_changelog:
737 763 return True
738 764 return False
739 765
740 def _touches_requirements(self):
766 @property
767 def touches_requirements(self):
741 768 for a in self.upgrade_actions:
742 769 # optimisations are used to re-process revlogs and does not result
743 770 # in a requirement being added or removed
744 771 if a.type == OPTIMISATION:
745 772 pass
746 773 elif a.touches_requirements:
747 774 return True
748 775 for a in self.removed_actions:
749 776 if a.touches_requirements:
750 777 return True
751 778
779 @property
780 def touches_dirstate(self):
781 for a in self.upgrade_actions:
782 # revlog optimisations do not affect the dirstate
783 if a.type == OPTIMISATION:
784 pass
785 elif a.touches_dirstate:
786 return True
787 for a in self.removed_actions:
788 if a.touches_dirstate:
789 return True
790
752 791 return False
753 792
754 793 def _write_labeled(self, l, label):
755 794 """
756 795 Utility function to aid writing of a list under one label
757 796 """
758 797 first = True
759 798 for r in sorted(l):
760 799 if not first:
761 800 self.ui.write(b', ')
762 801 self.ui.write(r, label=label)
763 802 first = False
764 803
765 804 def print_requirements(self):
766 805 self.ui.write(_(b'requirements\n'))
767 806 self.ui.write(_(b' preserved: '))
768 807 self._write_labeled(
769 808 self._preserved_requirements, "upgrade-repo.requirement.preserved"
770 809 )
771 810 self.ui.write((b'\n'))
772 811 if self._removed_requirements:
773 812 self.ui.write(_(b' removed: '))
774 813 self._write_labeled(
775 814 self._removed_requirements, "upgrade-repo.requirement.removed"
776 815 )
777 816 self.ui.write((b'\n'))
778 817 if self._added_requirements:
779 818 self.ui.write(_(b' added: '))
780 819 self._write_labeled(
781 820 self._added_requirements, "upgrade-repo.requirement.added"
782 821 )
783 822 self.ui.write((b'\n'))
784 823 self.ui.write(b'\n')
785 824
786 825 def print_optimisations(self):
787 826 optimisations = [
788 827 a for a in self.upgrade_actions if a.type == OPTIMISATION
789 828 ]
790 829 optimisations.sort(key=lambda a: a.name)
791 830 if optimisations:
792 831 self.ui.write(_(b'optimisations: '))
793 832 self._write_labeled(
794 833 [a.name for a in optimisations],
795 834 "upgrade-repo.optimisation.performed",
796 835 )
797 836 self.ui.write(b'\n\n')
798 837
799 838 def print_upgrade_actions(self):
800 839 for a in self.upgrade_actions:
801 840 self.ui.status(b'%s\n %s\n\n' % (a.name, a.upgrademessage))
802 841
803 842 def print_affected_revlogs(self):
804 843 if not self.revlogs_to_process:
805 844 self.ui.write((b'no revlogs to process\n'))
806 845 else:
807 846 self.ui.write((b'processed revlogs:\n'))
808 847 for r in sorted(self.revlogs_to_process):
809 848 self.ui.write((b' - %s\n' % r))
810 849 self.ui.write((b'\n'))
811 850
812 851 def print_unused_optimizations(self):
813 852 for i in self.unused_optimizations:
814 853 self.ui.status(_(b'%s\n %s\n\n') % (i.name, i.description))
815 854
816 855 def has_upgrade_action(self, name):
817 856 """Check whether the upgrade operation will perform this action"""
818 857 return name in self._upgrade_actions_names
819 858
820 859 def print_post_op_messages(self):
821 860 """print post upgrade operation warning messages"""
822 861 for a in self.upgrade_actions:
823 862 if a.postupgrademessage is not None:
824 863 self.ui.warn(b'%s\n' % a.postupgrademessage)
825 864 for a in self.removed_actions:
826 865 if a.postdowngrademessage is not None:
827 866 self.ui.warn(b'%s\n' % a.postdowngrademessage)
828 867
829 868
830 869 ### Code checking if a repository can got through the upgrade process at all. #
831 870
832 871
833 872 def requiredsourcerequirements(repo):
834 873 """Obtain requirements required to be present to upgrade a repo.
835 874
836 875 An upgrade will not be allowed if the repository doesn't have the
837 876 requirements returned by this function.
838 877 """
839 878 return {
840 879 # Introduced in Mercurial 0.9.2.
841 880 requirements.STORE_REQUIREMENT,
842 881 }
843 882
844 883
845 884 def blocksourcerequirements(repo):
846 885 """Obtain requirements that will prevent an upgrade from occurring.
847 886
848 887 An upgrade cannot be performed if the source repository contains a
849 888 requirements in the returned set.
850 889 """
851 890 return {
852 891 # The upgrade code does not yet support these experimental features.
853 892 # This is an artificial limitation.
854 893 requirements.TREEMANIFEST_REQUIREMENT,
855 894 # This was a precursor to generaldelta and was never enabled by default.
856 895 # It should (hopefully) not exist in the wild.
857 896 b'parentdelta',
858 897 # Upgrade should operate on the actual store, not the shared link.
859 898 requirements.SHARED_REQUIREMENT,
860 899 }
861 900
862 901
863 902 def check_revlog_version(reqs):
864 903 """Check that the requirements contain at least one Revlog version"""
865 904 all_revlogs = {
866 905 requirements.REVLOGV1_REQUIREMENT,
867 906 requirements.REVLOGV2_REQUIREMENT,
868 907 }
869 908 if not all_revlogs.intersection(reqs):
870 909 msg = _(b'cannot upgrade repository; missing a revlog version')
871 910 raise error.Abort(msg)
872 911
873 912
874 913 def check_source_requirements(repo):
875 914 """Ensure that no existing requirements prevent the repository upgrade"""
876 915
877 916 check_revlog_version(repo.requirements)
878 917 required = requiredsourcerequirements(repo)
879 918 missingreqs = required - repo.requirements
880 919 if missingreqs:
881 920 msg = _(b'cannot upgrade repository; requirement missing: %s')
882 921 missingreqs = b', '.join(sorted(missingreqs))
883 922 raise error.Abort(msg % missingreqs)
884 923
885 924 blocking = blocksourcerequirements(repo)
886 925 blockingreqs = blocking & repo.requirements
887 926 if blockingreqs:
888 927 m = _(b'cannot upgrade repository; unsupported source requirement: %s')
889 928 blockingreqs = b', '.join(sorted(blockingreqs))
890 929 raise error.Abort(m % blockingreqs)
891 930
892 931
893 932 ### Verify the validity of the planned requirement changes ####################
894 933
895 934
896 935 def supportremovedrequirements(repo):
897 936 """Obtain requirements that can be removed during an upgrade.
898 937
899 938 If an upgrade were to create a repository that dropped a requirement,
900 939 the dropped requirement must appear in the returned set for the upgrade
901 940 to be allowed.
902 941 """
903 942 supported = {
904 943 requirements.SPARSEREVLOG_REQUIREMENT,
905 944 requirements.COPIESSDC_REQUIREMENT,
906 945 requirements.NODEMAP_REQUIREMENT,
907 946 requirements.SHARESAFE_REQUIREMENT,
908 947 requirements.REVLOGV2_REQUIREMENT,
909 948 requirements.CHANGELOGV2_REQUIREMENT,
910 949 requirements.REVLOGV1_REQUIREMENT,
950 requirements.DIRSTATE_V2_REQUIREMENT,
911 951 }
912 952 for name in compression.compengines:
913 953 engine = compression.compengines[name]
914 954 if engine.available() and engine.revlogheader():
915 955 supported.add(b'exp-compression-%s' % name)
916 956 if engine.name() == b'zstd':
917 957 supported.add(b'revlog-compression-zstd')
918 958 return supported
919 959
920 960
921 961 def supporteddestrequirements(repo):
922 962 """Obtain requirements that upgrade supports in the destination.
923 963
924 964 If the result of the upgrade would create requirements not in this set,
925 965 the upgrade is disallowed.
926 966
927 967 Extensions should monkeypatch this to add their custom requirements.
928 968 """
929 969 supported = {
930 970 requirements.DOTENCODE_REQUIREMENT,
931 971 requirements.FNCACHE_REQUIREMENT,
932 972 requirements.GENERALDELTA_REQUIREMENT,
933 973 requirements.REVLOGV1_REQUIREMENT, # allowed in case of downgrade
934 974 requirements.STORE_REQUIREMENT,
935 975 requirements.SPARSEREVLOG_REQUIREMENT,
936 976 requirements.COPIESSDC_REQUIREMENT,
937 977 requirements.NODEMAP_REQUIREMENT,
938 978 requirements.SHARESAFE_REQUIREMENT,
939 979 requirements.REVLOGV2_REQUIREMENT,
940 980 requirements.CHANGELOGV2_REQUIREMENT,
941 981 requirements.DIRSTATE_V2_REQUIREMENT,
942 982 }
943 983 for name in compression.compengines:
944 984 engine = compression.compengines[name]
945 985 if engine.available() and engine.revlogheader():
946 986 supported.add(b'exp-compression-%s' % name)
947 987 if engine.name() == b'zstd':
948 988 supported.add(b'revlog-compression-zstd')
949 989 return supported
950 990
951 991
952 992 def allowednewrequirements(repo):
953 993 """Obtain requirements that can be added to a repository during upgrade.
954 994
955 995 This is used to disallow proposed requirements from being added when
956 996 they weren't present before.
957 997
958 998 We use a list of allowed requirement additions instead of a list of known
959 999 bad additions because the whitelist approach is safer and will prevent
960 1000 future, unknown requirements from accidentally being added.
961 1001 """
962 1002 supported = {
963 1003 requirements.DOTENCODE_REQUIREMENT,
964 1004 requirements.FNCACHE_REQUIREMENT,
965 1005 requirements.GENERALDELTA_REQUIREMENT,
966 1006 requirements.SPARSEREVLOG_REQUIREMENT,
967 1007 requirements.COPIESSDC_REQUIREMENT,
968 1008 requirements.NODEMAP_REQUIREMENT,
969 1009 requirements.SHARESAFE_REQUIREMENT,
970 1010 requirements.REVLOGV1_REQUIREMENT,
971 1011 requirements.REVLOGV2_REQUIREMENT,
972 1012 requirements.CHANGELOGV2_REQUIREMENT,
1013 requirements.DIRSTATE_V2_REQUIREMENT,
973 1014 }
974 1015 for name in compression.compengines:
975 1016 engine = compression.compengines[name]
976 1017 if engine.available() and engine.revlogheader():
977 1018 supported.add(b'exp-compression-%s' % name)
978 1019 if engine.name() == b'zstd':
979 1020 supported.add(b'revlog-compression-zstd')
980 1021 return supported
981 1022
982 1023
983 1024 def check_requirements_changes(repo, new_reqs):
984 1025 old_reqs = repo.requirements
985 1026 check_revlog_version(repo.requirements)
986 1027 support_removal = supportremovedrequirements(repo)
987 1028 no_remove_reqs = old_reqs - new_reqs - support_removal
988 1029 if no_remove_reqs:
989 1030 msg = _(b'cannot upgrade repository; requirement would be removed: %s')
990 1031 no_remove_reqs = b', '.join(sorted(no_remove_reqs))
991 1032 raise error.Abort(msg % no_remove_reqs)
992 1033
993 1034 support_addition = allowednewrequirements(repo)
994 1035 no_add_reqs = new_reqs - old_reqs - support_addition
995 1036 if no_add_reqs:
996 1037 m = _(b'cannot upgrade repository; do not support adding requirement: ')
997 1038 no_add_reqs = b', '.join(sorted(no_add_reqs))
998 1039 raise error.Abort(m + no_add_reqs)
999 1040
1000 1041 supported = supporteddestrequirements(repo)
1001 1042 unsupported_reqs = new_reqs - supported
1002 1043 if unsupported_reqs:
1003 1044 msg = _(
1004 1045 b'cannot upgrade repository; do not support destination '
1005 1046 b'requirement: %s'
1006 1047 )
1007 1048 unsupported_reqs = b', '.join(sorted(unsupported_reqs))
1008 1049 raise error.Abort(msg % unsupported_reqs)
@@ -1,593 +1,632
1 1 # upgrade.py - functions for in place upgrade of Mercurial repository
2 2 #
3 3 # Copyright (c) 2016-present, Gregory Szorc
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.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import stat
11 11
12 12 from ..i18n import _
13 13 from ..pycompat import getattr
14 14 from .. import (
15 15 changelog,
16 16 error,
17 17 filelog,
18 18 manifest,
19 19 metadata,
20 20 pycompat,
21 21 requirements,
22 22 scmutil,
23 23 store,
24 24 util,
25 25 vfs as vfsmod,
26 26 )
27 27 from ..revlogutils import (
28 28 constants as revlogconst,
29 29 flagutil,
30 30 nodemap,
31 31 sidedata as sidedatamod,
32 32 )
33 from . import actions as upgrade_actions
33 34
34 35
35 36 def get_sidedata_helpers(srcrepo, dstrepo):
36 37 use_w = srcrepo.ui.configbool(b'experimental', b'worker.repository-upgrade')
37 38 sequential = pycompat.iswindows or not use_w
38 39 if not sequential:
39 40 srcrepo.register_sidedata_computer(
40 41 revlogconst.KIND_CHANGELOG,
41 42 sidedatamod.SD_FILES,
42 43 (sidedatamod.SD_FILES,),
43 44 metadata._get_worker_sidedata_adder(srcrepo, dstrepo),
44 45 flagutil.REVIDX_HASCOPIESINFO,
45 46 replace=True,
46 47 )
47 48 return sidedatamod.get_sidedata_helpers(srcrepo, dstrepo._wanted_sidedata)
48 49
49 50
50 51 def _revlogfrompath(repo, rl_type, path):
51 52 """Obtain a revlog from a repo path.
52 53
53 54 An instance of the appropriate class is returned.
54 55 """
55 56 if rl_type & store.FILEFLAGS_CHANGELOG:
56 57 return changelog.changelog(repo.svfs)
57 58 elif rl_type & store.FILEFLAGS_MANIFESTLOG:
58 59 mandir = b''
59 60 if b'/' in path:
60 61 mandir = path.rsplit(b'/', 1)[0]
61 62 return manifest.manifestrevlog(
62 63 repo.nodeconstants, repo.svfs, tree=mandir
63 64 )
64 65 else:
65 66 # drop the extension and the `data/` prefix
66 67 path = path.rsplit(b'.', 1)[0].split(b'/', 1)[1]
67 68 return filelog.filelog(repo.svfs, path)
68 69
69 70
70 71 def _copyrevlog(tr, destrepo, oldrl, rl_type, unencodedname):
71 72 """copy all relevant files for `oldrl` into `destrepo` store
72 73
73 74 Files are copied "as is" without any transformation. The copy is performed
74 75 without extra checks. Callers are responsible for making sure the copied
75 76 content is compatible with format of the destination repository.
76 77 """
77 78 oldrl = getattr(oldrl, '_revlog', oldrl)
78 79 newrl = _revlogfrompath(destrepo, rl_type, unencodedname)
79 80 newrl = getattr(newrl, '_revlog', newrl)
80 81
81 82 oldvfs = oldrl.opener
82 83 newvfs = newrl.opener
83 84 oldindex = oldvfs.join(oldrl._indexfile)
84 85 newindex = newvfs.join(newrl._indexfile)
85 86 olddata = oldvfs.join(oldrl._datafile)
86 87 newdata = newvfs.join(newrl._datafile)
87 88
88 89 with newvfs(newrl._indexfile, b'w'):
89 90 pass # create all the directories
90 91
91 92 util.copyfile(oldindex, newindex)
92 93 copydata = oldrl.opener.exists(oldrl._datafile)
93 94 if copydata:
94 95 util.copyfile(olddata, newdata)
95 96
96 97 if rl_type & store.FILEFLAGS_FILELOG:
97 98 destrepo.svfs.fncache.add(unencodedname)
98 99 if copydata:
99 100 destrepo.svfs.fncache.add(unencodedname[:-2] + b'.d')
100 101
101 102
102 103 UPGRADE_CHANGELOG = b"changelog"
103 104 UPGRADE_MANIFEST = b"manifest"
104 105 UPGRADE_FILELOGS = b"all-filelogs"
105 106
106 107 UPGRADE_ALL_REVLOGS = frozenset(
107 108 [UPGRADE_CHANGELOG, UPGRADE_MANIFEST, UPGRADE_FILELOGS]
108 109 )
109 110
110 111
111 112 def matchrevlog(revlogfilter, rl_type):
112 113 """check if a revlog is selected for cloning.
113 114
114 115 In other words, are there any updates which need to be done on revlog
115 116 or it can be blindly copied.
116 117
117 118 The store entry is checked against the passed filter"""
118 119 if rl_type & store.FILEFLAGS_CHANGELOG:
119 120 return UPGRADE_CHANGELOG in revlogfilter
120 121 elif rl_type & store.FILEFLAGS_MANIFESTLOG:
121 122 return UPGRADE_MANIFEST in revlogfilter
122 123 assert rl_type & store.FILEFLAGS_FILELOG
123 124 return UPGRADE_FILELOGS in revlogfilter
124 125
125 126
126 127 def _perform_clone(
127 128 ui,
128 129 dstrepo,
129 130 tr,
130 131 old_revlog,
131 132 rl_type,
132 133 unencoded,
133 134 upgrade_op,
134 135 sidedata_helpers,
135 136 oncopiedrevision,
136 137 ):
137 138 """returns the new revlog object created"""
138 139 newrl = None
139 140 if matchrevlog(upgrade_op.revlogs_to_process, rl_type):
140 141 ui.note(
141 142 _(b'cloning %d revisions from %s\n') % (len(old_revlog), unencoded)
142 143 )
143 144 newrl = _revlogfrompath(dstrepo, rl_type, unencoded)
144 145 old_revlog.clone(
145 146 tr,
146 147 newrl,
147 148 addrevisioncb=oncopiedrevision,
148 149 deltareuse=upgrade_op.delta_reuse_mode,
149 150 forcedeltabothparents=upgrade_op.force_re_delta_both_parents,
150 151 sidedata_helpers=sidedata_helpers,
151 152 )
152 153 else:
153 154 msg = _(b'blindly copying %s containing %i revisions\n')
154 155 ui.note(msg % (unencoded, len(old_revlog)))
155 156 _copyrevlog(tr, dstrepo, old_revlog, rl_type, unencoded)
156 157
157 158 newrl = _revlogfrompath(dstrepo, rl_type, unencoded)
158 159 return newrl
159 160
160 161
161 162 def _clonerevlogs(
162 163 ui,
163 164 srcrepo,
164 165 dstrepo,
165 166 tr,
166 167 upgrade_op,
167 168 ):
168 169 """Copy revlogs between 2 repos."""
169 170 revcount = 0
170 171 srcsize = 0
171 172 srcrawsize = 0
172 173 dstsize = 0
173 174 fcount = 0
174 175 frevcount = 0
175 176 fsrcsize = 0
176 177 frawsize = 0
177 178 fdstsize = 0
178 179 mcount = 0
179 180 mrevcount = 0
180 181 msrcsize = 0
181 182 mrawsize = 0
182 183 mdstsize = 0
183 184 crevcount = 0
184 185 csrcsize = 0
185 186 crawsize = 0
186 187 cdstsize = 0
187 188
188 189 alldatafiles = list(srcrepo.store.walk())
189 190 # mapping of data files which needs to be cloned
190 191 # key is unencoded filename
191 192 # value is revlog_object_from_srcrepo
192 193 manifests = {}
193 194 changelogs = {}
194 195 filelogs = {}
195 196
196 197 # Perform a pass to collect metadata. This validates we can open all
197 198 # source files and allows a unified progress bar to be displayed.
198 199 for rl_type, unencoded, encoded, size in alldatafiles:
199 200 if not rl_type & store.FILEFLAGS_REVLOG_MAIN:
200 201 continue
201 202
202 203 rl = _revlogfrompath(srcrepo, rl_type, unencoded)
203 204
204 205 info = rl.storageinfo(
205 206 exclusivefiles=True,
206 207 revisionscount=True,
207 208 trackedsize=True,
208 209 storedsize=True,
209 210 )
210 211
211 212 revcount += info[b'revisionscount'] or 0
212 213 datasize = info[b'storedsize'] or 0
213 214 rawsize = info[b'trackedsize'] or 0
214 215
215 216 srcsize += datasize
216 217 srcrawsize += rawsize
217 218
218 219 # This is for the separate progress bars.
219 220 if rl_type & store.FILEFLAGS_CHANGELOG:
220 221 changelogs[unencoded] = (rl_type, rl)
221 222 crevcount += len(rl)
222 223 csrcsize += datasize
223 224 crawsize += rawsize
224 225 elif rl_type & store.FILEFLAGS_MANIFESTLOG:
225 226 manifests[unencoded] = (rl_type, rl)
226 227 mcount += 1
227 228 mrevcount += len(rl)
228 229 msrcsize += datasize
229 230 mrawsize += rawsize
230 231 elif rl_type & store.FILEFLAGS_FILELOG:
231 232 filelogs[unencoded] = (rl_type, rl)
232 233 fcount += 1
233 234 frevcount += len(rl)
234 235 fsrcsize += datasize
235 236 frawsize += rawsize
236 237 else:
237 238 error.ProgrammingError(b'unknown revlog type')
238 239
239 240 if not revcount:
240 241 return
241 242
242 243 ui.status(
243 244 _(
244 245 b'migrating %d total revisions (%d in filelogs, %d in manifests, '
245 246 b'%d in changelog)\n'
246 247 )
247 248 % (revcount, frevcount, mrevcount, crevcount)
248 249 )
249 250 ui.status(
250 251 _(b'migrating %s in store; %s tracked data\n')
251 252 % ((util.bytecount(srcsize), util.bytecount(srcrawsize)))
252 253 )
253 254
254 255 # Used to keep track of progress.
255 256 progress = None
256 257
257 258 def oncopiedrevision(rl, rev, node):
258 259 progress.increment()
259 260
260 261 sidedata_helpers = get_sidedata_helpers(srcrepo, dstrepo)
261 262
262 263 # Migrating filelogs
263 264 ui.status(
264 265 _(
265 266 b'migrating %d filelogs containing %d revisions '
266 267 b'(%s in store; %s tracked data)\n'
267 268 )
268 269 % (
269 270 fcount,
270 271 frevcount,
271 272 util.bytecount(fsrcsize),
272 273 util.bytecount(frawsize),
273 274 )
274 275 )
275 276 progress = srcrepo.ui.makeprogress(_(b'file revisions'), total=frevcount)
276 277 for unencoded, (rl_type, oldrl) in sorted(filelogs.items()):
277 278 newrl = _perform_clone(
278 279 ui,
279 280 dstrepo,
280 281 tr,
281 282 oldrl,
282 283 rl_type,
283 284 unencoded,
284 285 upgrade_op,
285 286 sidedata_helpers,
286 287 oncopiedrevision,
287 288 )
288 289 info = newrl.storageinfo(storedsize=True)
289 290 fdstsize += info[b'storedsize'] or 0
290 291 ui.status(
291 292 _(
292 293 b'finished migrating %d filelog revisions across %d '
293 294 b'filelogs; change in size: %s\n'
294 295 )
295 296 % (frevcount, fcount, util.bytecount(fdstsize - fsrcsize))
296 297 )
297 298
298 299 # Migrating manifests
299 300 ui.status(
300 301 _(
301 302 b'migrating %d manifests containing %d revisions '
302 303 b'(%s in store; %s tracked data)\n'
303 304 )
304 305 % (
305 306 mcount,
306 307 mrevcount,
307 308 util.bytecount(msrcsize),
308 309 util.bytecount(mrawsize),
309 310 )
310 311 )
311 312 if progress:
312 313 progress.complete()
313 314 progress = srcrepo.ui.makeprogress(
314 315 _(b'manifest revisions'), total=mrevcount
315 316 )
316 317 for unencoded, (rl_type, oldrl) in sorted(manifests.items()):
317 318 newrl = _perform_clone(
318 319 ui,
319 320 dstrepo,
320 321 tr,
321 322 oldrl,
322 323 rl_type,
323 324 unencoded,
324 325 upgrade_op,
325 326 sidedata_helpers,
326 327 oncopiedrevision,
327 328 )
328 329 info = newrl.storageinfo(storedsize=True)
329 330 mdstsize += info[b'storedsize'] or 0
330 331 ui.status(
331 332 _(
332 333 b'finished migrating %d manifest revisions across %d '
333 334 b'manifests; change in size: %s\n'
334 335 )
335 336 % (mrevcount, mcount, util.bytecount(mdstsize - msrcsize))
336 337 )
337 338
338 339 # Migrating changelog
339 340 ui.status(
340 341 _(
341 342 b'migrating changelog containing %d revisions '
342 343 b'(%s in store; %s tracked data)\n'
343 344 )
344 345 % (
345 346 crevcount,
346 347 util.bytecount(csrcsize),
347 348 util.bytecount(crawsize),
348 349 )
349 350 )
350 351 if progress:
351 352 progress.complete()
352 353 progress = srcrepo.ui.makeprogress(
353 354 _(b'changelog revisions'), total=crevcount
354 355 )
355 356 for unencoded, (rl_type, oldrl) in sorted(changelogs.items()):
356 357 newrl = _perform_clone(
357 358 ui,
358 359 dstrepo,
359 360 tr,
360 361 oldrl,
361 362 rl_type,
362 363 unencoded,
363 364 upgrade_op,
364 365 sidedata_helpers,
365 366 oncopiedrevision,
366 367 )
367 368 info = newrl.storageinfo(storedsize=True)
368 369 cdstsize += info[b'storedsize'] or 0
369 370 progress.complete()
370 371 ui.status(
371 372 _(
372 373 b'finished migrating %d changelog revisions; change in size: '
373 374 b'%s\n'
374 375 )
375 376 % (crevcount, util.bytecount(cdstsize - csrcsize))
376 377 )
377 378
378 379 dstsize = fdstsize + mdstsize + cdstsize
379 380 ui.status(
380 381 _(
381 382 b'finished migrating %d total revisions; total change in store '
382 383 b'size: %s\n'
383 384 )
384 385 % (revcount, util.bytecount(dstsize - srcsize))
385 386 )
386 387
387 388
388 389 def _files_to_copy_post_revlog_clone(srcrepo):
389 390 """yields files which should be copied to destination after revlogs
390 391 are cloned"""
391 392 for path, kind, st in sorted(srcrepo.store.vfs.readdir(b'', stat=True)):
392 393 # don't copy revlogs as they are already cloned
393 394 if store.revlog_type(path) is not None:
394 395 continue
395 396 # Skip transaction related files.
396 397 if path.startswith(b'undo'):
397 398 continue
398 399 # Only copy regular files.
399 400 if kind != stat.S_IFREG:
400 401 continue
401 402 # Skip other skipped files.
402 403 if path in (b'lock', b'fncache'):
403 404 continue
404 405 # TODO: should we skip cache too?
405 406
406 407 yield path
407 408
408 409
409 410 def _replacestores(currentrepo, upgradedrepo, backupvfs, upgrade_op):
410 411 """Replace the stores after current repository is upgraded
411 412
412 413 Creates a backup of current repository store at backup path
413 414 Replaces upgraded store files in current repo from upgraded one
414 415
415 416 Arguments:
416 417 currentrepo: repo object of current repository
417 418 upgradedrepo: repo object of the upgraded data
418 419 backupvfs: vfs object for the backup path
419 420 upgrade_op: upgrade operation object
420 421 to be used to decide what all is upgraded
421 422 """
422 423 # TODO: don't blindly rename everything in store
423 424 # There can be upgrades where store is not touched at all
424 425 if upgrade_op.backup_store:
425 426 util.rename(currentrepo.spath, backupvfs.join(b'store'))
426 427 else:
427 428 currentrepo.vfs.rmtree(b'store', forcibly=True)
428 429 util.rename(upgradedrepo.spath, currentrepo.spath)
429 430
430 431
431 432 def finishdatamigration(ui, srcrepo, dstrepo, requirements):
432 433 """Hook point for extensions to perform additional actions during upgrade.
433 434
434 435 This function is called after revlogs and store files have been copied but
435 436 before the new store is swapped into the original location.
436 437 """
437 438
438 439
439 440 def upgrade(ui, srcrepo, dstrepo, upgrade_op):
440 441 """Do the low-level work of upgrading a repository.
441 442
442 443 The upgrade is effectively performed as a copy between a source
443 444 repository and a temporary destination repository.
444 445
445 446 The source repository is unmodified for as long as possible so the
446 447 upgrade can abort at any time without causing loss of service for
447 448 readers and without corrupting the source repository.
448 449 """
449 450 assert srcrepo.currentwlock()
450 451 assert dstrepo.currentwlock()
451 452 backuppath = None
452 453 backupvfs = None
453 454
454 455 ui.status(
455 456 _(
456 457 b'(it is safe to interrupt this process any time before '
457 458 b'data migration completes)\n'
458 459 )
459 460 )
460 461
462 if upgrade_actions.dirstatev2 in upgrade_op.upgrade_actions:
463 ui.status(_(b'upgrading to dirstate-v2 from v1\n'))
464 upgrade_dirstate(ui, srcrepo, upgrade_op, b'v1', b'v2')
465 upgrade_op.upgrade_actions.remove(upgrade_actions.dirstatev2)
466
467 if upgrade_actions.dirstatev2 in upgrade_op.removed_actions:
468 ui.status(_(b'downgrading from dirstate-v2 to v1\n'))
469 upgrade_dirstate(ui, srcrepo, upgrade_op, b'v2', b'v1')
470 upgrade_op.removed_actions.remove(upgrade_actions.dirstatev2)
471
472 if not (upgrade_op.upgrade_actions or upgrade_op.removed_actions):
473 return
474
461 475 if upgrade_op.requirements_only:
462 476 ui.status(_(b'upgrading repository requirements\n'))
463 477 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
464 478 # if there is only one action and that is persistent nodemap upgrade
465 479 # directly write the nodemap file and update requirements instead of going
466 480 # through the whole cloning process
467 481 elif (
468 482 len(upgrade_op.upgrade_actions) == 1
469 and b'persistent-nodemap' in upgrade_op._upgrade_actions_names
483 and b'persistent-nodemap' in upgrade_op.upgrade_actions_names
470 484 and not upgrade_op.removed_actions
471 485 ):
472 486 ui.status(
473 487 _(b'upgrading repository to use persistent nodemap feature\n')
474 488 )
475 489 with srcrepo.transaction(b'upgrade') as tr:
476 490 unfi = srcrepo.unfiltered()
477 491 cl = unfi.changelog
478 492 nodemap.persist_nodemap(tr, cl, force=True)
479 493 # we want to directly operate on the underlying revlog to force
480 494 # create a nodemap file. This is fine since this is upgrade code
481 495 # and it heavily relies on repository being revlog based
482 496 # hence accessing private attributes can be justified
483 497 nodemap.persist_nodemap(
484 498 tr, unfi.manifestlog._rootstore._revlog, force=True
485 499 )
486 500 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
487 501 elif (
488 502 len(upgrade_op.removed_actions) == 1
489 503 and [
490 504 x
491 505 for x in upgrade_op.removed_actions
492 506 if x.name == b'persistent-nodemap'
493 507 ]
494 508 and not upgrade_op.upgrade_actions
495 509 ):
496 510 ui.status(
497 511 _(b'downgrading repository to not use persistent nodemap feature\n')
498 512 )
499 513 with srcrepo.transaction(b'upgrade') as tr:
500 514 unfi = srcrepo.unfiltered()
501 515 cl = unfi.changelog
502 516 nodemap.delete_nodemap(tr, srcrepo, cl)
503 517 # check comment 20 lines above for accessing private attributes
504 518 nodemap.delete_nodemap(
505 519 tr, srcrepo, unfi.manifestlog._rootstore._revlog
506 520 )
507 521 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
508 522 else:
509 523 with dstrepo.transaction(b'upgrade') as tr:
510 524 _clonerevlogs(
511 525 ui,
512 526 srcrepo,
513 527 dstrepo,
514 528 tr,
515 529 upgrade_op,
516 530 )
517 531
518 532 # Now copy other files in the store directory.
519 533 for p in _files_to_copy_post_revlog_clone(srcrepo):
520 534 srcrepo.ui.status(_(b'copying %s\n') % p)
521 535 src = srcrepo.store.rawvfs.join(p)
522 536 dst = dstrepo.store.rawvfs.join(p)
523 537 util.copyfile(src, dst, copystat=True)
524 538
525 539 finishdatamigration(ui, srcrepo, dstrepo, requirements)
526 540
527 541 ui.status(_(b'data fully upgraded in a temporary repository\n'))
528 542
529 543 if upgrade_op.backup_store:
530 544 backuppath = pycompat.mkdtemp(
531 545 prefix=b'upgradebackup.', dir=srcrepo.path
532 546 )
533 547 backupvfs = vfsmod.vfs(backuppath)
534 548
535 549 # Make a backup of requires file first, as it is the first to be modified.
536 550 util.copyfile(
537 551 srcrepo.vfs.join(b'requires'), backupvfs.join(b'requires')
538 552 )
539 553
540 554 # We install an arbitrary requirement that clients must not support
541 555 # as a mechanism to lock out new clients during the data swap. This is
542 556 # better than allowing a client to continue while the repository is in
543 557 # an inconsistent state.
544 558 ui.status(
545 559 _(
546 560 b'marking source repository as being upgraded; clients will be '
547 561 b'unable to read from repository\n'
548 562 )
549 563 )
550 564 scmutil.writereporequirements(
551 565 srcrepo, srcrepo.requirements | {b'upgradeinprogress'}
552 566 )
553 567
554 568 ui.status(_(b'starting in-place swap of repository data\n'))
555 569 if upgrade_op.backup_store:
556 570 ui.status(
557 571 _(b'replaced files will be backed up at %s\n') % backuppath
558 572 )
559 573
560 574 # Now swap in the new store directory. Doing it as a rename should make
561 575 # the operation nearly instantaneous and atomic (at least in well-behaved
562 576 # environments).
563 577 ui.status(_(b'replacing store...\n'))
564 578 tstart = util.timer()
565 579 _replacestores(srcrepo, dstrepo, backupvfs, upgrade_op)
566 580 elapsed = util.timer() - tstart
567 581 ui.status(
568 582 _(
569 583 b'store replacement complete; repository was inconsistent for '
570 584 b'%0.1fs\n'
571 585 )
572 586 % elapsed
573 587 )
574 588
575 589 # We first write the requirements file. Any new requirements will lock
576 590 # out legacy clients.
577 591 ui.status(
578 592 _(
579 593 b'finalizing requirements file and making repository readable '
580 594 b'again\n'
581 595 )
582 596 )
583 597 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
584 598
585 599 if upgrade_op.backup_store:
586 600 # The lock file from the old store won't be removed because nothing has a
587 601 # reference to its new location. So clean it up manually. Alternatively, we
588 602 # could update srcrepo.svfs and other variables to point to the new
589 603 # location. This is simpler.
590 604 assert backupvfs is not None # help pytype
591 605 backupvfs.unlink(b'store/lock')
592 606
593 607 return backuppath
608
609
610 def upgrade_dirstate(ui, srcrepo, upgrade_op, old, new):
611 if upgrade_op.backup_store:
612 backuppath = pycompat.mkdtemp(
613 prefix=b'upgradebackup.', dir=srcrepo.path
614 )
615 ui.status(_(b'replaced files will be backed up at %s\n') % backuppath)
616 backupvfs = vfsmod.vfs(backuppath)
617 util.copyfile(
618 srcrepo.vfs.join(b'requires'), backupvfs.join(b'requires')
619 )
620 util.copyfile(
621 srcrepo.vfs.join(b'dirstate'), backupvfs.join(b'dirstate')
622 )
623
624 assert srcrepo.dirstate._use_dirstate_v2 == (old == b'v2')
625 srcrepo.dirstate._map._use_dirstate_tree = True
626 srcrepo.dirstate._map.preload()
627 srcrepo.dirstate._use_dirstate_v2 = new == b'v2'
628 srcrepo.dirstate._map._use_dirstate_v2 = srcrepo.dirstate._use_dirstate_v2
629 srcrepo.dirstate._dirty = True
630 srcrepo.dirstate.write(None)
631
632 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
@@ -1,3872 +1,3874
1 1 #testcases filelog compatibility changeset sidedata upgraded upgraded-parallel pull push pull-upgrade push-upgrade
2 2
3 3 =====================================================
4 4 Test Copy tracing for chain of copies involving merge
5 5 =====================================================
6 6
7 7 This test files covers copies/rename case for a chains of commit where merges
8 8 are involved. It cheks we do not have unwanted update of behavior and that the
9 9 different options to retrieve copies behave correctly.
10 10
11 11
12 12 Setup
13 13 =====
14 14
15 15 use git diff to see rename
16 16
17 17 $ cat << EOF >> ./no-linkrev
18 18 > #!$PYTHON
19 19 > # filter out linkrev part of the debugindex command
20 20 > import sys
21 21 > for line in sys.stdin:
22 22 > if " linkrev " in line:
23 23 > print(line.rstrip())
24 24 > else:
25 25 > l = "%s *%s" % (line[:6], line[14:].rstrip())
26 26 > print(l)
27 27 > EOF
28 28
29 29 $ cat << EOF >> $HGRCPATH
30 30 > [diff]
31 31 > git=yes
32 32 > [command-templates]
33 33 > log={desc}\n
34 34 > EOF
35 35
36 36 #if compatibility
37 37 $ cat >> $HGRCPATH << EOF
38 38 > [experimental]
39 39 > copies.read-from = compatibility
40 40 > EOF
41 41 #endif
42 42
43 43 #if changeset
44 44 $ cat >> $HGRCPATH << EOF
45 45 > [experimental]
46 46 > copies.read-from = changeset-only
47 47 > copies.write-to = changeset-only
48 48 > EOF
49 49 #endif
50 50
51 51 #if sidedata
52 52 $ cat >> $HGRCPATH << EOF
53 53 > [format]
54 54 > exp-use-copies-side-data-changeset = yes
55 55 > EOF
56 56 #endif
57 57
58 58 #if pull
59 59 $ cat >> $HGRCPATH << EOF
60 60 > [format]
61 61 > exp-use-copies-side-data-changeset = yes
62 62 > EOF
63 63 #endif
64 64
65 65 #if push
66 66 $ cat >> $HGRCPATH << EOF
67 67 > [format]
68 68 > exp-use-copies-side-data-changeset = yes
69 69 > EOF
70 70 #endif
71 71
72 72 #if pull-upgrade
73 73 $ cat >> $HGRCPATH << EOF
74 74 > [format]
75 75 > exp-use-copies-side-data-changeset = no
76 76 > [experimental]
77 77 > changegroup4 = yes
78 78 > EOF
79 79 #endif
80 80
81 81 #if push-upgrade
82 82 $ cat >> $HGRCPATH << EOF
83 83 > [format]
84 84 > exp-use-copies-side-data-changeset = no
85 85 > [experimental]
86 86 > changegroup4 = yes
87 87 > EOF
88 88 #endif
89 89
90 90 $ cat > same-content.txt << EOF
91 91 > Here is some content that will be the same accros multiple file.
92 92 >
93 93 > This is done on purpose so that we end up in some merge situation, were the
94 94 > resulting content is the same as in the parent(s), but a new filenodes still
95 95 > need to be created to record some file history information (especially
96 96 > about copies).
97 97 > EOF
98 98
99 99 $ hg init repo-chain
100 100 $ cd repo-chain
101 101
102 102 Add some linear rename initialy
103 103
104 104 $ cp ../same-content.txt a
105 105 $ cp ../same-content.txt b
106 106 $ cp ../same-content.txt h
107 107 $ echo "original content for P" > p
108 108 $ echo "original content for Q" > q
109 109 $ echo "original content for R" > r
110 110 $ hg ci -Am 'i-0 initial commit: a b h p q r'
111 111 adding a
112 112 adding b
113 113 adding h
114 114 adding p
115 115 adding q
116 116 adding r
117 117 $ hg mv a c
118 118 $ hg mv p s
119 119 $ hg ci -Am 'i-1: a -move-> c, p -move-> s'
120 120 $ hg mv c d
121 121 $ hg mv s t
122 122 $ hg ci -Am 'i-2: c -move-> d, s -move-> t'
123 123 $ hg log -G
124 124 @ i-2: c -move-> d, s -move-> t
125 125 |
126 126 o i-1: a -move-> c, p -move-> s
127 127 |
128 128 o i-0 initial commit: a b h p q r
129 129
130 130
131 131 And having another branch with renames on the other side
132 132
133 133 $ hg mv d e
134 134 $ hg ci -Am 'a-1: d -move-> e'
135 135 $ hg mv e f
136 136 $ hg ci -Am 'a-2: e -move-> f'
137 137 $ hg log -G --rev '::.'
138 138 @ a-2: e -move-> f
139 139 |
140 140 o a-1: d -move-> e
141 141 |
142 142 o i-2: c -move-> d, s -move-> t
143 143 |
144 144 o i-1: a -move-> c, p -move-> s
145 145 |
146 146 o i-0 initial commit: a b h p q r
147 147
148 148
149 149 Have a branching with nothing on one side
150 150
151 151 $ hg up 'desc("i-2")'
152 152 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
153 153 $ echo foo > b
154 154 $ hg ci -m 'b-1: b update'
155 155 created new head
156 156 $ hg log -G --rev '::.'
157 157 @ b-1: b update
158 158 |
159 159 o i-2: c -move-> d, s -move-> t
160 160 |
161 161 o i-1: a -move-> c, p -move-> s
162 162 |
163 163 o i-0 initial commit: a b h p q r
164 164
165 165
166 166 Create a branch that delete a file previous renamed
167 167
168 168 $ hg up 'desc("i-2")'
169 169 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
170 170 $ hg rm d
171 171 $ hg ci -m 'c-1 delete d'
172 172 created new head
173 173 $ hg log -G --rev '::.'
174 174 @ c-1 delete d
175 175 |
176 176 o i-2: c -move-> d, s -move-> t
177 177 |
178 178 o i-1: a -move-> c, p -move-> s
179 179 |
180 180 o i-0 initial commit: a b h p q r
181 181
182 182
183 183 Create a branch that delete a file previous renamed and recreate it
184 184
185 185 $ hg up 'desc("i-2")'
186 186 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
187 187 $ hg rm d
188 188 $ hg ci -m 'd-1 delete d'
189 189 created new head
190 190 $ echo bar > d
191 191 $ hg add d
192 192 $ hg ci -m 'd-2 re-add d'
193 193 $ hg log -G --rev '::.'
194 194 @ d-2 re-add d
195 195 |
196 196 o d-1 delete d
197 197 |
198 198 o i-2: c -move-> d, s -move-> t
199 199 |
200 200 o i-1: a -move-> c, p -move-> s
201 201 |
202 202 o i-0 initial commit: a b h p q r
203 203
204 204
205 205 Having another branch renaming a different file to the same filename as another
206 206
207 207 $ hg up 'desc("i-2")'
208 208 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
209 209 $ hg mv b g
210 210 $ hg ci -m 'e-1 b -move-> g'
211 211 created new head
212 212 $ hg mv g f
213 213 $ hg ci -m 'e-2 g -move-> f'
214 214 $ hg log -G --rev '::.'
215 215 @ e-2 g -move-> f
216 216 |
217 217 o e-1 b -move-> g
218 218 |
219 219 o i-2: c -move-> d, s -move-> t
220 220 |
221 221 o i-1: a -move-> c, p -move-> s
222 222 |
223 223 o i-0 initial commit: a b h p q r
224 224
225 225 $ hg up -q null
226 226
227 227 Having a branch similar to the 'a' one, but moving the 'p' file around.
228 228
229 229 $ hg up 'desc("i-2")'
230 230 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
231 231 $ hg mv t u
232 232 $ hg ci -Am 'p-1: t -move-> u'
233 233 created new head
234 234 $ hg mv u v
235 235 $ hg ci -Am 'p-2: u -move-> v'
236 236 $ hg log -G --rev '::.'
237 237 @ p-2: u -move-> v
238 238 |
239 239 o p-1: t -move-> u
240 240 |
241 241 o i-2: c -move-> d, s -move-> t
242 242 |
243 243 o i-1: a -move-> c, p -move-> s
244 244 |
245 245 o i-0 initial commit: a b h p q r
246 246
247 247 $ hg up -q null
248 248
249 249 Having another branch renaming a different file to the same filename as another
250 250
251 251 $ hg up 'desc("i-2")'
252 252 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
253 253 $ hg mv r w
254 254 $ hg ci -m 'q-1 r -move-> w'
255 255 created new head
256 256 $ hg mv w v
257 257 $ hg ci -m 'q-2 w -move-> v'
258 258 $ hg log -G --rev '::.'
259 259 @ q-2 w -move-> v
260 260 |
261 261 o q-1 r -move-> w
262 262 |
263 263 o i-2: c -move-> d, s -move-> t
264 264 |
265 265 o i-1: a -move-> c, p -move-> s
266 266 |
267 267 o i-0 initial commit: a b h p q r
268 268
269 269 $ hg up -q null
270 270
271 271 Setup all merge
272 272 ===============
273 273
274 274 This is done beforehand to validate that the upgrade process creates valid copy
275 275 information.
276 276
277 277 merging with unrelated change does not interfere with the renames
278 278 ---------------------------------------------------------------
279 279
280 280 - rename on one side
281 281 - unrelated change on the other side
282 282
283 283 $ case_desc="simple merge - A side: multiple renames, B side: unrelated update"
284 284
285 285 $ hg up 'desc("b-1")'
286 286 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
287 287 $ hg merge 'desc("a-2")'
288 288 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
289 289 (branch merge, don't forget to commit)
290 290 $ hg ci -m "mBAm-0 $case_desc - one way"
291 291 $ hg up 'desc("a-2")'
292 292 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
293 293 $ hg merge 'desc("b-1")'
294 294 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
295 295 (branch merge, don't forget to commit)
296 296 $ hg ci -m "mABm-0 $case_desc - the other way"
297 297 created new head
298 298 $ hg log -G --rev '::(desc("mABm")+desc("mBAm"))'
299 299 @ mABm-0 simple merge - A side: multiple renames, B side: unrelated update - the other way
300 300 |\
301 301 +---o mBAm-0 simple merge - A side: multiple renames, B side: unrelated update - one way
302 302 | |/
303 303 | o b-1: b update
304 304 | |
305 305 o | a-2: e -move-> f
306 306 | |
307 307 o | a-1: d -move-> e
308 308 |/
309 309 o i-2: c -move-> d, s -move-> t
310 310 |
311 311 o i-1: a -move-> c, p -move-> s
312 312 |
313 313 o i-0 initial commit: a b h p q r
314 314
315 315
316 316
317 317 merging with the side having a delete
318 318 -------------------------------------
319 319
320 320 case summary:
321 321 - one with change to an unrelated file
322 322 - one deleting the change
323 323 and recreate an unrelated file after the merge
324 324
325 325 $ case_desc="simple merge - C side: delete a file with copies history , B side: unrelated update"
326 326
327 327 $ hg up 'desc("b-1")'
328 328 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
329 329 $ hg merge 'desc("c-1")'
330 330 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
331 331 (branch merge, don't forget to commit)
332 332 $ hg ci -m "mBCm-0 $case_desc - one way"
333 333 $ echo bar > d
334 334 $ hg add d
335 335 $ hg ci -m 'mBCm-1 re-add d'
336 336 $ hg up 'desc("c-1")'
337 337 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
338 338 $ hg merge 'desc("b-1")'
339 339 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
340 340 (branch merge, don't forget to commit)
341 341 $ hg ci -m "mCBm-0 $case_desc - the other way"
342 342 created new head
343 343 $ echo bar > d
344 344 $ hg add d
345 345 $ hg ci -m 'mCBm-1 re-add d'
346 346 $ hg log -G --rev '::(desc("mCBm")+desc("mBCm"))'
347 347 @ mCBm-1 re-add d
348 348 |
349 349 o mCBm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - the other way
350 350 |\
351 351 | | o mBCm-1 re-add d
352 352 | | |
353 353 +---o mBCm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - one way
354 354 | |/
355 355 | o c-1 delete d
356 356 | |
357 357 o | b-1: b update
358 358 |/
359 359 o i-2: c -move-> d, s -move-> t
360 360 |
361 361 o i-1: a -move-> c, p -move-> s
362 362 |
363 363 o i-0 initial commit: a b h p q r
364 364
365 365
366 366 Comparing with a merge re-adding the file afterward
367 367 ---------------------------------------------------
368 368
369 369 Merge:
370 370 - one with change to an unrelated file
371 371 - one deleting and recreating the change
372 372
373 373 $ case_desc="simple merge - B side: unrelated update, D side: delete and recreate a file (with different content)"
374 374
375 375 $ hg up 'desc("b-1")'
376 376 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
377 377 $ hg merge 'desc("d-2")'
378 378 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
379 379 (branch merge, don't forget to commit)
380 380 $ hg ci -m "mBDm-0 $case_desc - one way"
381 381 $ hg up 'desc("d-2")'
382 382 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
383 383 $ hg merge 'desc("b-1")'
384 384 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
385 385 (branch merge, don't forget to commit)
386 386 $ hg ci -m "mDBm-0 $case_desc - the other way"
387 387 created new head
388 388 $ hg log -G --rev '::(desc("mDBm")+desc("mBDm"))'
389 389 @ mDBm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - the other way
390 390 |\
391 391 +---o mBDm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - one way
392 392 | |/
393 393 | o d-2 re-add d
394 394 | |
395 395 | o d-1 delete d
396 396 | |
397 397 o | b-1: b update
398 398 |/
399 399 o i-2: c -move-> d, s -move-> t
400 400 |
401 401 o i-1: a -move-> c, p -move-> s
402 402 |
403 403 o i-0 initial commit: a b h p q r
404 404
405 405
406 406
407 407 Comparing with a merge with colliding rename
408 408 --------------------------------------------
409 409
410 410 Subcase: new copy information on both side
411 411 ``````````````````````````````````````````
412 412
413 413 - the "e-" branch renaming b to f (through 'g')
414 414 - the "a-" branch renaming d to f (through e)
415 415
416 416 $ case_desc="merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f)"
417 417
418 418 $ hg up 'desc("a-2")'
419 419 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
420 420 $ hg merge 'desc("e-2")'
421 421 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
422 422 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
423 423 (branch merge, don't forget to commit)
424 424 $ hg ci -m "mAEm-0 $case_desc - one way"
425 425 $ hg up 'desc("e-2")'
426 426 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
427 427 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
428 428 $ hg merge 'desc("a-2")'
429 429 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
430 430 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
431 431 (branch merge, don't forget to commit)
432 432 $ hg ci -m "mEAm-0 $case_desc - the other way"
433 433 created new head
434 434 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
435 435 @ mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
436 436 |\
437 437 +---o mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
438 438 | |/
439 439 | o e-2 g -move-> f
440 440 | |
441 441 | o e-1 b -move-> g
442 442 | |
443 443 o | a-2: e -move-> f
444 444 | |
445 445 o | a-1: d -move-> e
446 446 |/
447 447 o i-2: c -move-> d, s -move-> t
448 448 |
449 449 o i-1: a -move-> c, p -move-> s
450 450 |
451 451 o i-0 initial commit: a b h p q r
452 452
453 453
454 454 Subcase: new copy information on both side with an actual merge happening
455 455 `````````````````````````````````````````````````````````````````````````
456 456
457 457 - the "p-" branch renaming 't' to 'v' (through 'u')
458 458 - the "q-" branch renaming 'r' to 'v' (through 'w')
459 459
460 460 $ case_desc="merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content)"
461 461
462 462 $ hg up 'desc("p-2")'
463 463 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
464 464 $ hg merge 'desc("q-2")' --tool ':union'
465 465 merging v
466 466 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
467 467 (branch merge, don't forget to commit)
468 468 $ hg ci -m "mPQm-0 $case_desc - one way"
469 469 $ hg up 'desc("q-2")'
470 470 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
471 471 $ hg merge 'desc("p-2")' --tool ':union'
472 472 merging v
473 473 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
474 474 (branch merge, don't forget to commit)
475 475 $ hg ci -m "mQPm-0 $case_desc - the other way"
476 476 created new head
477 477 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
478 478 o mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
479 479 |\
480 480 +---o mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
481 481 | |/
482 482 | o e-2 g -move-> f
483 483 | |
484 484 | o e-1 b -move-> g
485 485 | |
486 486 o | a-2: e -move-> f
487 487 | |
488 488 o | a-1: d -move-> e
489 489 |/
490 490 o i-2: c -move-> d, s -move-> t
491 491 |
492 492 o i-1: a -move-> c, p -move-> s
493 493 |
494 494 o i-0 initial commit: a b h p q r
495 495
496 496
497 497 Subcase: existing copy information overwritten on one branch
498 498 ````````````````````````````````````````````````````````````
499 499
500 500 Merge:
501 501 - one with change to an unrelated file (b)
502 502 - one overwriting a file (d) with a rename (from h to i to d)
503 503
504 504 $ case_desc="simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d)"
505 505
506 506 $ hg up 'desc("i-2")'
507 507 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
508 508 $ hg mv h i
509 509 $ hg commit -m "f-1: rename h -> i"
510 510 created new head
511 511 $ hg mv --force i d
512 512 $ hg commit -m "f-2: rename i -> d"
513 513 $ hg debugindex d | "$PYTHON" ../no-linkrev
514 514 rev linkrev nodeid p1 p2
515 515 0 * d8252ab2e760 000000000000 000000000000 (no-changeset !)
516 516 0 * ae258f702dfe 000000000000 000000000000 (changeset !)
517 517 1 * b004912a8510 000000000000 000000000000
518 518 2 * 7b79e2fe0c89 000000000000 000000000000 (no-changeset !)
519 519 $ hg up 'desc("b-1")'
520 520 3 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
521 521 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
522 522 $ hg merge 'desc("f-2")'
523 523 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
524 524 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
525 525 (branch merge, don't forget to commit)
526 526 $ hg ci -m "mBFm-0 $case_desc - one way"
527 527 $ hg up 'desc("f-2")'
528 528 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
529 529 $ hg merge 'desc("b-1")'
530 530 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
531 531 (branch merge, don't forget to commit)
532 532 $ hg ci -m "mFBm-0 $case_desc - the other way"
533 533 created new head
534 534 $ hg up null --quiet
535 535 $ hg log -G --rev '::(desc("mBFm")+desc("mFBm"))'
536 536 o mFBm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
537 537 |\
538 538 +---o mBFm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
539 539 | |/
540 540 | o f-2: rename i -> d
541 541 | |
542 542 | o f-1: rename h -> i
543 543 | |
544 544 o | b-1: b update
545 545 |/
546 546 o i-2: c -move-> d, s -move-> t
547 547 |
548 548 o i-1: a -move-> c, p -move-> s
549 549 |
550 550 o i-0 initial commit: a b h p q r
551 551
552 552
553 553 Subcase: existing copy information overwritten on one branch, with different content)
554 554 `````````````````````````````````````````````````````````````````````````````````````
555 555
556 556 Merge:
557 557 - one with change to an unrelated file (b)
558 558 - one overwriting a file (t) with a rename (from r to x to t), v content is not the same as on the other branch
559 559
560 560 $ case_desc="simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content"
561 561
562 562 $ hg up 'desc("i-2")'
563 563 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
564 564 $ hg mv r x
565 565 $ hg commit -m "r-1: rename r -> x"
566 566 created new head
567 567 $ hg mv --force x t
568 568 $ hg commit -m "r-2: rename t -> x"
569 569 $ hg debugindex t | "$PYTHON" ../no-linkrev
570 570 rev linkrev nodeid p1 p2
571 571 0 * d74efbf65309 000000000000 000000000000 (no-changeset !)
572 572 1 * 02a930b9d7ad 000000000000 000000000000 (no-changeset !)
573 573 0 * 5aed6a8dbff0 000000000000 000000000000 (changeset !)
574 574 1 * a38b2fa17021 000000000000 000000000000 (changeset !)
575 575 $ hg up 'desc("b-1")'
576 576 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
577 577 $ hg merge 'desc("r-2")'
578 578 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
579 579 (branch merge, don't forget to commit)
580 580 $ hg ci -m "mBRm-0 $case_desc - one way"
581 581 $ hg up 'desc("r-2")'
582 582 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
583 583 $ hg merge 'desc("b-1")'
584 584 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
585 585 (branch merge, don't forget to commit)
586 586 $ hg ci -m "mRBm-0 $case_desc - the other way"
587 587 created new head
588 588 $ hg up null --quiet
589 589 $ hg log -G --rev '::(desc("mBRm")+desc("mRBm"))'
590 590 o mRBm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - the other way
591 591 |\
592 592 +---o mBRm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - one way
593 593 | |/
594 594 | o r-2: rename t -> x
595 595 | |
596 596 | o r-1: rename r -> x
597 597 | |
598 598 o | b-1: b update
599 599 |/
600 600 o i-2: c -move-> d, s -move-> t
601 601 |
602 602 o i-1: a -move-> c, p -move-> s
603 603 |
604 604 o i-0 initial commit: a b h p q r
605 605
606 606
607 607
608 608 Subcase: reset of the copy history on one side
609 609 ``````````````````````````````````````````````
610 610
611 611 Merge:
612 612 - one with change to a file
613 613 - one deleting and recreating the file
614 614
615 615 Unlike in the 'BD/DB' cases, an actual merge happened here. So we should
616 616 consider history and rename on both branch of the merge.
617 617
618 618 $ case_desc="actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content"
619 619
620 620 $ hg up 'desc("i-2")'
621 621 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
622 622 $ echo "some update" >> d
623 623 $ hg commit -m "g-1: update d"
624 624 created new head
625 625 $ hg up 'desc("d-2")'
626 626 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
627 627 $ hg merge 'desc("g-1")' --tool :union
628 628 merging d
629 629 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
630 630 (branch merge, don't forget to commit)
631 631 $ hg ci -m "mDGm-0 $case_desc - one way"
632 632 $ hg up 'desc("g-1")'
633 633 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
634 634 $ hg merge 'desc("d-2")' --tool :union
635 635 merging d
636 636 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
637 637 (branch merge, don't forget to commit)
638 638 $ hg ci -m "mGDm-0 $case_desc - the other way"
639 639 created new head
640 640 $ hg log -G --rev '::(desc("mDGm")+desc("mGDm"))'
641 641 @ mGDm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - the other way
642 642 |\
643 643 +---o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
644 644 | |/
645 645 | o g-1: update d
646 646 | |
647 647 o | d-2 re-add d
648 648 | |
649 649 o | d-1 delete d
650 650 |/
651 651 o i-2: c -move-> d, s -move-> t
652 652 |
653 653 o i-1: a -move-> c, p -move-> s
654 654 |
655 655 o i-0 initial commit: a b h p q r
656 656
657 657
658 658 Subcase: merging a change to a file with a "copy overwrite" to that file from another branch
659 659 ````````````````````````````````````````````````````````````````````````````````````````````
660 660
661 661 Merge:
662 662 - one with change to a file (d)
663 663 - one overwriting that file with a rename (from h to i, to d)
664 664
665 665 This case is similar to BF/FB, but an actual merge happens, so both side of the
666 666 history are relevant.
667 667
668 668 Note:
669 669 | In this case, the merge get conflicting information since on one side we have
670 670 | "a -> c -> d". and one the other one we have "h -> i -> d".
671 671 |
672 672 | The current code arbitrarily pick one side
673 673
674 674 $ case_desc="merge - G side: content change, F side: copy overwrite, no content change"
675 675
676 676 $ hg up 'desc("f-2")'
677 677 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
678 678 $ hg merge 'desc("g-1")' --tool :union
679 679 merging d (no-changeset !)
680 680 0 files updated, 1 files merged, 0 files removed, 0 files unresolved (no-changeset !)
681 681 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
682 682 (branch merge, don't forget to commit)
683 683 $ hg ci -m "mFGm-0 $case_desc - one way"
684 684 created new head
685 685 $ hg up 'desc("g-1")'
686 686 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
687 687 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
688 688 $ hg merge 'desc("f-2")' --tool :union
689 689 merging d (no-changeset !)
690 690 0 files updated, 1 files merged, 1 files removed, 0 files unresolved (no-changeset !)
691 691 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
692 692 (branch merge, don't forget to commit)
693 693 $ hg ci -m "mGFm-0 $case_desc - the other way"
694 694 created new head
695 695 $ hg log -G --rev '::(desc("mGFm")+desc("mFGm"))'
696 696 @ mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
697 697 |\
698 698 +---o mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
699 699 | |/
700 700 | o g-1: update d
701 701 | |
702 702 o | f-2: rename i -> d
703 703 | |
704 704 o | f-1: rename h -> i
705 705 |/
706 706 o i-2: c -move-> d, s -move-> t
707 707 |
708 708 o i-1: a -move-> c, p -move-> s
709 709 |
710 710 o i-0 initial commit: a b h p q r
711 711
712 712
713 713
714 714 Comparing with merging with a deletion (and keeping the file)
715 715 -------------------------------------------------------------
716 716
717 717 Merge:
718 718 - one removing a file (d)
719 719 - one updating that file
720 720 - the merge keep the modified version of the file (canceling the delete)
721 721
722 722 In this case, the file keep on living after the merge. So we should not drop its
723 723 copy tracing chain.
724 724
725 725 $ case_desc="merge updated/deleted - revive the file (updated content)"
726 726
727 727 $ hg up 'desc("c-1")'
728 728 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
729 729 $ hg merge 'desc("g-1")'
730 730 file 'd' was deleted in local [working copy] but was modified in other [merge rev].
731 731 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
732 732 What do you want to do? u
733 733 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
734 734 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
735 735 [1]
736 736 $ hg resolve -t :other d
737 737 (no more unresolved files)
738 738 $ hg ci -m "mCGm-0 $case_desc - one way"
739 739 created new head
740 740
741 741 $ hg up 'desc("g-1")'
742 742 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
743 743 $ hg merge 'desc("c-1")'
744 744 file 'd' was deleted in other [merge rev] but was modified in local [working copy].
745 745 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
746 746 What do you want to do? u
747 747 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
748 748 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
749 749 [1]
750 750 $ hg resolve -t :local d
751 751 (no more unresolved files)
752 752 $ hg ci -m "mGCm-0 $case_desc - the other way"
753 753 created new head
754 754
755 755 $ hg log -G --rev '::(desc("mCGm")+desc("mGCm"))'
756 756 @ mGCm-0 merge updated/deleted - revive the file (updated content) - the other way
757 757 |\
758 758 +---o mCGm-0 merge updated/deleted - revive the file (updated content) - one way
759 759 | |/
760 760 | o g-1: update d
761 761 | |
762 762 o | c-1 delete d
763 763 |/
764 764 o i-2: c -move-> d, s -move-> t
765 765 |
766 766 o i-1: a -move-> c, p -move-> s
767 767 |
768 768 o i-0 initial commit: a b h p q r
769 769
770 770
771 771
772 772
773 773 Comparing with merge restoring an untouched deleted file
774 774 --------------------------------------------------------
775 775
776 776 Merge:
777 777 - one removing a file (d)
778 778 - one leaving the file untouched
779 779 - the merge actively restore the file to the same content.
780 780
781 781 In this case, the file keep on living after the merge. So we should not drop its
782 782 copy tracing chain.
783 783
784 784 $ case_desc="merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge)"
785 785
786 786 $ hg up 'desc("c-1")'
787 787 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
788 788 $ hg merge 'desc("b-1")'
789 789 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
790 790 (branch merge, don't forget to commit)
791 791 $ hg revert --rev 'desc("b-1")' d
792 792 $ hg ci -m "mCB-revert-m-0 $case_desc - one way"
793 793 created new head
794 794
795 795 $ hg up 'desc("b-1")'
796 796 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
797 797 $ hg merge 'desc("c-1")'
798 798 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
799 799 (branch merge, don't forget to commit)
800 800 $ hg revert --rev 'desc("b-1")' d
801 801 $ hg ci -m "mBC-revert-m-0 $case_desc - the other way"
802 802 created new head
803 803
804 804 $ hg log -G --rev '::(desc("mCB-revert-m")+desc("mBC-revert-m"))'
805 805 @ mBC-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
806 806 |\
807 807 +---o mCB-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
808 808 | |/
809 809 | o c-1 delete d
810 810 | |
811 811 o | b-1: b update
812 812 |/
813 813 o i-2: c -move-> d, s -move-> t
814 814 |
815 815 o i-1: a -move-> c, p -move-> s
816 816 |
817 817 o i-0 initial commit: a b h p q r
818 818
819 819
820 820
821 821 $ hg up null --quiet
822 822
823 823 Merging a branch where a rename was deleted with a branch where the same file was renamed
824 824 ------------------------------------------------------------------------------------------
825 825
826 826 Create a "conflicting" merge where `d` get removed on one branch before its
827 827 rename information actually conflict with the other branch.
828 828
829 829 (the copy information from the branch that was not deleted should win).
830 830
831 831 $ case_desc="simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch)"
832 832
833 833 $ hg up 'desc("i-0")'
834 834 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
835 835 $ hg mv b d
836 836 $ hg ci -m "h-1: b -(move)-> d"
837 837 created new head
838 838
839 839 $ hg up 'desc("c-1")'
840 840 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
841 841 $ hg merge 'desc("h-1")'
842 842 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
843 843 (branch merge, don't forget to commit)
844 844 $ hg ci -m "mCH-delete-before-conflict-m-0 $case_desc - one way"
845 845
846 846 $ hg up 'desc("h-1")'
847 847 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
848 848 $ hg merge 'desc("c-1")'
849 849 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
850 850 (branch merge, don't forget to commit)
851 851 $ hg ci -m "mHC-delete-before-conflict-m-0 $case_desc - the other way"
852 852 created new head
853 853 $ hg log -G --rev '::(desc("mCH-delete-before-conflict-m")+desc("mHC-delete-before-conflict-m"))'
854 854 @ mHC-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - the other way
855 855 |\
856 856 +---o mCH-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - one way
857 857 | |/
858 858 | o h-1: b -(move)-> d
859 859 | |
860 860 o | c-1 delete d
861 861 | |
862 862 o | i-2: c -move-> d, s -move-> t
863 863 | |
864 864 o | i-1: a -move-> c, p -move-> s
865 865 |/
866 866 o i-0 initial commit: a b h p q r
867 867
868 868
869 869 Variant of previous with extra changes introduced by the merge
870 870 --------------------------------------------------------------
871 871
872 872 Multiple cases above explicitely test cases where content are the same on both side during merge. In this section we will introduce variants for theses cases where new change are introduced to these file content during the merges.
873 873
874 874
875 875 Subcase: merge has same initial content on both side, but merge introduced a change
876 876 ```````````````````````````````````````````````````````````````````````````````````
877 877
878 878 Same as `mAEm` and `mEAm` but with extra change to the file before commiting
879 879
880 880 - the "e-" branch renaming b to f (through 'g')
881 881 - the "a-" branch renaming d to f (through e)
882 882
883 883 $ case_desc="merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent)"
884 884
885 885 $ hg up 'desc("a-2")'
886 886 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
887 887 $ hg merge 'desc("e-2")'
888 888 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
889 889 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
890 890 (branch merge, don't forget to commit)
891 891 $ echo "content change for mAE-change-m" > f
892 892 $ hg ci -m "mAE-change-m-0 $case_desc - one way"
893 893 created new head
894 894 $ hg up 'desc("e-2")'
895 895 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
896 896 $ hg merge 'desc("a-2")'
897 897 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
898 898 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
899 899 (branch merge, don't forget to commit)
900 900 $ echo "content change for mEA-change-m" > f
901 901 $ hg ci -m "mEA-change-m-0 $case_desc - the other way"
902 902 created new head
903 903 $ hg log -G --rev '::(desc("mAE-change-m")+desc("mEA-change-m"))'
904 904 @ mEA-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - the other way
905 905 |\
906 906 +---o mAE-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - one way
907 907 | |/
908 908 | o e-2 g -move-> f
909 909 | |
910 910 | o e-1 b -move-> g
911 911 | |
912 912 o | a-2: e -move-> f
913 913 | |
914 914 o | a-1: d -move-> e
915 915 |/
916 916 o i-2: c -move-> d, s -move-> t
917 917 |
918 918 o i-1: a -move-> c, p -move-> s
919 919 |
920 920 o i-0 initial commit: a b h p q r
921 921
922 922
923 923 Subcase: merge overwrite common copy information, but with extra change during the merge
924 924 ````````````````````````````````````````````````````````````````````````````````````````
925 925
926 926 Merge:
927 927 - one with change to an unrelated file (b)
928 928 - one overwriting a file (d) with a rename (from h to i to d)
929 929 - the merge update f content
930 930
931 931 $ case_desc="merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d)"
932 932
933 933 $ hg up 'desc("f-2")'
934 934 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
935 935 #if no-changeset
936 936 $ hg debugindex d | "$PYTHON" ../no-linkrev
937 937 rev linkrev nodeid p1 p2
938 938 0 * d8252ab2e760 000000000000 000000000000
939 939 1 * b004912a8510 000000000000 000000000000
940 940 2 * 7b79e2fe0c89 000000000000 000000000000
941 941 3 * 17ec97e60577 d8252ab2e760 000000000000
942 942 4 * 06dabf50734c b004912a8510 17ec97e60577
943 943 5 * 19c0e3924691 17ec97e60577 b004912a8510
944 944 6 * 89c873a01d97 7b79e2fe0c89 17ec97e60577
945 945 7 * d55cb4e9ef57 000000000000 000000000000
946 946 #else
947 947 $ hg debugindex d | "$PYTHON" ../no-linkrev
948 948 rev linkrev nodeid p1 p2
949 949 0 * ae258f702dfe 000000000000 000000000000
950 950 1 * b004912a8510 000000000000 000000000000
951 951 2 * 5cce88bf349f ae258f702dfe 000000000000
952 952 3 * cc269dd788c8 b004912a8510 5cce88bf349f
953 953 4 * 51c91a115080 5cce88bf349f b004912a8510
954 954 #endif
955 955 $ hg up 'desc("b-1")'
956 956 3 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
957 957 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
958 958 $ hg merge 'desc("f-2")'
959 959 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
960 960 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
961 961 (branch merge, don't forget to commit)
962 962 $ echo "extra-change to (formelly h) during the merge" > d
963 963 $ hg ci -m "mBF-change-m-0 $case_desc - one way"
964 964 created new head
965 965 $ hg manifest --rev . --debug | grep " d"
966 966 1c334238bd42ec85c6a0d83fd1b2a898a6a3215d 644 d (no-changeset !)
967 967 cea2d99c0fde64672ef61953786fdff34f16e230 644 d (changeset !)
968 968
969 969 $ hg up 'desc("f-2")'
970 970 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
971 971 $ hg merge 'desc("b-1")'
972 972 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
973 973 (branch merge, don't forget to commit)
974 974 $ echo "extra-change to (formelly h) during the merge" > d
975 975 $ hg ci -m "mFB-change-m-0 $case_desc - the other way"
976 976 created new head
977 977 $ hg manifest --rev . --debug | grep " d"
978 978 1c334238bd42ec85c6a0d83fd1b2a898a6a3215d 644 d (no-changeset !)
979 979 cea2d99c0fde64672ef61953786fdff34f16e230 644 d (changeset !)
980 980 #if no-changeset
981 981 $ hg debugindex d | "$PYTHON" ../no-linkrev
982 982 rev linkrev nodeid p1 p2
983 983 0 * d8252ab2e760 000000000000 000000000000
984 984 1 * b004912a8510 000000000000 000000000000
985 985 2 * 7b79e2fe0c89 000000000000 000000000000
986 986 3 * 17ec97e60577 d8252ab2e760 000000000000
987 987 4 * 06dabf50734c b004912a8510 17ec97e60577
988 988 5 * 19c0e3924691 17ec97e60577 b004912a8510
989 989 6 * 89c873a01d97 7b79e2fe0c89 17ec97e60577
990 990 7 * d55cb4e9ef57 000000000000 000000000000
991 991 8 * 1c334238bd42 7b79e2fe0c89 000000000000
992 992 #else
993 993 $ hg debugindex d | "$PYTHON" ../no-linkrev
994 994 rev linkrev nodeid p1 p2
995 995 0 * ae258f702dfe 000000000000 000000000000
996 996 1 * b004912a8510 000000000000 000000000000
997 997 2 * 5cce88bf349f ae258f702dfe 000000000000
998 998 3 * cc269dd788c8 b004912a8510 5cce88bf349f
999 999 4 * 51c91a115080 5cce88bf349f b004912a8510
1000 1000 5 * cea2d99c0fde ae258f702dfe 000000000000
1001 1001 #endif
1002 1002 $ hg log -G --rev '::(desc("mBF-change-m")+desc("mFB-change-m"))'
1003 1003 @ mFB-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
1004 1004 |\
1005 1005 +---o mBF-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
1006 1006 | |/
1007 1007 | o f-2: rename i -> d
1008 1008 | |
1009 1009 | o f-1: rename h -> i
1010 1010 | |
1011 1011 o | b-1: b update
1012 1012 |/
1013 1013 o i-2: c -move-> d, s -move-> t
1014 1014 |
1015 1015 o i-1: a -move-> c, p -move-> s
1016 1016 |
1017 1017 o i-0 initial commit: a b h p q r
1018 1018
1019 1019
1020 1020 Subcase: restoring and untouched deleted file, while touching it
1021 1021 ````````````````````````````````````````````````````````````````
1022 1022
1023 1023 Merge:
1024 1024 - one removing a file (d)
1025 1025 - one leaving the file untouched
1026 1026 - the merge actively restore the file to the same content.
1027 1027
1028 1028 In this case, the file keep on living after the merge. So we should not drop its
1029 1029 copy tracing chain.
1030 1030
1031 1031 $ case_desc="merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge)"
1032 1032
1033 1033 $ hg up 'desc("c-1")'
1034 1034 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1035 1035 $ hg merge 'desc("b-1")'
1036 1036 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1037 1037 (branch merge, don't forget to commit)
1038 1038 $ hg revert --rev 'desc("b-1")' d
1039 1039 $ echo "new content for d after the revert" > d
1040 1040 $ hg ci -m "mCB-change-m-0 $case_desc - one way"
1041 1041 created new head
1042 1042 $ hg manifest --rev . --debug | grep " d"
1043 1043 e333780c17752a3b0dd15e3ad48aa4e5c745f621 644 d (no-changeset !)
1044 1044 4b540a18ad699234b2b2aa18cb69555ac9c4b1df 644 d (changeset !)
1045 1045
1046 1046 $ hg up 'desc("b-1")'
1047 1047 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1048 1048 $ hg merge 'desc("c-1")'
1049 1049 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1050 1050 (branch merge, don't forget to commit)
1051 1051 $ hg revert --rev 'desc("b-1")' d
1052 1052 $ echo "new content for d after the revert" > d
1053 1053 $ hg ci -m "mBC-change-m-0 $case_desc - the other way"
1054 1054 created new head
1055 1055 $ hg manifest --rev . --debug | grep " d"
1056 1056 e333780c17752a3b0dd15e3ad48aa4e5c745f621 644 d (no-changeset !)
1057 1057 4b540a18ad699234b2b2aa18cb69555ac9c4b1df 644 d (changeset !)
1058 1058
1059 1059
1060 1060 $ hg up null --quiet
1061 1061 $ hg log -G --rev '::(desc("mCB-change-m")+desc("mBC-change-m"))'
1062 1062 o mBC-change-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
1063 1063 |\
1064 1064 +---o mCB-change-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
1065 1065 | |/
1066 1066 | o c-1 delete d
1067 1067 | |
1068 1068 o | b-1: b update
1069 1069 |/
1070 1070 o i-2: c -move-> d, s -move-> t
1071 1071 |
1072 1072 o i-1: a -move-> c, p -move-> s
1073 1073 |
1074 1074 o i-0 initial commit: a b h p q r
1075 1075
1076 1076
1077 1077 Decision from previous merge are properly chained with later merge
1078 1078 ------------------------------------------------------------------
1079 1079
1080 1080 Subcase: chaining conflicting rename resolution
1081 1081 ```````````````````````````````````````````````
1082 1082
1083 1083 The "mAEm" and "mEAm" case create a rename tracking conflict on file 'f'. We
1084 1084 add more change on the respective branch and merge again. These second merge
1085 1085 does not involve the file 'f' and the arbitration done within "mAEm" and "mEA"
1086 1086 about that file should stay unchanged.
1087 1087
1088 1088 We also touch J during some of the merge to check for unrelated change to new file during merge.
1089 1089
1090 1090 $ case_desc="chained merges (conflict -> simple) - same content everywhere"
1091 1091
1092 1092 (extra unrelated changes)
1093 1093
1094 1094 $ hg up 'desc("a-2")'
1095 1095 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
1096 1096 $ echo j > unrelated-j
1097 1097 $ hg add unrelated-j
1098 1098 $ hg ci -m 'j-1: unrelated changes (based on the "a" series of changes)'
1099 1099 created new head
1100 1100
1101 1101 $ hg up 'desc("e-2")'
1102 1102 2 files updated, 0 files merged, 2 files removed, 0 files unresolved (no-changeset !)
1103 1103 1 files updated, 0 files merged, 2 files removed, 0 files unresolved (changeset !)
1104 1104 $ echo k > unrelated-k
1105 1105 $ hg add unrelated-k
1106 1106 $ hg ci -m 'k-1: unrelated changes (based on "e" changes)'
1107 1107 created new head
1108 1108
1109 1109 (merge variant 1)
1110 1110
1111 1111 $ hg up 'desc("mAEm")'
1112 1112 1 files updated, 0 files merged, 2 files removed, 0 files unresolved (no-changeset !)
1113 1113 0 files updated, 0 files merged, 2 files removed, 0 files unresolved (changeset !)
1114 1114 $ hg merge 'desc("k-1")'
1115 1115 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1116 1116 (branch merge, don't forget to commit)
1117 1117 $ hg ci -m "mAE,Km: $case_desc"
1118 1118
1119 1119 (merge variant 2)
1120 1120
1121 1121 $ hg up 'desc("k-1")'
1122 1122 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
1123 1123 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
1124 1124
1125 1125 $ hg merge 'desc("mAEm")'
1126 1126 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
1127 1127 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
1128 1128 (branch merge, don't forget to commit)
1129 1129 $ hg ci -m "mK,AEm: $case_desc"
1130 1130 created new head
1131 1131
1132 1132 (merge variant 3)
1133 1133
1134 1134 $ hg up 'desc("mEAm")'
1135 1135 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1136 1136 $ hg merge 'desc("j-1")'
1137 1137 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1138 1138 (branch merge, don't forget to commit)
1139 1139 $ echo jj > unrelated-j
1140 1140 $ hg ci -m "mEA,Jm: $case_desc"
1141 1141
1142 1142 (merge variant 4)
1143 1143
1144 1144 $ hg up 'desc("j-1")'
1145 1145 3 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
1146 1146 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
1147 1147 $ hg merge 'desc("mEAm")'
1148 1148 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
1149 1149 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
1150 1150 (branch merge, don't forget to commit)
1151 1151 $ echo jj > unrelated-j
1152 1152 $ hg ci -m "mJ,EAm: $case_desc"
1153 1153 created new head
1154 1154
1155 1155
1156 1156 $ hg log -G --rev '::(desc("mAE,Km") + desc("mK,AEm") + desc("mEA,Jm") + desc("mJ,EAm"))'
1157 1157 @ mJ,EAm: chained merges (conflict -> simple) - same content everywhere
1158 1158 |\
1159 1159 +---o mEA,Jm: chained merges (conflict -> simple) - same content everywhere
1160 1160 | |/
1161 1161 | | o mK,AEm: chained merges (conflict -> simple) - same content everywhere
1162 1162 | | |\
1163 1163 | | +---o mAE,Km: chained merges (conflict -> simple) - same content everywhere
1164 1164 | | | |/
1165 1165 | | | o k-1: unrelated changes (based on "e" changes)
1166 1166 | | | |
1167 1167 | o | | j-1: unrelated changes (based on the "a" series of changes)
1168 1168 | | | |
1169 1169 o-----+ mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
1170 1170 |/ / /
1171 1171 | o / mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
1172 1172 |/|/
1173 1173 | o e-2 g -move-> f
1174 1174 | |
1175 1175 | o e-1 b -move-> g
1176 1176 | |
1177 1177 o | a-2: e -move-> f
1178 1178 | |
1179 1179 o | a-1: d -move-> e
1180 1180 |/
1181 1181 o i-2: c -move-> d, s -move-> t
1182 1182 |
1183 1183 o i-1: a -move-> c, p -move-> s
1184 1184 |
1185 1185 o i-0 initial commit: a b h p q r
1186 1186
1187 1187
1188 1188 Subcase: chaining conflicting rename resolution, with actual merging happening
1189 1189 ``````````````````````````````````````````````````````````````````````````````
1190 1190
1191 1191 The "mPQm" and "mQPm" case create a rename tracking conflict on file 't'. We
1192 1192 add more change on the respective branch and merge again. These second merge
1193 1193 does not involve the file 't' and the arbitration done within "mPQm" and "mQP"
1194 1194 about that file should stay unchanged.
1195 1195
1196 1196 $ case_desc="chained merges (conflict -> simple) - different content"
1197 1197
1198 1198 (extra unrelated changes)
1199 1199
1200 1200 $ hg up 'desc("p-2")'
1201 1201 3 files updated, 0 files merged, 3 files removed, 0 files unresolved
1202 1202 $ echo s > unrelated-s
1203 1203 $ hg add unrelated-s
1204 1204 $ hg ci -m 's-1: unrelated changes (based on the "p" series of changes)'
1205 1205 created new head
1206 1206
1207 1207 $ hg up 'desc("q-2")'
1208 1208 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
1209 1209 $ echo t > unrelated-t
1210 1210 $ hg add unrelated-t
1211 1211 $ hg ci -m 't-1: unrelated changes (based on "q" changes)'
1212 1212 created new head
1213 1213
1214 1214 (merge variant 1)
1215 1215
1216 1216 $ hg up 'desc("mPQm")'
1217 1217 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
1218 1218 $ hg merge 'desc("t-1")'
1219 1219 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1220 1220 (branch merge, don't forget to commit)
1221 1221 $ hg ci -m "mPQ,Tm: $case_desc"
1222 1222
1223 1223 (merge variant 2)
1224 1224
1225 1225 $ hg up 'desc("t-1")'
1226 1226 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1227 1227
1228 1228 $ hg merge 'desc("mPQm")'
1229 1229 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1230 1230 (branch merge, don't forget to commit)
1231 1231 $ hg ci -m "mT,PQm: $case_desc"
1232 1232 created new head
1233 1233
1234 1234 (merge variant 3)
1235 1235
1236 1236 $ hg up 'desc("mQPm")'
1237 1237 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1238 1238 $ hg merge 'desc("s-1")'
1239 1239 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1240 1240 (branch merge, don't forget to commit)
1241 1241 $ hg ci -m "mQP,Sm: $case_desc"
1242 1242
1243 1243 (merge variant 4)
1244 1244
1245 1245 $ hg up 'desc("s-1")'
1246 1246 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1247 1247 $ hg merge 'desc("mQPm")'
1248 1248 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1249 1249 (branch merge, don't forget to commit)
1250 1250 $ hg ci -m "mS,QPm: $case_desc"
1251 1251 created new head
1252 1252 $ hg up null --quiet
1253 1253
1254 1254
1255 1255 $ hg log -G --rev '::(desc("mPQ,Tm") + desc("mT,PQm") + desc("mQP,Sm") + desc("mS,QPm"))'
1256 1256 o mS,QPm: chained merges (conflict -> simple) - different content
1257 1257 |\
1258 1258 +---o mQP,Sm: chained merges (conflict -> simple) - different content
1259 1259 | |/
1260 1260 | | o mT,PQm: chained merges (conflict -> simple) - different content
1261 1261 | | |\
1262 1262 | | +---o mPQ,Tm: chained merges (conflict -> simple) - different content
1263 1263 | | | |/
1264 1264 | | | o t-1: unrelated changes (based on "q" changes)
1265 1265 | | | |
1266 1266 | o | | s-1: unrelated changes (based on the "p" series of changes)
1267 1267 | | | |
1268 1268 o-----+ mQPm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - the other way
1269 1269 |/ / /
1270 1270 | o / mPQm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - one way
1271 1271 |/|/
1272 1272 | o q-2 w -move-> v
1273 1273 | |
1274 1274 | o q-1 r -move-> w
1275 1275 | |
1276 1276 o | p-2: u -move-> v
1277 1277 | |
1278 1278 o | p-1: t -move-> u
1279 1279 |/
1280 1280 o i-2: c -move-> d, s -move-> t
1281 1281 |
1282 1282 o i-1: a -move-> c, p -move-> s
1283 1283 |
1284 1284 o i-0 initial commit: a b h p q r
1285 1285
1286 1286
1287 1287 Subcase: chaining salvage information during a merge
1288 1288 ````````````````````````````````````````````````````
1289 1289
1290 1290 We add more change on the branch were the file was deleted. merging again
1291 1291 should preserve the fact eh file was salvaged.
1292 1292
1293 1293 $ case_desc="chained merges (salvaged -> simple) - same content (when the file exists)"
1294 1294
1295 1295 (creating the change)
1296 1296
1297 1297 $ hg up 'desc("c-1")'
1298 1298 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1299 1299 $ echo l > unrelated-l
1300 1300 $ hg add unrelated-l
1301 1301 $ hg ci -m 'l-1: unrelated changes (based on "c" changes)'
1302 1302 created new head
1303 1303
1304 1304 (Merge variant 1)
1305 1305
1306 1306 $ hg up 'desc("mBC-revert-m")'
1307 1307 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1308 1308 $ hg merge 'desc("l-1")'
1309 1309 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1310 1310 (branch merge, don't forget to commit)
1311 1311 $ hg ci -m "mBC+revert,Lm: $case_desc"
1312 1312
1313 1313 (Merge variant 2)
1314 1314
1315 1315 $ hg up 'desc("mCB-revert-m")'
1316 1316 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1317 1317 $ hg merge 'desc("l-1")'
1318 1318 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1319 1319 (branch merge, don't forget to commit)
1320 1320 $ hg ci -m "mCB+revert,Lm: $case_desc"
1321 1321
1322 1322 (Merge variant 3)
1323 1323
1324 1324 $ hg up 'desc("l-1")'
1325 1325 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1326 1326
1327 1327 $ hg merge 'desc("mBC-revert-m")'
1328 1328 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1329 1329 (branch merge, don't forget to commit)
1330 1330 $ hg ci -m "mL,BC+revertm: $case_desc"
1331 1331 created new head
1332 1332
1333 1333 (Merge variant 4)
1334 1334
1335 1335 $ hg up 'desc("l-1")'
1336 1336 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1337 1337
1338 1338 $ hg merge 'desc("mCB-revert-m")'
1339 1339 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1340 1340 (branch merge, don't forget to commit)
1341 1341 $ hg ci -m "mL,CB+revertm: $case_desc"
1342 1342 created new head
1343 1343
1344 1344 $ hg log -G --rev '::(desc("mBC+revert,Lm") + desc("mCB+revert,Lm") + desc("mL,BC+revertm") + desc("mL,CB+revertm"))'
1345 1345 @ mL,CB+revertm: chained merges (salvaged -> simple) - same content (when the file exists)
1346 1346 |\
1347 1347 | | o mL,BC+revertm: chained merges (salvaged -> simple) - same content (when the file exists)
1348 1348 | |/|
1349 1349 +-+---o mCB+revert,Lm: chained merges (salvaged -> simple) - same content (when the file exists)
1350 1350 | | |
1351 1351 | +---o mBC+revert,Lm: chained merges (salvaged -> simple) - same content (when the file exists)
1352 1352 | | |/
1353 1353 | o | l-1: unrelated changes (based on "c" changes)
1354 1354 | | |
1355 1355 | | o mBC-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
1356 1356 | |/|
1357 1357 o---+ mCB-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
1358 1358 |/ /
1359 1359 o | c-1 delete d
1360 1360 | |
1361 1361 | o b-1: b update
1362 1362 |/
1363 1363 o i-2: c -move-> d, s -move-> t
1364 1364 |
1365 1365 o i-1: a -move-> c, p -move-> s
1366 1366 |
1367 1367 o i-0 initial commit: a b h p q r
1368 1368
1369 1369
1370 1370
1371 1371 Subcase: chaining "merged" information during a merge
1372 1372 ``````````````````````````````````````````````````````
1373 1373
1374 1374 When a non-rename change are merged with a copy overwrite, the merge pick the copy source from (p1) as the reference. We should preserve this information in subsequent merges.
1375 1375
1376 1376 $ case_desc="chained merges (copy-overwrite -> simple) - same content"
1377 1377
1378 1378 (extra unrelated changes)
1379 1379
1380 1380 $ hg up 'desc("f-2")'
1381 1381 2 files updated, 0 files merged, 2 files removed, 0 files unresolved (no-changeset !)
1382 1382 1 files updated, 0 files merged, 2 files removed, 0 files unresolved (changeset !)
1383 1383 $ echo n > unrelated-n
1384 1384 $ hg add unrelated-n
1385 1385 $ hg ci -m 'n-1: unrelated changes (based on the "f" series of changes)'
1386 1386 created new head
1387 1387
1388 1388 $ hg up 'desc("g-1")'
1389 1389 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1390 1390 $ echo o > unrelated-o
1391 1391 $ hg add unrelated-o
1392 1392 $ hg ci -m 'o-1: unrelated changes (based on "g" changes)'
1393 1393 created new head
1394 1394
1395 1395 (merge variant 1)
1396 1396
1397 1397 $ hg up 'desc("mFGm")'
1398 1398 1 files updated, 0 files merged, 2 files removed, 0 files unresolved (no-changeset !)
1399 1399 0 files updated, 0 files merged, 2 files removed, 0 files unresolved (changeset !)
1400 1400 $ hg merge 'desc("o-1")'
1401 1401 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1402 1402 (branch merge, don't forget to commit)
1403 1403 $ hg ci -m "mFG,Om: $case_desc"
1404 1404
1405 1405 (merge variant 2)
1406 1406
1407 1407 $ hg up 'desc("o-1")'
1408 1408 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
1409 1409 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
1410 1410 $ hg merge 'desc("FGm")'
1411 1411 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
1412 1412 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
1413 1413 (branch merge, don't forget to commit)
1414 1414 $ hg ci -m "mO,FGm: $case_desc"
1415 1415 created new head
1416 1416
1417 1417 (merge variant 3)
1418 1418
1419 1419 $ hg up 'desc("mGFm")'
1420 1420 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1421 1421 $ hg merge 'desc("n-1")'
1422 1422 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1423 1423 (branch merge, don't forget to commit)
1424 1424 $ hg ci -m "mGF,Nm: $case_desc"
1425 1425
1426 1426 (merge variant 4)
1427 1427
1428 1428 $ hg up 'desc("n-1")'
1429 1429 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1430 1430 $ hg merge 'desc("mGFm")'
1431 1431 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1432 1432 (branch merge, don't forget to commit)
1433 1433 $ hg ci -m "mN,GFm: $case_desc"
1434 1434 created new head
1435 1435
1436 1436 $ hg log -G --rev '::(desc("mFG,Om") + desc("mO,FGm") + desc("mGF,Nm") + desc("mN,GFm"))'
1437 1437 @ mN,GFm: chained merges (copy-overwrite -> simple) - same content
1438 1438 |\
1439 1439 +---o mGF,Nm: chained merges (copy-overwrite -> simple) - same content
1440 1440 | |/
1441 1441 | | o mO,FGm: chained merges (copy-overwrite -> simple) - same content
1442 1442 | | |\
1443 1443 | | +---o mFG,Om: chained merges (copy-overwrite -> simple) - same content
1444 1444 | | | |/
1445 1445 | | | o o-1: unrelated changes (based on "g" changes)
1446 1446 | | | |
1447 1447 | o | | n-1: unrelated changes (based on the "f" series of changes)
1448 1448 | | | |
1449 1449 o-----+ mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
1450 1450 |/ / /
1451 1451 | o / mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
1452 1452 |/|/
1453 1453 | o g-1: update d
1454 1454 | |
1455 1455 o | f-2: rename i -> d
1456 1456 | |
1457 1457 o | f-1: rename h -> i
1458 1458 |/
1459 1459 o i-2: c -move-> d, s -move-> t
1460 1460 |
1461 1461 o i-1: a -move-> c, p -move-> s
1462 1462 |
1463 1463 o i-0 initial commit: a b h p q r
1464 1464
1465 1465
1466 1466 Subcase: chaining conflicting rename resolution, with extra change during the merge
1467 1467 ```````````````````````````````````````````````````````````````````````````````````
1468 1468
1469 1469 The "mEA-change-m-0" and "mAE-change-m-0" case create a rename tracking conflict on file 'f'. We
1470 1470 add more change on the respective branch and merge again. These second merge
1471 1471 does not involve the file 'f' and the arbitration done within "mAEm" and "mEA"
1472 1472 about that file should stay unchanged.
1473 1473
1474 1474 $ case_desc="chained merges (conflict+change -> simple) - same content on both branch in the initial merge"
1475 1475
1476 1476
1477 1477 (merge variant 1)
1478 1478
1479 1479 $ hg up 'desc("mAE-change-m")'
1480 1480 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
1481 1481 $ hg merge 'desc("k-1")'
1482 1482 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1483 1483 (branch merge, don't forget to commit)
1484 1484 $ hg ci -m "mAE-change,Km: $case_desc"
1485 1485
1486 1486 (merge variant 2)
1487 1487
1488 1488 $ hg up 'desc("k-1")'
1489 1489 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1490 1490
1491 1491 $ hg merge 'desc("mAE-change-m")'
1492 1492 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1493 1493 (branch merge, don't forget to commit)
1494 1494 $ hg ci -m "mK,AE-change-m: $case_desc"
1495 1495 created new head
1496 1496
1497 1497 (merge variant 3)
1498 1498
1499 1499 $ hg up 'desc("mEA-change-m")'
1500 1500 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1501 1501 $ hg merge 'desc("j-1")'
1502 1502 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1503 1503 (branch merge, don't forget to commit)
1504 1504 $ hg ci -m "mEA-change,Jm: $case_desc"
1505 1505
1506 1506 (merge variant 4)
1507 1507
1508 1508 $ hg up 'desc("j-1")'
1509 1509 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1510 1510 $ hg merge 'desc("mEA-change-m")'
1511 1511 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1512 1512 (branch merge, don't forget to commit)
1513 1513 $ hg ci -m "mJ,EA-change-m: $case_desc"
1514 1514 created new head
1515 1515
1516 1516
1517 1517 $ hg log -G --rev '::(desc("mAE-change,Km") + desc("mK,AE-change-m") + desc("mEA-change,Jm") + desc("mJ,EA-change-m"))'
1518 1518 @ mJ,EA-change-m: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1519 1519 |\
1520 1520 +---o mEA-change,Jm: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1521 1521 | |/
1522 1522 | | o mK,AE-change-m: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1523 1523 | | |\
1524 1524 | | +---o mAE-change,Km: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1525 1525 | | | |/
1526 1526 | | | o k-1: unrelated changes (based on "e" changes)
1527 1527 | | | |
1528 1528 | o | | j-1: unrelated changes (based on the "a" series of changes)
1529 1529 | | | |
1530 1530 o-----+ mEA-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - the other way
1531 1531 |/ / /
1532 1532 | o / mAE-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - one way
1533 1533 |/|/
1534 1534 | o e-2 g -move-> f
1535 1535 | |
1536 1536 | o e-1 b -move-> g
1537 1537 | |
1538 1538 o | a-2: e -move-> f
1539 1539 | |
1540 1540 o | a-1: d -move-> e
1541 1541 |/
1542 1542 o i-2: c -move-> d, s -move-> t
1543 1543 |
1544 1544 o i-1: a -move-> c, p -move-> s
1545 1545 |
1546 1546 o i-0 initial commit: a b h p q r
1547 1547
1548 1548
1549 1549 Summary of all created cases
1550 1550 ----------------------------
1551 1551
1552 1552 $ hg up --quiet null
1553 1553
1554 1554 (This exists to help keeping a compact list of the various cases we have built)
1555 1555
1556 1556 $ hg log -T '{desc|firstline}\n'| sort
1557 1557 a-1: d -move-> e
1558 1558 a-2: e -move-> f
1559 1559 b-1: b update
1560 1560 c-1 delete d
1561 1561 d-1 delete d
1562 1562 d-2 re-add d
1563 1563 e-1 b -move-> g
1564 1564 e-2 g -move-> f
1565 1565 f-1: rename h -> i
1566 1566 f-2: rename i -> d
1567 1567 g-1: update d
1568 1568 h-1: b -(move)-> d
1569 1569 i-0 initial commit: a b h p q r
1570 1570 i-1: a -move-> c, p -move-> s
1571 1571 i-2: c -move-> d, s -move-> t
1572 1572 j-1: unrelated changes (based on the "a" series of changes)
1573 1573 k-1: unrelated changes (based on "e" changes)
1574 1574 l-1: unrelated changes (based on "c" changes)
1575 1575 mABm-0 simple merge - A side: multiple renames, B side: unrelated update - the other way
1576 1576 mAE,Km: chained merges (conflict -> simple) - same content everywhere
1577 1577 mAE-change,Km: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1578 1578 mAE-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - one way
1579 1579 mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
1580 1580 mBAm-0 simple merge - A side: multiple renames, B side: unrelated update - one way
1581 1581 mBC+revert,Lm: chained merges (salvaged -> simple) - same content (when the file exists)
1582 1582 mBC-change-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
1583 1583 mBC-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
1584 1584 mBCm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - one way
1585 1585 mBCm-1 re-add d
1586 1586 mBDm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - one way
1587 1587 mBF-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
1588 1588 mBFm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
1589 1589 mBRm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - one way
1590 1590 mCB+revert,Lm: chained merges (salvaged -> simple) - same content (when the file exists)
1591 1591 mCB-change-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
1592 1592 mCB-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
1593 1593 mCBm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - the other way
1594 1594 mCBm-1 re-add d
1595 1595 mCGm-0 merge updated/deleted - revive the file (updated content) - one way
1596 1596 mCH-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - one way
1597 1597 mDBm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - the other way
1598 1598 mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
1599 1599 mEA,Jm: chained merges (conflict -> simple) - same content everywhere
1600 1600 mEA-change,Jm: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1601 1601 mEA-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - the other way
1602 1602 mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
1603 1603 mFB-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
1604 1604 mFBm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
1605 1605 mFG,Om: chained merges (copy-overwrite -> simple) - same content
1606 1606 mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
1607 1607 mGCm-0 merge updated/deleted - revive the file (updated content) - the other way
1608 1608 mGDm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - the other way
1609 1609 mGF,Nm: chained merges (copy-overwrite -> simple) - same content
1610 1610 mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
1611 1611 mHC-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - the other way
1612 1612 mJ,EA-change-m: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1613 1613 mJ,EAm: chained merges (conflict -> simple) - same content everywhere
1614 1614 mK,AE-change-m: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1615 1615 mK,AEm: chained merges (conflict -> simple) - same content everywhere
1616 1616 mL,BC+revertm: chained merges (salvaged -> simple) - same content (when the file exists)
1617 1617 mL,CB+revertm: chained merges (salvaged -> simple) - same content (when the file exists)
1618 1618 mN,GFm: chained merges (copy-overwrite -> simple) - same content
1619 1619 mO,FGm: chained merges (copy-overwrite -> simple) - same content
1620 1620 mPQ,Tm: chained merges (conflict -> simple) - different content
1621 1621 mPQm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - one way
1622 1622 mQP,Sm: chained merges (conflict -> simple) - different content
1623 1623 mQPm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - the other way
1624 1624 mRBm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - the other way
1625 1625 mS,QPm: chained merges (conflict -> simple) - different content
1626 1626 mT,PQm: chained merges (conflict -> simple) - different content
1627 1627 n-1: unrelated changes (based on the "f" series of changes)
1628 1628 o-1: unrelated changes (based on "g" changes)
1629 1629 p-1: t -move-> u
1630 1630 p-2: u -move-> v
1631 1631 q-1 r -move-> w
1632 1632 q-2 w -move-> v
1633 1633 r-1: rename r -> x
1634 1634 r-2: rename t -> x
1635 1635 s-1: unrelated changes (based on the "p" series of changes)
1636 1636 t-1: unrelated changes (based on "q" changes)
1637 1637
1638 1638
1639 1639 Test that sidedata computations during upgrades are correct
1640 1640 ===========================================================
1641 1641
1642 1642 We upgrade a repository that is not using sidedata (the filelog case) and
1643 1643 check that the same side data have been generated as if they were computed at
1644 1644 commit time.
1645 1645
1646 1646
1647 1647 #if upgraded
1648 1648 $ cat >> $HGRCPATH << EOF
1649 1649 > [format]
1650 1650 > exp-use-copies-side-data-changeset = yes
1651 1651 > EOF
1652 1652 $ hg debugformat -v
1653 1653 format-variant repo config default
1654 1654 fncache: yes yes yes
1655 dirstate-v2: no no no
1655 1656 dotencode: yes yes yes
1656 1657 generaldelta: yes yes yes
1657 1658 share-safe: no no no
1658 1659 sparserevlog: yes yes yes
1659 1660 persistent-nodemap: no no no (no-rust !)
1660 1661 persistent-nodemap: yes yes no (rust !)
1661 1662 copies-sdc: no yes no
1662 1663 revlog-v2: no no no
1663 1664 changelog-v2: no yes no
1664 1665 plain-cl-delta: yes yes yes
1665 1666 compression: * (glob)
1666 1667 compression-level: default default default
1667 1668 $ hg debugupgraderepo --run --quiet
1668 1669 upgrade will perform the following actions:
1669 1670
1670 1671 requirements
1671 1672 preserved: * (glob)
1672 1673 added: exp-changelog-v2, exp-copies-sidedata-changeset
1673 1674
1674 1675 processed revlogs:
1675 1676 - all-filelogs
1676 1677 - changelog
1677 1678 - manifest
1678 1679
1679 1680 #endif
1680 1681
1681 1682 #if upgraded-parallel
1682 1683 $ cat >> $HGRCPATH << EOF
1683 1684 > [format]
1684 1685 > exp-use-copies-side-data-changeset = yes
1685 1686 > [experimental]
1686 1687 > worker.repository-upgrade=yes
1687 1688 > [worker]
1688 1689 > enabled=yes
1689 1690 > numcpus=8
1690 1691 > EOF
1691 1692 $ hg debugformat -v
1692 1693 format-variant repo config default
1693 1694 fncache: yes yes yes
1695 dirstate-v2: no no no
1694 1696 dotencode: yes yes yes
1695 1697 generaldelta: yes yes yes
1696 1698 share-safe: no no no
1697 1699 sparserevlog: yes yes yes
1698 1700 persistent-nodemap: no no no (no-rust !)
1699 1701 persistent-nodemap: yes yes no (rust !)
1700 1702 copies-sdc: no yes no
1701 1703 revlog-v2: no no no
1702 1704 changelog-v2: no yes no
1703 1705 plain-cl-delta: yes yes yes
1704 1706 compression: * (glob)
1705 1707 compression-level: default default default
1706 1708 $ hg debugupgraderepo --run --quiet
1707 1709 upgrade will perform the following actions:
1708 1710
1709 1711 requirements
1710 1712 preserved: * (glob)
1711 1713 added: exp-changelog-v2, exp-copies-sidedata-changeset
1712 1714
1713 1715 processed revlogs:
1714 1716 - all-filelogs
1715 1717 - changelog
1716 1718 - manifest
1717 1719
1718 1720 #endif
1719 1721
1720 1722 #if pull
1721 1723 $ cd ..
1722 1724 $ mv repo-chain repo-source
1723 1725 $ hg init repo-chain
1724 1726 $ cd repo-chain
1725 1727 $ hg pull ../repo-source
1726 1728 pulling from ../repo-source
1727 1729 requesting all changes
1728 1730 adding changesets
1729 1731 adding manifests
1730 1732 adding file changes
1731 1733 added 80 changesets with 44 changes to 25 files (+39 heads)
1732 1734 new changesets a3a31bbefea6:908ce9259ffa
1733 1735 (run 'hg heads' to see heads, 'hg merge' to merge)
1734 1736 #endif
1735 1737
1736 1738 #if pull-upgrade
1737 1739 $ cat >> $HGRCPATH << EOF
1738 1740 > [format]
1739 1741 > exp-use-copies-side-data-changeset = yes
1740 1742 > [experimental]
1741 1743 > changegroup4 = yes
1742 1744 > EOF
1743 1745 $ cd ..
1744 1746 $ mv repo-chain repo-source
1745 1747 $ hg init repo-chain
1746 1748 $ cd repo-chain
1747 1749 $ hg pull ../repo-source
1748 1750 pulling from ../repo-source
1749 1751 requesting all changes
1750 1752 adding changesets
1751 1753 adding manifests
1752 1754 adding file changes
1753 1755 added 80 changesets with 44 changes to 25 files (+39 heads)
1754 1756 new changesets a3a31bbefea6:908ce9259ffa
1755 1757 (run 'hg heads' to see heads, 'hg merge' to merge)
1756 1758 #endif
1757 1759
1758 1760 #if push
1759 1761 $ cd ..
1760 1762 $ mv repo-chain repo-source
1761 1763 $ hg init repo-chain
1762 1764 $ cd repo-source
1763 1765 $ hg push ../repo-chain
1764 1766 pushing to ../repo-chain
1765 1767 searching for changes
1766 1768 adding changesets
1767 1769 adding manifests
1768 1770 adding file changes
1769 1771 added 80 changesets with 44 changes to 25 files (+39 heads)
1770 1772 $ cd ../repo-chain
1771 1773 #endif
1772 1774
1773 1775 #if push-upgrade
1774 1776 $ cat >> $HGRCPATH << EOF
1775 1777 > [format]
1776 1778 > exp-use-copies-side-data-changeset = yes
1777 1779 > [experimental]
1778 1780 > changegroup4 = yes
1779 1781 > EOF
1780 1782 $ cd ..
1781 1783 $ mv repo-chain repo-source
1782 1784 $ hg init repo-chain
1783 1785 $ cd repo-source
1784 1786 $ hg push ../repo-chain
1785 1787 pushing to ../repo-chain
1786 1788 searching for changes
1787 1789 adding changesets
1788 1790 adding manifests
1789 1791 adding file changes
1790 1792 added 80 changesets with 44 changes to 25 files (+39 heads)
1791 1793 $ cd ../repo-chain
1792 1794 #endif
1793 1795
1794 1796 #if no-compatibility no-filelog no-changeset
1795 1797
1796 1798 $ hg debugchangedfiles --compute 0
1797 1799 added : a, ;
1798 1800 added : b, ;
1799 1801 added : h, ;
1800 1802 added : p, ;
1801 1803 added : q, ;
1802 1804 added : r, ;
1803 1805
1804 1806 $ for rev in `hg log --rev 'all()' -T '{rev}\n'`; do
1805 1807 > case_id=`hg log -r $rev -T '{word(0, desc, ":")}\n'`
1806 1808 > echo "##### revision \"$case_id\" #####"
1807 1809 > hg debugsidedata -c -v -- $rev
1808 1810 > hg debugchangedfiles $rev
1809 1811 > done
1810 1812 ##### revision "i-0 initial commit" #####
1811 1813 1 sidedata entries
1812 1814 entry-0014 size 64
1813 1815 '\x00\x00\x00\x06\x04\x00\x00\x00\x01\x00\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x00\x04\x00\x00\x00\x03\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x00\x04\x00\x00\x00\x06\x00\x00\x00\x00abhpqr'
1814 1816 added : a, ;
1815 1817 added : b, ;
1816 1818 added : h, ;
1817 1819 added : p, ;
1818 1820 added : q, ;
1819 1821 added : r, ;
1820 1822 ##### revision "i-1" #####
1821 1823 1 sidedata entries
1822 1824 entry-0014 size 44
1823 1825 '\x00\x00\x00\x04\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00\x0c\x00\x00\x00\x03\x00\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\x02acps'
1824 1826 removed : a, ;
1825 1827 added p1: c, a;
1826 1828 removed : p, ;
1827 1829 added p1: s, p;
1828 1830 ##### revision "i-2" #####
1829 1831 1 sidedata entries
1830 1832 entry-0014 size 44
1831 1833 '\x00\x00\x00\x04\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00\x0c\x00\x00\x00\x03\x00\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\x02cdst'
1832 1834 removed : c, ;
1833 1835 added p1: d, c;
1834 1836 removed : s, ;
1835 1837 added p1: t, s;
1836 1838 ##### revision "a-1" #####
1837 1839 1 sidedata entries
1838 1840 entry-0014 size 24
1839 1841 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00de'
1840 1842 removed : d, ;
1841 1843 added p1: e, d;
1842 1844 ##### revision "a-2" #####
1843 1845 1 sidedata entries
1844 1846 entry-0014 size 24
1845 1847 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00ef'
1846 1848 removed : e, ;
1847 1849 added p1: f, e;
1848 1850 ##### revision "b-1" #####
1849 1851 1 sidedata entries
1850 1852 entry-0014 size 14
1851 1853 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00b'
1852 1854 touched : b, ;
1853 1855 ##### revision "c-1 delete d" #####
1854 1856 1 sidedata entries
1855 1857 entry-0014 size 14
1856 1858 '\x00\x00\x00\x01\x0c\x00\x00\x00\x01\x00\x00\x00\x00d'
1857 1859 removed : d, ;
1858 1860 ##### revision "d-1 delete d" #####
1859 1861 1 sidedata entries
1860 1862 entry-0014 size 14
1861 1863 '\x00\x00\x00\x01\x0c\x00\x00\x00\x01\x00\x00\x00\x00d'
1862 1864 removed : d, ;
1863 1865 ##### revision "d-2 re-add d" #####
1864 1866 1 sidedata entries
1865 1867 entry-0014 size 14
1866 1868 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
1867 1869 added : d, ;
1868 1870 ##### revision "e-1 b -move-> g" #####
1869 1871 1 sidedata entries
1870 1872 entry-0014 size 24
1871 1873 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00bg'
1872 1874 removed : b, ;
1873 1875 added p1: g, b;
1874 1876 ##### revision "e-2 g -move-> f" #####
1875 1877 1 sidedata entries
1876 1878 entry-0014 size 24
1877 1879 '\x00\x00\x00\x02\x06\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00fg'
1878 1880 added p1: f, g;
1879 1881 removed : g, ;
1880 1882 ##### revision "p-1" #####
1881 1883 1 sidedata entries
1882 1884 entry-0014 size 24
1883 1885 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00tu'
1884 1886 removed : t, ;
1885 1887 added p1: u, t;
1886 1888 ##### revision "p-2" #####
1887 1889 1 sidedata entries
1888 1890 entry-0014 size 24
1889 1891 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00uv'
1890 1892 removed : u, ;
1891 1893 added p1: v, u;
1892 1894 ##### revision "q-1 r -move-> w" #####
1893 1895 1 sidedata entries
1894 1896 entry-0014 size 24
1895 1897 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00rw'
1896 1898 removed : r, ;
1897 1899 added p1: w, r;
1898 1900 ##### revision "q-2 w -move-> v" #####
1899 1901 1 sidedata entries
1900 1902 entry-0014 size 24
1901 1903 '\x00\x00\x00\x02\x06\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00vw'
1902 1904 added p1: v, w;
1903 1905 removed : w, ;
1904 1906 ##### revision "mBAm-0 simple merge - A side" #####
1905 1907 1 sidedata entries
1906 1908 entry-0014 size 4
1907 1909 '\x00\x00\x00\x00'
1908 1910 ##### revision "mABm-0 simple merge - A side" #####
1909 1911 1 sidedata entries
1910 1912 entry-0014 size 4
1911 1913 '\x00\x00\x00\x00'
1912 1914 ##### revision "mBCm-0 simple merge - C side" #####
1913 1915 1 sidedata entries
1914 1916 entry-0014 size 4
1915 1917 '\x00\x00\x00\x00'
1916 1918 ##### revision "mBCm-1 re-add d" #####
1917 1919 1 sidedata entries
1918 1920 entry-0014 size 14
1919 1921 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
1920 1922 added : d, ;
1921 1923 ##### revision "mCBm-0 simple merge - C side" #####
1922 1924 1 sidedata entries
1923 1925 entry-0014 size 4
1924 1926 '\x00\x00\x00\x00'
1925 1927 ##### revision "mCBm-1 re-add d" #####
1926 1928 1 sidedata entries
1927 1929 entry-0014 size 14
1928 1930 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
1929 1931 added : d, ;
1930 1932 ##### revision "mBDm-0 simple merge - B side" #####
1931 1933 1 sidedata entries
1932 1934 entry-0014 size 4
1933 1935 '\x00\x00\x00\x00'
1934 1936 ##### revision "mDBm-0 simple merge - B side" #####
1935 1937 1 sidedata entries
1936 1938 entry-0014 size 4
1937 1939 '\x00\x00\x00\x00'
1938 1940 ##### revision "mAEm-0 merge with copies info on both side - A side" #####
1939 1941 1 sidedata entries
1940 1942 entry-0014 size 14
1941 1943 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
1942 1944 merged : f, ;
1943 1945 ##### revision "mEAm-0 merge with copies info on both side - A side" #####
1944 1946 1 sidedata entries
1945 1947 entry-0014 size 14
1946 1948 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
1947 1949 merged : f, ;
1948 1950 ##### revision "mPQm-0 merge with copies info on both side - P side" #####
1949 1951 1 sidedata entries
1950 1952 entry-0014 size 14
1951 1953 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00v'
1952 1954 merged : v, ;
1953 1955 ##### revision "mQPm-0 merge with copies info on both side - P side" #####
1954 1956 1 sidedata entries
1955 1957 entry-0014 size 14
1956 1958 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00v'
1957 1959 merged : v, ;
1958 1960 ##### revision "f-1" #####
1959 1961 1 sidedata entries
1960 1962 entry-0014 size 24
1961 1963 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00hi'
1962 1964 removed : h, ;
1963 1965 added p1: i, h;
1964 1966 ##### revision "f-2" #####
1965 1967 1 sidedata entries
1966 1968 entry-0014 size 24
1967 1969 '\x00\x00\x00\x02\x16\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00di'
1968 1970 touched p1: d, i;
1969 1971 removed : i, ;
1970 1972 ##### revision "mBFm-0 simple merge - B side" #####
1971 1973 1 sidedata entries
1972 1974 entry-0014 size 4
1973 1975 '\x00\x00\x00\x00'
1974 1976 ##### revision "mFBm-0 simple merge - B side" #####
1975 1977 1 sidedata entries
1976 1978 entry-0014 size 4
1977 1979 '\x00\x00\x00\x00'
1978 1980 ##### revision "r-1" #####
1979 1981 1 sidedata entries
1980 1982 entry-0014 size 24
1981 1983 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00rx'
1982 1984 removed : r, ;
1983 1985 added p1: x, r;
1984 1986 ##### revision "r-2" #####
1985 1987 1 sidedata entries
1986 1988 entry-0014 size 24
1987 1989 '\x00\x00\x00\x02\x16\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00tx'
1988 1990 touched p1: t, x;
1989 1991 removed : x, ;
1990 1992 ##### revision "mBRm-0 simple merge - B side" #####
1991 1993 1 sidedata entries
1992 1994 entry-0014 size 4
1993 1995 '\x00\x00\x00\x00'
1994 1996 ##### revision "mRBm-0 simple merge - B side" #####
1995 1997 1 sidedata entries
1996 1998 entry-0014 size 4
1997 1999 '\x00\x00\x00\x00'
1998 2000 ##### revision "g-1" #####
1999 2001 1 sidedata entries
2000 2002 entry-0014 size 14
2001 2003 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00d'
2002 2004 touched : d, ;
2003 2005 ##### revision "mDGm-0 actual content merge, copies on one side - D side" #####
2004 2006 1 sidedata entries
2005 2007 entry-0014 size 14
2006 2008 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
2007 2009 merged : d, ;
2008 2010 ##### revision "mGDm-0 actual content merge, copies on one side - D side" #####
2009 2011 1 sidedata entries
2010 2012 entry-0014 size 14
2011 2013 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
2012 2014 merged : d, ;
2013 2015 ##### revision "mFGm-0 merge - G side" #####
2014 2016 1 sidedata entries
2015 2017 entry-0014 size 14
2016 2018 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
2017 2019 merged : d, ;
2018 2020 ##### revision "mGFm-0 merge - G side" #####
2019 2021 1 sidedata entries
2020 2022 entry-0014 size 14
2021 2023 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
2022 2024 merged : d, ;
2023 2025 ##### revision "mCGm-0 merge updated/deleted - revive the file (updated content) - one way" #####
2024 2026 1 sidedata entries
2025 2027 entry-0014 size 14
2026 2028 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
2027 2029 salvaged : d, ;
2028 2030 ##### revision "mGCm-0 merge updated/deleted - revive the file (updated content) - the other way" #####
2029 2031 1 sidedata entries
2030 2032 entry-0014 size 14
2031 2033 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
2032 2034 salvaged : d, ;
2033 2035 ##### revision "mCB-revert-m-0 merge explicitely revive deleted file - B side" #####
2034 2036 1 sidedata entries
2035 2037 entry-0014 size 14
2036 2038 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
2037 2039 salvaged : d, ;
2038 2040 ##### revision "mBC-revert-m-0 merge explicitely revive deleted file - B side" #####
2039 2041 1 sidedata entries
2040 2042 entry-0014 size 14
2041 2043 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
2042 2044 salvaged : d, ;
2043 2045 ##### revision "h-1" #####
2044 2046 1 sidedata entries
2045 2047 entry-0014 size 24
2046 2048 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00bd'
2047 2049 removed : b, ;
2048 2050 added p1: d, b;
2049 2051 ##### revision "mCH-delete-before-conflict-m-0 simple merge - C side" #####
2050 2052 1 sidedata entries
2051 2053 entry-0014 size 4
2052 2054 '\x00\x00\x00\x00'
2053 2055 ##### revision "mHC-delete-before-conflict-m-0 simple merge - C side" #####
2054 2056 1 sidedata entries
2055 2057 entry-0014 size 4
2056 2058 '\x00\x00\x00\x00'
2057 2059 ##### revision "mAE-change-m-0 merge with file update and copies info on both side - A side" #####
2058 2060 1 sidedata entries
2059 2061 entry-0014 size 14
2060 2062 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
2061 2063 merged : f, ;
2062 2064 ##### revision "mEA-change-m-0 merge with file update and copies info on both side - A side" #####
2063 2065 1 sidedata entries
2064 2066 entry-0014 size 14
2065 2067 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
2066 2068 merged : f, ;
2067 2069 ##### revision "mBF-change-m-0 merge with extra change - B side" #####
2068 2070 1 sidedata entries
2069 2071 entry-0014 size 14
2070 2072 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00d'
2071 2073 touched : d, ;
2072 2074 ##### revision "mFB-change-m-0 merge with extra change - B side" #####
2073 2075 1 sidedata entries
2074 2076 entry-0014 size 14
2075 2077 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00d'
2076 2078 touched : d, ;
2077 2079 ##### revision "mCB-change-m-0 merge explicitely revive deleted file - B side" #####
2078 2080 1 sidedata entries
2079 2081 entry-0014 size 14
2080 2082 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
2081 2083 salvaged : d, ;
2082 2084 ##### revision "mBC-change-m-0 merge explicitely revive deleted file - B side" #####
2083 2085 1 sidedata entries
2084 2086 entry-0014 size 14
2085 2087 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
2086 2088 salvaged : d, ;
2087 2089 ##### revision "j-1" #####
2088 2090 1 sidedata entries
2089 2091 entry-0014 size 24
2090 2092 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-j'
2091 2093 added : unrelated-j, ;
2092 2094 ##### revision "k-1" #####
2093 2095 1 sidedata entries
2094 2096 entry-0014 size 24
2095 2097 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-k'
2096 2098 added : unrelated-k, ;
2097 2099 ##### revision "mAE,Km" #####
2098 2100 1 sidedata entries
2099 2101 entry-0014 size 4
2100 2102 '\x00\x00\x00\x00'
2101 2103 ##### revision "mK,AEm" #####
2102 2104 1 sidedata entries
2103 2105 entry-0014 size 4
2104 2106 '\x00\x00\x00\x00'
2105 2107 ##### revision "mEA,Jm" #####
2106 2108 1 sidedata entries
2107 2109 entry-0014 size 24
2108 2110 '\x00\x00\x00\x01\x14\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-j'
2109 2111 touched : unrelated-j, ;
2110 2112 ##### revision "mJ,EAm" #####
2111 2113 1 sidedata entries
2112 2114 entry-0014 size 24
2113 2115 '\x00\x00\x00\x01\x14\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-j'
2114 2116 touched : unrelated-j, ;
2115 2117 ##### revision "s-1" #####
2116 2118 1 sidedata entries
2117 2119 entry-0014 size 24
2118 2120 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-s'
2119 2121 added : unrelated-s, ;
2120 2122 ##### revision "t-1" #####
2121 2123 1 sidedata entries
2122 2124 entry-0014 size 24
2123 2125 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-t'
2124 2126 added : unrelated-t, ;
2125 2127 ##### revision "mPQ,Tm" #####
2126 2128 1 sidedata entries
2127 2129 entry-0014 size 4
2128 2130 '\x00\x00\x00\x00'
2129 2131 ##### revision "mT,PQm" #####
2130 2132 1 sidedata entries
2131 2133 entry-0014 size 4
2132 2134 '\x00\x00\x00\x00'
2133 2135 ##### revision "mQP,Sm" #####
2134 2136 1 sidedata entries
2135 2137 entry-0014 size 4
2136 2138 '\x00\x00\x00\x00'
2137 2139 ##### revision "mS,QPm" #####
2138 2140 1 sidedata entries
2139 2141 entry-0014 size 4
2140 2142 '\x00\x00\x00\x00'
2141 2143 ##### revision "l-1" #####
2142 2144 1 sidedata entries
2143 2145 entry-0014 size 24
2144 2146 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-l'
2145 2147 added : unrelated-l, ;
2146 2148 ##### revision "mBC+revert,Lm" #####
2147 2149 1 sidedata entries
2148 2150 entry-0014 size 4
2149 2151 '\x00\x00\x00\x00'
2150 2152 ##### revision "mCB+revert,Lm" #####
2151 2153 1 sidedata entries
2152 2154 entry-0014 size 4
2153 2155 '\x00\x00\x00\x00'
2154 2156 ##### revision "mL,BC+revertm" #####
2155 2157 1 sidedata entries
2156 2158 entry-0014 size 4
2157 2159 '\x00\x00\x00\x00'
2158 2160 ##### revision "mL,CB+revertm" #####
2159 2161 1 sidedata entries
2160 2162 entry-0014 size 4
2161 2163 '\x00\x00\x00\x00'
2162 2164 ##### revision "n-1" #####
2163 2165 1 sidedata entries
2164 2166 entry-0014 size 24
2165 2167 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-n'
2166 2168 added : unrelated-n, ;
2167 2169 ##### revision "o-1" #####
2168 2170 1 sidedata entries
2169 2171 entry-0014 size 24
2170 2172 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-o'
2171 2173 added : unrelated-o, ;
2172 2174 ##### revision "mFG,Om" #####
2173 2175 1 sidedata entries
2174 2176 entry-0014 size 4
2175 2177 '\x00\x00\x00\x00'
2176 2178 ##### revision "mO,FGm" #####
2177 2179 1 sidedata entries
2178 2180 entry-0014 size 4
2179 2181 '\x00\x00\x00\x00'
2180 2182 ##### revision "mGF,Nm" #####
2181 2183 1 sidedata entries
2182 2184 entry-0014 size 4
2183 2185 '\x00\x00\x00\x00'
2184 2186 ##### revision "mN,GFm" #####
2185 2187 1 sidedata entries
2186 2188 entry-0014 size 4
2187 2189 '\x00\x00\x00\x00'
2188 2190 ##### revision "mAE-change,Km" #####
2189 2191 1 sidedata entries
2190 2192 entry-0014 size 4
2191 2193 '\x00\x00\x00\x00'
2192 2194 ##### revision "mK,AE-change-m" #####
2193 2195 1 sidedata entries
2194 2196 entry-0014 size 4
2195 2197 '\x00\x00\x00\x00'
2196 2198 ##### revision "mEA-change,Jm" #####
2197 2199 1 sidedata entries
2198 2200 entry-0014 size 4
2199 2201 '\x00\x00\x00\x00'
2200 2202 ##### revision "mJ,EA-change-m" #####
2201 2203 1 sidedata entries
2202 2204 entry-0014 size 4
2203 2205 '\x00\x00\x00\x00'
2204 2206
2205 2207 #endif
2206 2208
2207 2209
2208 2210 Test copy information chaining
2209 2211 ==============================
2210 2212
2211 2213 Check that matching only affect the destination and not intermediate path
2212 2214 -------------------------------------------------------------------------
2213 2215
2214 2216 The two status call should give the same value for f
2215 2217
2216 2218 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("a-2")'
2217 2219 A f
2218 2220 a
2219 2221 A t
2220 2222 p
2221 2223 R a
2222 2224 R p
2223 2225 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("a-2")' f
2224 2226 A f
2225 2227 a (no-changeset no-compatibility !)
2226 2228
2227 2229 merging with unrelated change does not interfere with the renames
2228 2230 ---------------------------------------------------------------
2229 2231
2230 2232 - rename on one side
2231 2233 - unrelated change on the other side
2232 2234
2233 2235 $ hg log -G --rev '::(desc("mABm")+desc("mBAm"))'
2234 2236 o mABm-0 simple merge - A side: multiple renames, B side: unrelated update - the other way
2235 2237 |\
2236 2238 +---o mBAm-0 simple merge - A side: multiple renames, B side: unrelated update - one way
2237 2239 | |/
2238 2240 | o b-1: b update
2239 2241 | |
2240 2242 o | a-2: e -move-> f
2241 2243 | |
2242 2244 o | a-1: d -move-> e
2243 2245 |/
2244 2246 o i-2: c -move-> d, s -move-> t
2245 2247 |
2246 2248 o i-1: a -move-> c, p -move-> s
2247 2249 |
2248 2250 o i-0 initial commit: a b h p q r
2249 2251
2250 2252
2251 2253 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mABm")'
2252 2254 A f
2253 2255 d
2254 2256 R d
2255 2257 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBAm")'
2256 2258 A f
2257 2259 d
2258 2260 R d
2259 2261 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mABm")'
2260 2262 M b
2261 2263 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mBAm")'
2262 2264 M b
2263 2265 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mABm")'
2264 2266 M b
2265 2267 A f
2266 2268 d
2267 2269 R d
2268 2270 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBAm")'
2269 2271 M b
2270 2272 A f
2271 2273 d
2272 2274 R d
2273 2275 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mABm")'
2274 2276 M b
2275 2277 A f
2276 2278 a
2277 2279 A t
2278 2280 p
2279 2281 R a
2280 2282 R p
2281 2283 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBAm")'
2282 2284 M b
2283 2285 A f
2284 2286 a
2285 2287 A t
2286 2288 p
2287 2289 R a
2288 2290 R p
2289 2291
2290 2292 merging with the side having a delete
2291 2293 -------------------------------------
2292 2294
2293 2295 case summary:
2294 2296 - one with change to an unrelated file
2295 2297 - one deleting the change
2296 2298 and recreate an unrelated file after the merge
2297 2299
2298 2300 $ hg log -G --rev '::(desc("mCBm")+desc("mBCm"))'
2299 2301 o mCBm-1 re-add d
2300 2302 |
2301 2303 o mCBm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - the other way
2302 2304 |\
2303 2305 | | o mBCm-1 re-add d
2304 2306 | | |
2305 2307 +---o mBCm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - one way
2306 2308 | |/
2307 2309 | o c-1 delete d
2308 2310 | |
2309 2311 o | b-1: b update
2310 2312 |/
2311 2313 o i-2: c -move-> d, s -move-> t
2312 2314 |
2313 2315 o i-1: a -move-> c, p -move-> s
2314 2316 |
2315 2317 o i-0 initial commit: a b h p q r
2316 2318
2317 2319 - comparing from the merge
2318 2320
2319 2321 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-0")'
2320 2322 R d
2321 2323 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-0")'
2322 2324 R d
2323 2325 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-0")'
2324 2326 M b
2325 2327 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-0")'
2326 2328 M b
2327 2329 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-0")'
2328 2330 M b
2329 2331 R d
2330 2332 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-0")'
2331 2333 M b
2332 2334 R d
2333 2335 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-0")'
2334 2336 M b
2335 2337 A t
2336 2338 p
2337 2339 R a
2338 2340 R p
2339 2341 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-0")'
2340 2342 M b
2341 2343 A t
2342 2344 p
2343 2345 R a
2344 2346 R p
2345 2347
2346 2348 - comparing with the merge children re-adding the file
2347 2349
2348 2350 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-1")'
2349 2351 M d
2350 2352 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-1")'
2351 2353 M d
2352 2354 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-1")'
2353 2355 M b
2354 2356 A d
2355 2357 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-1")'
2356 2358 M b
2357 2359 A d
2358 2360 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-1")'
2359 2361 M b
2360 2362 M d
2361 2363 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-1")'
2362 2364 M b
2363 2365 M d
2364 2366 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-1")'
2365 2367 M b
2366 2368 A d
2367 2369 A t
2368 2370 p
2369 2371 R a
2370 2372 R p
2371 2373 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-1")'
2372 2374 M b
2373 2375 A d
2374 2376 A t
2375 2377 p
2376 2378 R a
2377 2379 R p
2378 2380
2379 2381 Comparing with a merge re-adding the file afterward
2380 2382 ---------------------------------------------------
2381 2383
2382 2384 Merge:
2383 2385 - one with change to an unrelated file
2384 2386 - one deleting and recreating the change
2385 2387
2386 2388 $ hg log -G --rev '::(desc("mDBm")+desc("mBDm"))'
2387 2389 o mDBm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - the other way
2388 2390 |\
2389 2391 +---o mBDm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - one way
2390 2392 | |/
2391 2393 | o d-2 re-add d
2392 2394 | |
2393 2395 | o d-1 delete d
2394 2396 | |
2395 2397 o | b-1: b update
2396 2398 |/
2397 2399 o i-2: c -move-> d, s -move-> t
2398 2400 |
2399 2401 o i-1: a -move-> c, p -move-> s
2400 2402 |
2401 2403 o i-0 initial commit: a b h p q r
2402 2404
2403 2405 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBDm-0")'
2404 2406 M d
2405 2407 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mDBm-0")'
2406 2408 M d
2407 2409 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mBDm-0")'
2408 2410 M b
2409 2411 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDBm-0")'
2410 2412 M b
2411 2413 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBDm-0")'
2412 2414 M b
2413 2415 M d
2414 2416 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mDBm-0")'
2415 2417 M b
2416 2418 M d
2417 2419
2418 2420 The bugs makes recorded copy is different depending of where we started the merge from since
2419 2421
2420 2422 $ hg manifest --debug --rev 'desc("mBDm-0")' | grep '644 d'
2421 2423 b004912a8510032a0350a74daa2803dadfb00e12 644 d
2422 2424 $ hg manifest --debug --rev 'desc("mDBm-0")' | grep '644 d'
2423 2425 b004912a8510032a0350a74daa2803dadfb00e12 644 d
2424 2426
2425 2427 $ hg manifest --debug --rev 'desc("d-2")' | grep '644 d'
2426 2428 b004912a8510032a0350a74daa2803dadfb00e12 644 d
2427 2429 $ hg manifest --debug --rev 'desc("b-1")' | grep '644 d'
2428 2430 d8252ab2e760b0d4e5288fd44cbd15a0fa567e16 644 d (no-changeset !)
2429 2431 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 d (changeset !)
2430 2432 $ hg debugindex d | head -n 4 | "$PYTHON" ../no-linkrev
2431 2433 rev linkrev nodeid p1 p2
2432 2434 0 * d8252ab2e760 000000000000 000000000000 (no-changeset !)
2433 2435 0 * ae258f702dfe 000000000000 000000000000 (changeset !)
2434 2436 1 * b004912a8510 000000000000 000000000000
2435 2437 2 * 7b79e2fe0c89 000000000000 000000000000 (no-changeset !)
2436 2438 2 * 5cce88bf349f ae258f702dfe 000000000000 (changeset !)
2437 2439
2438 2440 Log output should not include a merge commit as it did not happen
2439 2441
2440 2442 $ hg log -Gfr 'desc("mBDm-0")' d
2441 2443 o d-2 re-add d
2442 2444 |
2443 2445 ~
2444 2446
2445 2447 $ hg log -Gfr 'desc("mDBm-0")' d
2446 2448 o d-2 re-add d
2447 2449 |
2448 2450 ~
2449 2451
2450 2452 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBDm-0")'
2451 2453 M b
2452 2454 A d
2453 2455 A t
2454 2456 p
2455 2457 R a
2456 2458 R p
2457 2459 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDBm-0")'
2458 2460 M b
2459 2461 A d
2460 2462 A t
2461 2463 p
2462 2464 R a
2463 2465 R p
2464 2466
2465 2467
2466 2468 Comparing with a merge with colliding rename
2467 2469 --------------------------------------------
2468 2470
2469 2471 Subcase: new copy information on both side
2470 2472 ``````````````````````````````````````````
2471 2473
2472 2474 - the "e-" branch renaming b to f (through 'g')
2473 2475 - the "a-" branch renaming d to f (through e)
2474 2476
2475 2477 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
2476 2478 o mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
2477 2479 |\
2478 2480 +---o mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
2479 2481 | |/
2480 2482 | o e-2 g -move-> f
2481 2483 | |
2482 2484 | o e-1 b -move-> g
2483 2485 | |
2484 2486 o | a-2: e -move-> f
2485 2487 | |
2486 2488 o | a-1: d -move-> e
2487 2489 |/
2488 2490 o i-2: c -move-> d, s -move-> t
2489 2491 |
2490 2492 o i-1: a -move-> c, p -move-> s
2491 2493 |
2492 2494 o i-0 initial commit: a b h p q r
2493 2495
2494 2496 #if no-changeset
2495 2497 $ hg manifest --debug --rev 'desc("mAEm-0")' | grep '644 f'
2496 2498 2ff93c643948464ee1f871867910ae43a45b0bea 644 f
2497 2499 $ hg manifest --debug --rev 'desc("mEAm-0")' | grep '644 f'
2498 2500 2ff93c643948464ee1f871867910ae43a45b0bea 644 f
2499 2501 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
2500 2502 b76eb76580df486c3d51d63c5c210d4dd43a8ac7 644 f
2501 2503 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
2502 2504 e8825b386367b29fec957283a80bb47b47483fe1 644 f
2503 2505 $ hg debugindex f | "$PYTHON" ../no-linkrev
2504 2506 rev linkrev nodeid p1 p2
2505 2507 0 * b76eb76580df 000000000000 000000000000
2506 2508 1 * e8825b386367 000000000000 000000000000
2507 2509 2 * 2ff93c643948 b76eb76580df e8825b386367
2508 2510 3 * 2f649fba7eb2 b76eb76580df e8825b386367
2509 2511 4 * 774e7c1637d5 e8825b386367 b76eb76580df
2510 2512 #else
2511 2513 $ hg manifest --debug --rev 'desc("mAEm-0")' | grep '644 f'
2512 2514 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2513 2515 $ hg manifest --debug --rev 'desc("mEAm-0")' | grep '644 f'
2514 2516 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2515 2517 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
2516 2518 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2517 2519 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
2518 2520 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2519 2521 $ hg debugindex f | "$PYTHON" ../no-linkrev
2520 2522 rev linkrev nodeid p1 p2
2521 2523 0 * ae258f702dfe 000000000000 000000000000
2522 2524 1 * d3613c1ec831 ae258f702dfe 000000000000
2523 2525 2 * 05e03c868bbc ae258f702dfe 000000000000
2524 2526 #endif
2525 2527
2526 2528 # Here the filelog based implementation is not looking at the rename
2527 2529 # information (because the file exist on both side). However the changelog
2528 2530 # based on works fine. We have different output.
2529 2531
2530 2532 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mAEm-0")'
2531 2533 M f (no-changeset !)
2532 2534 b (no-filelog no-changeset !)
2533 2535 R b
2534 2536 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mEAm-0")'
2535 2537 M f (no-changeset !)
2536 2538 b (no-filelog no-changeset !)
2537 2539 R b
2538 2540 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mAEm-0")'
2539 2541 M f (no-changeset !)
2540 2542 d (no-filelog no-changeset !)
2541 2543 R d
2542 2544 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mEAm-0")'
2543 2545 M f (no-changeset !)
2544 2546 d (no-filelog no-changeset !)
2545 2547 R d
2546 2548 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("a-2")'
2547 2549 A f
2548 2550 d
2549 2551 R d
2550 2552 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("e-2")'
2551 2553 A f
2552 2554 b
2553 2555 R b
2554 2556
2555 2557 # From here, we run status against revision where both source file exists.
2556 2558 #
2557 2559 # The filelog based implementation picks an arbitrary side based on revision
2558 2560 # numbers. So the same side "wins" whatever the parents order is. This is
2559 2561 # sub-optimal because depending on revision numbers means the result can be
2560 2562 # different from one repository to the next.
2561 2563 #
2562 2564 # The changeset based algorithm use the parent order to break tie on conflicting
2563 2565 # information and will have a different order depending on who is p1 and p2.
2564 2566 # That order is stable accross repositories. (data from p1 prevails)
2565 2567
2566 2568 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mAEm-0")'
2567 2569 A f
2568 2570 d
2569 2571 R b
2570 2572 R d
2571 2573 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mEAm-0")'
2572 2574 A f
2573 2575 d (filelog !)
2574 2576 b (no-filelog !)
2575 2577 R b
2576 2578 R d
2577 2579 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAEm-0")'
2578 2580 A f
2579 2581 a
2580 2582 A t
2581 2583 p
2582 2584 R a
2583 2585 R b
2584 2586 R p
2585 2587 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEAm-0")'
2586 2588 A f
2587 2589 a (filelog !)
2588 2590 b (no-filelog !)
2589 2591 A t
2590 2592 p
2591 2593 R a
2592 2594 R b
2593 2595 R p
2594 2596
2595 2597
2596 2598 Subcase: existing copy information overwritten on one branch
2597 2599 ````````````````````````````````````````````````````````````
2598 2600
2599 2601 Note:
2600 2602 | In this case, one of the merge wrongly record a merge while there is none.
2601 2603 | This lead to bad copy tracing information to be dug up.
2602 2604
2603 2605
2604 2606 Merge:
2605 2607 - one with change to an unrelated file (b)
2606 2608 - one overwriting a file (d) with a rename (from h to i to d)
2607 2609
2608 2610 $ hg log -G --rev '::(desc("mBFm")+desc("mFBm"))'
2609 2611 o mFBm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
2610 2612 |\
2611 2613 +---o mBFm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
2612 2614 | |/
2613 2615 | o f-2: rename i -> d
2614 2616 | |
2615 2617 | o f-1: rename h -> i
2616 2618 | |
2617 2619 o | b-1: b update
2618 2620 |/
2619 2621 o i-2: c -move-> d, s -move-> t
2620 2622 |
2621 2623 o i-1: a -move-> c, p -move-> s
2622 2624 |
2623 2625 o i-0 initial commit: a b h p q r
2624 2626
2625 2627 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBFm-0")'
2626 2628 M b
2627 2629 A d
2628 2630 h
2629 2631 A t
2630 2632 p
2631 2633 R a
2632 2634 R h
2633 2635 R p
2634 2636 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFBm-0")'
2635 2637 M b
2636 2638 A d
2637 2639 h
2638 2640 A t
2639 2641 p
2640 2642 R a
2641 2643 R h
2642 2644 R p
2643 2645 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBFm-0")'
2644 2646 M d (no-changeset !)
2645 2647 h (no-filelog no-changeset !)
2646 2648 R h
2647 2649 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mBFm-0")'
2648 2650 M b
2649 2651 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mBFm-0")'
2650 2652 M b
2651 2653 M d (no-changeset !)
2652 2654 i (no-filelog no-changeset !)
2653 2655 R i
2654 2656 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mFBm-0")'
2655 2657 M d (no-changeset !)
2656 2658 h (no-filelog no-changeset !)
2657 2659 R h
2658 2660 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFBm-0")'
2659 2661 M b
2660 2662 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFBm-0")'
2661 2663 M b
2662 2664 M d (no-changeset !)
2663 2665 i (no-filelog no-changeset !)
2664 2666 R i
2665 2667
2666 2668 #if no-changeset
2667 2669 $ hg log -Gfr 'desc("mBFm-0")' d
2668 2670 o f-2: rename i -> d
2669 2671 |
2670 2672 o f-1: rename h -> i
2671 2673 :
2672 2674 o i-0 initial commit: a b h p q r
2673 2675
2674 2676 #else
2675 2677 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2676 2678 $ hg log -Gfr 'desc("mBFm-0")' d
2677 2679 o i-2: c -move-> d, s -move-> t
2678 2680 |
2679 2681 ~
2680 2682 #endif
2681 2683
2682 2684 #if no-changeset
2683 2685 $ hg log -Gfr 'desc("mFBm-0")' d
2684 2686 o f-2: rename i -> d
2685 2687 |
2686 2688 o f-1: rename h -> i
2687 2689 :
2688 2690 o i-0 initial commit: a b h p q r
2689 2691
2690 2692 #else
2691 2693 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2692 2694 $ hg log -Gfr 'desc("mFBm-0")' d
2693 2695 o i-2: c -move-> d, s -move-> t
2694 2696 |
2695 2697 ~
2696 2698 #endif
2697 2699
2698 2700
2699 2701 Subcase: existing copy information overwritten on one branch, with different content)
2700 2702 `````````````````````````````````````````````````````````````````````````````````````
2701 2703
2702 2704 Merge:
2703 2705 - one with change to an unrelated file (b)
2704 2706 - one overwriting a file (t) with a rename (from r to x to t), v content is not the same as on the other branch
2705 2707
2706 2708 $ hg log -G --rev '::(desc("mBRm")+desc("mRBm"))'
2707 2709 o mRBm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - the other way
2708 2710 |\
2709 2711 +---o mBRm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - one way
2710 2712 | |/
2711 2713 | o r-2: rename t -> x
2712 2714 | |
2713 2715 | o r-1: rename r -> x
2714 2716 | |
2715 2717 o | b-1: b update
2716 2718 |/
2717 2719 o i-2: c -move-> d, s -move-> t
2718 2720 |
2719 2721 o i-1: a -move-> c, p -move-> s
2720 2722 |
2721 2723 o i-0 initial commit: a b h p q r
2722 2724
2723 2725 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBRm-0")'
2724 2726 M b
2725 2727 A d
2726 2728 a
2727 2729 A t
2728 2730 r
2729 2731 R a
2730 2732 R p
2731 2733 R r
2732 2734 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mRBm-0")'
2733 2735 M b
2734 2736 A d
2735 2737 a
2736 2738 A t
2737 2739 r
2738 2740 R a
2739 2741 R p
2740 2742 R r
2741 2743 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBRm-0")'
2742 2744 M t
2743 2745 r (no-filelog !)
2744 2746 R r
2745 2747 $ hg status --copies --rev 'desc("r-2")' --rev 'desc("mBRm-0")'
2746 2748 M b
2747 2749 $ hg status --copies --rev 'desc("r-1")' --rev 'desc("mBRm-0")'
2748 2750 M b
2749 2751 M t
2750 2752 x (no-filelog !)
2751 2753 R x
2752 2754 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mRBm-0")'
2753 2755 M t
2754 2756 r (no-filelog !)
2755 2757 R r
2756 2758 $ hg status --copies --rev 'desc("r-2")' --rev 'desc("mRBm-0")'
2757 2759 M b
2758 2760 $ hg status --copies --rev 'desc("r-1")' --rev 'desc("mRBm-0")'
2759 2761 M b
2760 2762 M t
2761 2763 x (no-filelog !)
2762 2764 R x
2763 2765
2764 2766 #if no-changeset
2765 2767 $ hg log -Gfr 'desc("mBRm-0")' d
2766 2768 o i-2: c -move-> d, s -move-> t
2767 2769 |
2768 2770 o i-1: a -move-> c, p -move-> s
2769 2771 |
2770 2772 o i-0 initial commit: a b h p q r
2771 2773
2772 2774 #else
2773 2775 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2774 2776 $ hg log -Gfr 'desc("mBRm-0")' d
2775 2777 o i-2: c -move-> d, s -move-> t
2776 2778 |
2777 2779 ~
2778 2780 #endif
2779 2781
2780 2782 #if no-changeset
2781 2783 $ hg log -Gfr 'desc("mRBm-0")' d
2782 2784 o i-2: c -move-> d, s -move-> t
2783 2785 |
2784 2786 o i-1: a -move-> c, p -move-> s
2785 2787 |
2786 2788 o i-0 initial commit: a b h p q r
2787 2789
2788 2790 #else
2789 2791 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2790 2792 $ hg log -Gfr 'desc("mRBm-0")' d
2791 2793 o i-2: c -move-> d, s -move-> t
2792 2794 |
2793 2795 ~
2794 2796 #endif
2795 2797
2796 2798 Subcase: reset of the copy history on one side
2797 2799 ``````````````````````````````````````````````
2798 2800
2799 2801 Merge:
2800 2802 - one with change to a file
2801 2803 - one deleting and recreating the file
2802 2804
2803 2805 Unlike in the 'BD/DB' cases, an actual merge happened here. So we should
2804 2806 consider history and rename on both branch of the merge.
2805 2807
2806 2808 $ hg log -G --rev '::(desc("mDGm")+desc("mGDm"))'
2807 2809 o mGDm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - the other way
2808 2810 |\
2809 2811 +---o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2810 2812 | |/
2811 2813 | o g-1: update d
2812 2814 | |
2813 2815 o | d-2 re-add d
2814 2816 | |
2815 2817 o | d-1 delete d
2816 2818 |/
2817 2819 o i-2: c -move-> d, s -move-> t
2818 2820 |
2819 2821 o i-1: a -move-> c, p -move-> s
2820 2822 |
2821 2823 o i-0 initial commit: a b h p q r
2822 2824
2823 2825 One side of the merge have a long history with rename. The other side of the
2824 2826 merge point to a new file with a smaller history. Each side is "valid".
2825 2827
2826 2828 (and again the filelog based algorithm only explore one, with a pick based on
2827 2829 revision numbers)
2828 2830
2829 2831 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDGm-0")'
2830 2832 A d
2831 2833 a (filelog !)
2832 2834 A t
2833 2835 p
2834 2836 R a
2835 2837 R p
2836 2838 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGDm-0")'
2837 2839 A d
2838 2840 a
2839 2841 A t
2840 2842 p
2841 2843 R a
2842 2844 R p
2843 2845 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDGm-0")'
2844 2846 M d
2845 2847 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mGDm-0")'
2846 2848 M d
2847 2849 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mDGm-0")'
2848 2850 M d
2849 2851 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGDm-0")'
2850 2852 M d
2851 2853
2852 2854 #if no-changeset
2853 2855 $ hg log -Gfr 'desc("mDGm-0")' d
2854 2856 o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2855 2857 |\
2856 2858 | o g-1: update d
2857 2859 | |
2858 2860 o | d-2 re-add d
2859 2861 |/
2860 2862 o i-2: c -move-> d, s -move-> t
2861 2863 |
2862 2864 o i-1: a -move-> c, p -move-> s
2863 2865 |
2864 2866 o i-0 initial commit: a b h p q r
2865 2867
2866 2868 #else
2867 2869 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2868 2870 $ hg log -Gfr 'desc("mDGm-0")' d
2869 2871 o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2870 2872 |\
2871 2873 | o g-1: update d
2872 2874 | |
2873 2875 o | d-2 re-add d
2874 2876 |/
2875 2877 o i-2: c -move-> d, s -move-> t
2876 2878 |
2877 2879 ~
2878 2880 #endif
2879 2881
2880 2882
2881 2883 #if no-changeset
2882 2884 $ hg log -Gfr 'desc("mDGm-0")' d
2883 2885 o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2884 2886 |\
2885 2887 | o g-1: update d
2886 2888 | |
2887 2889 o | d-2 re-add d
2888 2890 |/
2889 2891 o i-2: c -move-> d, s -move-> t
2890 2892 |
2891 2893 o i-1: a -move-> c, p -move-> s
2892 2894 |
2893 2895 o i-0 initial commit: a b h p q r
2894 2896
2895 2897 #else
2896 2898 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2897 2899 $ hg log -Gfr 'desc("mDGm-0")' d
2898 2900 o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2899 2901 |\
2900 2902 | o g-1: update d
2901 2903 | |
2902 2904 o | d-2 re-add d
2903 2905 |/
2904 2906 o i-2: c -move-> d, s -move-> t
2905 2907 |
2906 2908 ~
2907 2909 #endif
2908 2910
2909 2911 Subcase: merging a change to a file with a "copy overwrite" to that file from another branch
2910 2912 ````````````````````````````````````````````````````````````````````````````````````````````
2911 2913
2912 2914 Merge:
2913 2915 - one with change to a file (d)
2914 2916 - one overwriting that file with a rename (from h to i, to d)
2915 2917
2916 2918 This case is similar to BF/FB, but an actual merge happens, so both side of the
2917 2919 history are relevant.
2918 2920
2919 2921
2920 2922 $ hg log -G --rev '::(desc("mGFm")+desc("mFGm"))'
2921 2923 o mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
2922 2924 |\
2923 2925 +---o mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
2924 2926 | |/
2925 2927 | o g-1: update d
2926 2928 | |
2927 2929 o | f-2: rename i -> d
2928 2930 | |
2929 2931 o | f-1: rename h -> i
2930 2932 |/
2931 2933 o i-2: c -move-> d, s -move-> t
2932 2934 |
2933 2935 o i-1: a -move-> c, p -move-> s
2934 2936 |
2935 2937 o i-0 initial commit: a b h p q r
2936 2938
2937 2939
2938 2940 Note:
2939 2941 | In this case, the merge get conflicting information since on one side we have
2940 2942 | "a -> c -> d". and one the other one we have "h -> i -> d".
2941 2943 |
2942 2944 | The current code arbitrarily pick one side depending the ordering of the merged hash:
2943 2945
2944 2946 In this case, the file hash from "f-2" is lower, so it will be `p1` of the resulting filenode its copy tracing information will win (and trace back to "h"):
2945 2947
2946 2948 Details on this hash ordering pick:
2947 2949
2948 2950 $ hg manifest --debug 'desc("g-1")' | egrep 'd$'
2949 2951 17ec97e605773eb44a117d1136b3849bcdc1924f 644 d (no-changeset !)
2950 2952 5cce88bf349f7c742bb440f2c53f81db9c294279 644 d (changeset !)
2951 2953 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("g-1")' d
2952 2954 A d
2953 2955 a (no-changeset no-compatibility !)
2954 2956
2955 2957 $ hg manifest --debug 'desc("f-2")' | egrep 'd$'
2956 2958 7b79e2fe0c8924e0e598a82f048a7b024afa4d96 644 d (no-changeset !)
2957 2959 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 d (changeset !)
2958 2960 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("f-2")' d
2959 2961 A d
2960 2962 h (no-changeset no-compatibility !)
2961 2963
2962 2964 Copy tracing data on the resulting merge:
2963 2965
2964 2966 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFGm-0")'
2965 2967 A d
2966 2968 h (no-filelog !)
2967 2969 a (filelog !)
2968 2970 A t
2969 2971 p
2970 2972 R a
2971 2973 R h
2972 2974 R p
2973 2975 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGFm-0")'
2974 2976 A d
2975 2977 a (no-changeset !)
2976 2978 h (changeset !)
2977 2979 A t
2978 2980 p
2979 2981 R a
2980 2982 R h
2981 2983 R p
2982 2984 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFGm-0")'
2983 2985 M d
2984 2986 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mGFm-0")'
2985 2987 M d
2986 2988 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFGm-0")'
2987 2989 M d
2988 2990 i (no-filelog !)
2989 2991 R i
2990 2992 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mGFm-0")'
2991 2993 M d
2992 2994 i (no-filelog !)
2993 2995 R i
2994 2996 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mFGm-0")'
2995 2997 M d (no-changeset !)
2996 2998 h (no-filelog no-changeset !)
2997 2999 R h
2998 3000 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGFm-0")'
2999 3001 M d (no-changeset !)
3000 3002 h (no-filelog no-changeset !)
3001 3003 R h
3002 3004
3003 3005 #if no-changeset
3004 3006 $ hg log -Gfr 'desc("mFGm-0")' d
3005 3007 o mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
3006 3008 |\
3007 3009 | o g-1: update d
3008 3010 | |
3009 3011 o | f-2: rename i -> d
3010 3012 | |
3011 3013 o | f-1: rename h -> i
3012 3014 |/
3013 3015 o i-2: c -move-> d, s -move-> t
3014 3016 |
3015 3017 o i-1: a -move-> c, p -move-> s
3016 3018 |
3017 3019 o i-0 initial commit: a b h p q r
3018 3020
3019 3021 #else
3020 3022 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
3021 3023 $ hg log -Gfr 'desc("mFGm-0")' d
3022 3024 o g-1: update d
3023 3025 |
3024 3026 o i-2: c -move-> d, s -move-> t
3025 3027 |
3026 3028 ~
3027 3029 #endif
3028 3030
3029 3031 #if no-changeset
3030 3032 $ hg log -Gfr 'desc("mGFm-0")' d
3031 3033 o mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
3032 3034 |\
3033 3035 | o g-1: update d
3034 3036 | |
3035 3037 o | f-2: rename i -> d
3036 3038 | |
3037 3039 o | f-1: rename h -> i
3038 3040 |/
3039 3041 o i-2: c -move-> d, s -move-> t
3040 3042 |
3041 3043 o i-1: a -move-> c, p -move-> s
3042 3044 |
3043 3045 o i-0 initial commit: a b h p q r
3044 3046
3045 3047 #else
3046 3048 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
3047 3049 $ hg log -Gfr 'desc("mGFm-0")' d
3048 3050 o g-1: update d
3049 3051 |
3050 3052 o i-2: c -move-> d, s -move-> t
3051 3053 |
3052 3054 ~
3053 3055 #endif
3054 3056
3055 3057 Subcase: new copy information on both side with an actual merge happening
3056 3058 `````````````````````````````````````````````````````````````````````````
3057 3059
3058 3060 - the "p-" branch renaming 't' to 'v' (through 'u')
3059 3061 - the "q-" branch renaming 'r' to 'v' (through 'w')
3060 3062
3061 3063
3062 3064 $ hg log -G --rev '::(desc("mPQm")+desc("mQPm"))'
3063 3065 o mQPm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - the other way
3064 3066 |\
3065 3067 +---o mPQm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - one way
3066 3068 | |/
3067 3069 | o q-2 w -move-> v
3068 3070 | |
3069 3071 | o q-1 r -move-> w
3070 3072 | |
3071 3073 o | p-2: u -move-> v
3072 3074 | |
3073 3075 o | p-1: t -move-> u
3074 3076 |/
3075 3077 o i-2: c -move-> d, s -move-> t
3076 3078 |
3077 3079 o i-1: a -move-> c, p -move-> s
3078 3080 |
3079 3081 o i-0 initial commit: a b h p q r
3080 3082
3081 3083
3082 3084 #if no-changeset
3083 3085 $ hg manifest --debug --rev 'desc("mPQm-0")' | grep '644 v'
3084 3086 0946c662ef16e4e67397fd717389eb6693d41749 644 v
3085 3087 $ hg manifest --debug --rev 'desc("mQPm-0")' | grep '644 v'
3086 3088 0db3aad7fcc1ec27fab57060e327b9e864ea0cc9 644 v
3087 3089 $ hg manifest --debug --rev 'desc("p-2")' | grep '644 v'
3088 3090 3f91841cd75cadc9a1f1b4e7c1aa6d411f76032e 644 v
3089 3091 $ hg manifest --debug --rev 'desc("q-2")' | grep '644 v'
3090 3092 c43c088b811fd27983c0a9aadf44f3343cd4cd7e 644 v
3091 3093 $ hg debugindex v | "$PYTHON" ../no-linkrev
3092 3094 rev linkrev nodeid p1 p2
3093 3095 0 * 3f91841cd75c 000000000000 000000000000
3094 3096 1 * c43c088b811f 000000000000 000000000000
3095 3097 2 * 0946c662ef16 3f91841cd75c c43c088b811f
3096 3098 3 * 0db3aad7fcc1 c43c088b811f 3f91841cd75c
3097 3099 #else
3098 3100 $ hg manifest --debug --rev 'desc("mPQm-0")' | grep '644 v'
3099 3101 65fde9f6e4d4da23b3f610e07b53673ea9541d75 644 v
3100 3102 $ hg manifest --debug --rev 'desc("mQPm-0")' | grep '644 v'
3101 3103 a098dda6413aecf154eefc976afc38b295acb7e5 644 v
3102 3104 $ hg manifest --debug --rev 'desc("p-2")' | grep '644 v'
3103 3105 5aed6a8dbff0301328c08360d24354d3d064cf0d 644 v
3104 3106 $ hg manifest --debug --rev 'desc("q-2")' | grep '644 v'
3105 3107 a38b2fa170219750dac9bc7d19df831f213ba708 644 v
3106 3108 $ hg debugindex v | "$PYTHON" ../no-linkrev
3107 3109 rev linkrev nodeid p1 p2
3108 3110 0 * 5aed6a8dbff0 000000000000 000000000000
3109 3111 1 * a38b2fa17021 000000000000 000000000000
3110 3112 2 * 65fde9f6e4d4 5aed6a8dbff0 a38b2fa17021
3111 3113 3 * a098dda6413a a38b2fa17021 5aed6a8dbff0
3112 3114 #endif
3113 3115
3114 3116 # Here the filelog based implementation is not looking at the rename
3115 3117 # information (because the file exist on both side). However the changelog
3116 3118 # based on works fine. We have different output.
3117 3119
3118 3120 $ hg status --copies --rev 'desc("p-2")' --rev 'desc("mPQm-0")'
3119 3121 M v
3120 3122 r (no-filelog !)
3121 3123 R r
3122 3124 $ hg status --copies --rev 'desc("p-2")' --rev 'desc("mQPm-0")'
3123 3125 M v
3124 3126 r (no-filelog !)
3125 3127 R r
3126 3128 $ hg status --copies --rev 'desc("q-2")' --rev 'desc("mPQm-0")'
3127 3129 M v
3128 3130 t (no-filelog !)
3129 3131 R t
3130 3132 $ hg status --copies --rev 'desc("q-2")' --rev 'desc("mQPm-0")'
3131 3133 M v
3132 3134 t (no-filelog !)
3133 3135 R t
3134 3136 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("p-2")'
3135 3137 A v
3136 3138 t
3137 3139 R t
3138 3140 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("q-2")'
3139 3141 A v
3140 3142 r
3141 3143 R r
3142 3144
3143 3145 # From here, we run status against revision where both source file exists.
3144 3146 #
3145 3147 # The filelog based implementation picks an arbitrary side based on revision
3146 3148 # numbers. So the same side "wins" whatever the parents order is. This is
3147 3149 # sub-optimal because depending on revision numbers means the result can be
3148 3150 # different from one repository to the next.
3149 3151 #
3150 3152 # The changeset based algorithm use the parent order to break tie on conflicting
3151 3153 # information and will have a different order depending on who is p1 and p2.
3152 3154 # That order is stable accross repositories. (data from p1 prevails)
3153 3155
3154 3156 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mPQm-0")'
3155 3157 A v
3156 3158 t
3157 3159 R r
3158 3160 R t
3159 3161 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mQPm-0")'
3160 3162 A v
3161 3163 t (filelog !)
3162 3164 r (no-filelog !)
3163 3165 R r
3164 3166 R t
3165 3167 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mPQm-0")'
3166 3168 A d
3167 3169 a
3168 3170 A v
3169 3171 r (filelog !)
3170 3172 p (no-filelog !)
3171 3173 R a
3172 3174 R p
3173 3175 R r
3174 3176 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mQPm-0")'
3175 3177 A d
3176 3178 a
3177 3179 A v
3178 3180 r
3179 3181 R a
3180 3182 R p
3181 3183 R r
3182 3184
3183 3185
3184 3186 Comparing with merging with a deletion (and keeping the file)
3185 3187 -------------------------------------------------------------
3186 3188
3187 3189 Merge:
3188 3190 - one removing a file (d)
3189 3191 - one updating that file
3190 3192 - the merge keep the modified version of the file (canceling the delete)
3191 3193
3192 3194 In this case, the file keep on living after the merge. So we should not drop its
3193 3195 copy tracing chain.
3194 3196
3195 3197 $ hg log -G --rev '::(desc("mCGm")+desc("mGCm"))'
3196 3198 o mGCm-0 merge updated/deleted - revive the file (updated content) - the other way
3197 3199 |\
3198 3200 +---o mCGm-0 merge updated/deleted - revive the file (updated content) - one way
3199 3201 | |/
3200 3202 | o g-1: update d
3201 3203 | |
3202 3204 o | c-1 delete d
3203 3205 |/
3204 3206 o i-2: c -move-> d, s -move-> t
3205 3207 |
3206 3208 o i-1: a -move-> c, p -move-> s
3207 3209 |
3208 3210 o i-0 initial commit: a b h p q r
3209 3211
3210 3212
3211 3213 'a' is the copy source of 'd'
3212 3214
3213 3215 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCGm-0")'
3214 3216 A d
3215 3217 a (no-compatibility no-changeset !)
3216 3218 A t
3217 3219 p
3218 3220 R a
3219 3221 R p
3220 3222 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGCm-0")'
3221 3223 A d
3222 3224 a (no-compatibility no-changeset !)
3223 3225 A t
3224 3226 p
3225 3227 R a
3226 3228 R p
3227 3229 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCGm-0")'
3228 3230 A d
3229 3231 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mGCm-0")'
3230 3232 A d
3231 3233 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mCGm-0")'
3232 3234 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGCm-0")'
3233 3235
3234 3236
3235 3237 Comparing with merge restoring an untouched deleted file
3236 3238 --------------------------------------------------------
3237 3239
3238 3240 Merge:
3239 3241 - one removing a file (d)
3240 3242 - one leaving the file untouched
3241 3243 - the merge actively restore the file to the same content.
3242 3244
3243 3245 In this case, the file keep on living after the merge. So we should not drop its
3244 3246 copy tracing chain.
3245 3247
3246 3248 $ hg log -G --rev '::(desc("mCB-revert-m")+desc("mBC-revert-m"))'
3247 3249 o mBC-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
3248 3250 |\
3249 3251 +---o mCB-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
3250 3252 | |/
3251 3253 | o c-1 delete d
3252 3254 | |
3253 3255 o | b-1: b update
3254 3256 |/
3255 3257 o i-2: c -move-> d, s -move-> t
3256 3258 |
3257 3259 o i-1: a -move-> c, p -move-> s
3258 3260 |
3259 3261 o i-0 initial commit: a b h p q r
3260 3262
3261 3263
3262 3264 'a' is the the copy source of 'd'
3263 3265
3264 3266 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCB-revert-m-0")'
3265 3267 M b
3266 3268 A d
3267 3269 a (no-compatibility no-changeset !)
3268 3270 A t
3269 3271 p
3270 3272 R a
3271 3273 R p
3272 3274 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBC-revert-m-0")'
3273 3275 M b
3274 3276 A d
3275 3277 a (no-compatibility no-changeset !)
3276 3278 A t
3277 3279 p
3278 3280 R a
3279 3281 R p
3280 3282 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCB-revert-m-0")'
3281 3283 M b
3282 3284 A d
3283 3285 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBC-revert-m-0")'
3284 3286 M b
3285 3287 A d
3286 3288 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCB-revert-m-0")'
3287 3289 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBC-revert-m-0")'
3288 3290
3289 3291
3290 3292 Merging a branch where a rename was deleted with a branch where the same file was renamed
3291 3293 ------------------------------------------------------------------------------------------
3292 3294
3293 3295 Create a "conflicting" merge where `d` get removed on one branch before its
3294 3296 rename information actually conflict with the other branch.
3295 3297
3296 3298 (the copy information from the branch that was not deleted should win).
3297 3299
3298 3300 $ hg log -G --rev '::(desc("mCH-delete-before-conflict-m")+desc("mHC-delete-before-conflict-m"))'
3299 3301 o mHC-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - the other way
3300 3302 |\
3301 3303 +---o mCH-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - one way
3302 3304 | |/
3303 3305 | o h-1: b -(move)-> d
3304 3306 | |
3305 3307 o | c-1 delete d
3306 3308 | |
3307 3309 o | i-2: c -move-> d, s -move-> t
3308 3310 | |
3309 3311 o | i-1: a -move-> c, p -move-> s
3310 3312 |/
3311 3313 o i-0 initial commit: a b h p q r
3312 3314
3313 3315
3314 3316 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCH-delete-before-conflict-m")'
3315 3317 A d
3316 3318 b (no-compatibility no-changeset !)
3317 3319 A t
3318 3320 p
3319 3321 R a
3320 3322 R b
3321 3323 R p
3322 3324 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mHC-delete-before-conflict-m")'
3323 3325 A d
3324 3326 b
3325 3327 A t
3326 3328 p
3327 3329 R a
3328 3330 R b
3329 3331 R p
3330 3332 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCH-delete-before-conflict-m")'
3331 3333 A d
3332 3334 b
3333 3335 R b
3334 3336 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mHC-delete-before-conflict-m")'
3335 3337 A d
3336 3338 b
3337 3339 R b
3338 3340 $ hg status --copies --rev 'desc("h-1")' --rev 'desc("mCH-delete-before-conflict-m")'
3339 3341 A t
3340 3342 p
3341 3343 R a
3342 3344 R p
3343 3345 $ hg status --copies --rev 'desc("h-1")' --rev 'desc("mHC-delete-before-conflict-m")'
3344 3346 A t
3345 3347 p
3346 3348 R a
3347 3349 R p
3348 3350
3349 3351 Variant of previous with extra changes introduced by the merge
3350 3352 --------------------------------------------------------------
3351 3353
3352 3354 (see case declaration for details)
3353 3355
3354 3356 Subcase: merge has same initial content on both side, but merge introduced a change
3355 3357 ```````````````````````````````````````````````````````````````````````````````````
3356 3358
3357 3359 - the "e-" branch renaming b to f (through 'g')
3358 3360 - the "a-" branch renaming d to f (through e)
3359 3361 - the merge add new change to b
3360 3362
3361 3363 $ hg log -G --rev '::(desc("mAE-change-m")+desc("mEA-change-m"))'
3362 3364 o mEA-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - the other way
3363 3365 |\
3364 3366 +---o mAE-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - one way
3365 3367 | |/
3366 3368 | o e-2 g -move-> f
3367 3369 | |
3368 3370 | o e-1 b -move-> g
3369 3371 | |
3370 3372 o | a-2: e -move-> f
3371 3373 | |
3372 3374 o | a-1: d -move-> e
3373 3375 |/
3374 3376 o i-2: c -move-> d, s -move-> t
3375 3377 |
3376 3378 o i-1: a -move-> c, p -move-> s
3377 3379 |
3378 3380 o i-0 initial commit: a b h p q r
3379 3381
3380 3382 #if no-changeset
3381 3383 $ hg manifest --debug --rev 'desc("mAE-change-m-0")' | grep '644 f'
3382 3384 2f649fba7eb284e720d02b61f0546fcef694c045 644 f
3383 3385 $ hg manifest --debug --rev 'desc("mEA-change-m-0")' | grep '644 f'
3384 3386 774e7c1637d536b99e2d8ef16fd731f87a82bd09 644 f
3385 3387 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
3386 3388 b76eb76580df486c3d51d63c5c210d4dd43a8ac7 644 f
3387 3389 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
3388 3390 e8825b386367b29fec957283a80bb47b47483fe1 644 f
3389 3391 $ hg debugindex f | "$PYTHON" ../no-linkrev
3390 3392 rev linkrev nodeid p1 p2
3391 3393 0 * b76eb76580df 000000000000 000000000000
3392 3394 1 * e8825b386367 000000000000 000000000000
3393 3395 2 * 2ff93c643948 b76eb76580df e8825b386367
3394 3396 3 * 2f649fba7eb2 b76eb76580df e8825b386367
3395 3397 4 * 774e7c1637d5 e8825b386367 b76eb76580df
3396 3398 #else
3397 3399 $ hg manifest --debug --rev 'desc("mAE-change-m-0")' | grep '644 f'
3398 3400 d3613c1ec8310a812ac4268fd853ac576b6caea5 644 f
3399 3401 $ hg manifest --debug --rev 'desc("mEA-change-m-0")' | grep '644 f'
3400 3402 05e03c868bbcab4a649cb33a238d7aa07398a469 644 f
3401 3403 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
3402 3404 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
3403 3405 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
3404 3406 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
3405 3407 $ hg debugindex f | "$PYTHON" ../no-linkrev
3406 3408 rev linkrev nodeid p1 p2
3407 3409 0 * ae258f702dfe 000000000000 000000000000
3408 3410 1 * d3613c1ec831 ae258f702dfe 000000000000
3409 3411 2 * 05e03c868bbc ae258f702dfe 000000000000
3410 3412 #endif
3411 3413
3412 3414 # Here the filelog based implementation is not looking at the rename
3413 3415 # information (because the file exist on both side). However the changelog
3414 3416 # based on works fine. We have different output.
3415 3417
3416 3418 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mAE-change-m-0")'
3417 3419 M f
3418 3420 b (no-filelog !)
3419 3421 R b
3420 3422 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mEA-change-m-0")'
3421 3423 M f
3422 3424 b (no-filelog !)
3423 3425 R b
3424 3426 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mAE-change-m-0")'
3425 3427 M f
3426 3428 d (no-filelog !)
3427 3429 R d
3428 3430 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mEA-change-m-0")'
3429 3431 M f
3430 3432 d (no-filelog !)
3431 3433 R d
3432 3434 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("a-2")'
3433 3435 A f
3434 3436 d
3435 3437 R d
3436 3438 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("e-2")'
3437 3439 A f
3438 3440 b
3439 3441 R b
3440 3442
3441 3443 # From here, we run status against revision where both source file exists.
3442 3444 #
3443 3445 # The filelog based implementation picks an arbitrary side based on revision
3444 3446 # numbers. So the same side "wins" whatever the parents order is. This is
3445 3447 # sub-optimal because depending on revision numbers means the result can be
3446 3448 # different from one repository to the next.
3447 3449 #
3448 3450 # The changeset based algorithm use the parent order to break tie on conflicting
3449 3451 # information and will have a different order depending on who is p1 and p2.
3450 3452 # That order is stable accross repositories. (data from p1 prevails)
3451 3453
3452 3454 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mAE-change-m-0")'
3453 3455 A f
3454 3456 d
3455 3457 R b
3456 3458 R d
3457 3459 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mEA-change-m-0")'
3458 3460 A f
3459 3461 d (filelog !)
3460 3462 b (no-filelog !)
3461 3463 R b
3462 3464 R d
3463 3465 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAE-change-m-0")'
3464 3466 A f
3465 3467 a
3466 3468 A t
3467 3469 p
3468 3470 R a
3469 3471 R b
3470 3472 R p
3471 3473 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEA-change-m-0")'
3472 3474 A f
3473 3475 a (filelog !)
3474 3476 b (no-filelog !)
3475 3477 A t
3476 3478 p
3477 3479 R a
3478 3480 R b
3479 3481 R p
3480 3482
3481 3483
3482 3484 Subcase: merge overwrite common copy information, but with extra change during the merge
3483 3485 ```````````````````````````````````````````````````````````````````````````````````
3484 3486
3485 3487 Merge:
3486 3488 - one with change to an unrelated file (b)
3487 3489 - one overwriting a file (d) with a rename (from h to i to d)
3488 3490
3489 3491 $ hg log -G --rev '::(desc("mBF-change-m")+desc("mFB-change-m"))'
3490 3492 o mFB-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
3491 3493 |\
3492 3494 +---o mBF-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
3493 3495 | |/
3494 3496 | o f-2: rename i -> d
3495 3497 | |
3496 3498 | o f-1: rename h -> i
3497 3499 | |
3498 3500 o | b-1: b update
3499 3501 |/
3500 3502 o i-2: c -move-> d, s -move-> t
3501 3503 |
3502 3504 o i-1: a -move-> c, p -move-> s
3503 3505 |
3504 3506 o i-0 initial commit: a b h p q r
3505 3507
3506 3508 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBF-change-m-0")'
3507 3509 M b
3508 3510 A d
3509 3511 h
3510 3512 A t
3511 3513 p
3512 3514 R a
3513 3515 R h
3514 3516 R p
3515 3517 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFB-change-m-0")'
3516 3518 M b
3517 3519 A d
3518 3520 h
3519 3521 A t
3520 3522 p
3521 3523 R a
3522 3524 R h
3523 3525 R p
3524 3526 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBF-change-m-0")'
3525 3527 M d
3526 3528 h (no-filelog !)
3527 3529 R h
3528 3530 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mBF-change-m-0")'
3529 3531 M b
3530 3532 M d
3531 3533 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mBF-change-m-0")'
3532 3534 M b
3533 3535 M d
3534 3536 i (no-filelog !)
3535 3537 R i
3536 3538 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mFB-change-m-0")'
3537 3539 M d
3538 3540 h (no-filelog !)
3539 3541 R h
3540 3542 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFB-change-m-0")'
3541 3543 M b
3542 3544 M d
3543 3545 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFB-change-m-0")'
3544 3546 M b
3545 3547 M d
3546 3548 i (no-filelog !)
3547 3549 R i
3548 3550
3549 3551 #if no-changeset
3550 3552 $ hg log -Gfr 'desc("mBF-change-m-0")' d
3551 3553 o mBF-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
3552 3554 |\
3553 3555 o : f-2: rename i -> d
3554 3556 | :
3555 3557 o : f-1: rename h -> i
3556 3558 :/
3557 3559 o i-0 initial commit: a b h p q r
3558 3560
3559 3561 #else
3560 3562 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
3561 3563 $ hg log -Gfr 'desc("mBF-change-m-0")' d
3562 3564 o mBF-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
3563 3565 :
3564 3566 o i-2: c -move-> d, s -move-> t
3565 3567 |
3566 3568 ~
3567 3569 #endif
3568 3570
3569 3571 #if no-changeset
3570 3572 $ hg log -Gfr 'desc("mFB-change-m-0")' d
3571 3573 o mFB-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
3572 3574 |\
3573 3575 o : f-2: rename i -> d
3574 3576 | :
3575 3577 o : f-1: rename h -> i
3576 3578 :/
3577 3579 o i-0 initial commit: a b h p q r
3578 3580
3579 3581 #else
3580 3582 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
3581 3583 $ hg log -Gfr 'desc("mFB-change-m-0")' d
3582 3584 o mFB-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
3583 3585 :
3584 3586 o i-2: c -move-> d, s -move-> t
3585 3587 |
3586 3588 ~
3587 3589 #endif
3588 3590
3589 3591
3590 3592 Subcase: restoring and untouched deleted file, while touching it
3591 3593 ````````````````````````````````````````````````````````````````
3592 3594
3593 3595 Merge:
3594 3596 - one removing a file (d)
3595 3597 - one leaving the file untouched
3596 3598 - the merge actively restore the file to the same content.
3597 3599
3598 3600 In this case, the file keep on living after the merge. So we should not drop its
3599 3601 copy tracing chain.
3600 3602
3601 3603 $ hg log -G --rev '::(desc("mCB-change-m")+desc("mBC-change-m"))'
3602 3604 o mBC-change-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
3603 3605 |\
3604 3606 +---o mCB-change-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
3605 3607 | |/
3606 3608 | o c-1 delete d
3607 3609 | |
3608 3610 o | b-1: b update
3609 3611 |/
3610 3612 o i-2: c -move-> d, s -move-> t
3611 3613 |
3612 3614 o i-1: a -move-> c, p -move-> s
3613 3615 |
3614 3616 o i-0 initial commit: a b h p q r
3615 3617
3616 3618
3617 3619 'a' is the the copy source of 'd'
3618 3620
3619 3621 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCB-change-m-0")'
3620 3622 M b
3621 3623 A d
3622 3624 a (no-compatibility no-changeset !)
3623 3625 A t
3624 3626 p
3625 3627 R a
3626 3628 R p
3627 3629 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBC-change-m-0")'
3628 3630 M b
3629 3631 A d
3630 3632 a (no-compatibility no-changeset !)
3631 3633 A t
3632 3634 p
3633 3635 R a
3634 3636 R p
3635 3637 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCB-change-m-0")'
3636 3638 M b
3637 3639 A d
3638 3640 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBC-change-m-0")'
3639 3641 M b
3640 3642 A d
3641 3643 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCB-change-m-0")'
3642 3644 M d
3643 3645 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBC-change-m-0")'
3644 3646 M d
3645 3647
3646 3648
3647 3649 Decision from previous merge are properly chained with later merge
3648 3650 ------------------------------------------------------------------
3649 3651
3650 3652
3651 3653 Subcase: chaining conflicting rename resolution
3652 3654 ```````````````````````````````````````````````
3653 3655
3654 3656 The "mAEm" and "mEAm" case create a rename tracking conflict on file 'f'. We
3655 3657 add more change on the respective branch and merge again. These second merge
3656 3658 does not involve the file 'f' and the arbitration done within "mAEm" and "mEA"
3657 3659 about that file should stay unchanged.
3658 3660
3659 3661 The result from mAEm is the same for the subsequent merge:
3660 3662
3661 3663 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAEm")' f
3662 3664 A f
3663 3665 a (no-changeset no-compatibility !)
3664 3666
3665 3667 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAE,Km")' f
3666 3668 A f
3667 3669 a (no-changeset no-compatibility !)
3668 3670
3669 3671 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mK,AEm")' f
3670 3672 A f
3671 3673 a (no-changeset no-compatibility !)
3672 3674
3673 3675
3674 3676 The result from mEAm is the same for the subsequent merge:
3675 3677
3676 3678 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEAm")' f
3677 3679 A f
3678 3680 a (filelog !)
3679 3681 b (no-changeset no-compatibility no-filelog !)
3680 3682
3681 3683 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEA,Jm")' f
3682 3684 A f
3683 3685 a (filelog !)
3684 3686 b (no-changeset no-compatibility no-filelog !)
3685 3687
3686 3688 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mJ,EAm")' f
3687 3689 A f
3688 3690 a (filelog !)
3689 3691 b (no-changeset no-compatibility no-filelog !)
3690 3692
3691 3693 Subcase: chaining conflicting rename resolution
3692 3694 ```````````````````````````````````````````````
3693 3695
3694 3696 The "mPQm" and "mQPm" case create a rename tracking conflict on file 'v'. We
3695 3697 add more change on the respective branch and merge again. These second merge
3696 3698 does not involve the file 'v' and the arbitration done within "mPQm" and "mQP"
3697 3699 about that file should stay unchanged.
3698 3700
3699 3701 The result from mPQm is the same for the subsequent merge:
3700 3702
3701 3703 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mPQm")' v
3702 3704 A v
3703 3705 r (filelog !)
3704 3706 p (no-changeset no-compatibility no-filelog !)
3705 3707
3706 3708 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mPQ,Tm")' v
3707 3709 A v
3708 3710 r (filelog !)
3709 3711 p (no-changeset no-compatibility no-filelog !)
3710 3712
3711 3713 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mT,PQm")' v
3712 3714 A v
3713 3715 r (filelog !)
3714 3716 p (no-changeset no-compatibility no-filelog !)
3715 3717
3716 3718 The result from mQPm is the same for the subsequent merge:
3717 3719
3718 3720 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mQPm")' v
3719 3721 A v
3720 3722 r (no-changeset no-compatibility !)
3721 3723
3722 3724 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mQP,Sm")' v
3723 3725 A v
3724 3726 r (no-changeset no-compatibility !)
3725 3727
3726 3728 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mS,QPm")' v
3727 3729 A v
3728 3730 r (filelog !)
3729 3731 r (no-changeset no-compatibility no-filelog !)
3730 3732
3731 3733
3732 3734 Subcase: chaining salvage information during a merge
3733 3735 ````````````````````````````````````````````````````
3734 3736
3735 3737 We add more change on the branch were the file was deleted. merging again
3736 3738 should preserve the fact eh file was salvaged.
3737 3739
3738 3740 reference output:
3739 3741
3740 3742 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCB-revert-m-0")'
3741 3743 M b
3742 3744 A d
3743 3745 a (no-changeset no-compatibility !)
3744 3746 A t
3745 3747 p
3746 3748 R a
3747 3749 R p
3748 3750 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBC-revert-m-0")'
3749 3751 M b
3750 3752 A d
3751 3753 a (no-changeset no-compatibility !)
3752 3754 A t
3753 3755 p
3754 3756 R a
3755 3757 R p
3756 3758
3757 3759 chained output
3758 3760 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBC+revert,Lm")'
3759 3761 M b
3760 3762 A d
3761 3763 a (no-changeset no-compatibility !)
3762 3764 A t
3763 3765 p
3764 3766 A unrelated-l
3765 3767 R a
3766 3768 R p
3767 3769 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCB+revert,Lm")'
3768 3770 M b
3769 3771 A d
3770 3772 a (no-changeset no-compatibility !)
3771 3773 A t
3772 3774 p
3773 3775 A unrelated-l
3774 3776 R a
3775 3777 R p
3776 3778 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mL,BC+revertm")'
3777 3779 M b
3778 3780 A d
3779 3781 a (no-changeset no-compatibility !)
3780 3782 A t
3781 3783 p
3782 3784 A unrelated-l
3783 3785 R a
3784 3786 R p
3785 3787 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mL,CB+revertm")'
3786 3788 M b
3787 3789 A d
3788 3790 a (no-changeset no-compatibility !)
3789 3791 A t
3790 3792 p
3791 3793 A unrelated-l
3792 3794 R a
3793 3795 R p
3794 3796
3795 3797 Subcase: chaining "merged" information during a merge
3796 3798 ``````````````````````````````````````````````````````
3797 3799
3798 3800 When a non-rename change are merged with a copy overwrite, the merge pick the copy source from (p1) as the reference. We should preserve this information in subsequent merges.
3799 3801
3800 3802
3801 3803 reference output:
3802 3804
3803 3805 (for details about the filelog pick, check the mFGm/mGFm case)
3804 3806
3805 3807 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFGm")' d
3806 3808 A d
3807 3809 a (filelog !)
3808 3810 h (no-changeset no-compatibility no-filelog !)
3809 3811 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGFm")' d
3810 3812 A d
3811 3813 a (filelog !)
3812 3814 a (no-changeset no-compatibility no-filelog !)
3813 3815
3814 3816 Chained output
3815 3817
3816 3818 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mO,FGm")' d
3817 3819 A d
3818 3820 a (filelog !)
3819 3821 h (no-changeset no-compatibility no-filelog !)
3820 3822 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFG,Om")' d
3821 3823 A d
3822 3824 a (filelog !)
3823 3825 h (no-changeset no-compatibility no-filelog !)
3824 3826
3825 3827
3826 3828 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGF,Nm")' d
3827 3829 A d
3828 3830 a (no-changeset no-compatibility !)
3829 3831 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mN,GFm")' d
3830 3832 A d
3831 3833 a (no-changeset no-compatibility !)
3832 3834
3833 3835
3834 3836 Subcase: chaining conflicting rename resolution, with extra change during the merge
3835 3837 ```````````````````````````````````````````````````````````````````````````````````
3836 3838
3837 3839 The "mAEm" and "mEAm" case create a rename tracking conflict on file 'f'. We
3838 3840 add more change on the respective branch and merge again. These second merge
3839 3841 does not involve the file 'f' and the arbitration done within "mAEm" and "mEA"
3840 3842 about that file should stay unchanged.
3841 3843
3842 3844 The result from mAEm is the same for the subsequent merge:
3843 3845
3844 3846 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAE-change-m")' f
3845 3847 A f
3846 3848 a (no-changeset no-compatibility !)
3847 3849
3848 3850 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAE-change,Km")' f
3849 3851 A f
3850 3852 a (no-changeset no-compatibility !)
3851 3853
3852 3854 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mK,AE-change-m")' f
3853 3855 A f
3854 3856 a (no-changeset no-compatibility !)
3855 3857
3856 3858
3857 3859 The result from mEAm is the same for the subsequent merge:
3858 3860
3859 3861 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEA-change-m")' f
3860 3862 A f
3861 3863 a (filelog !)
3862 3864 b (no-changeset no-compatibility no-filelog !)
3863 3865
3864 3866 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEA-change,Jm")' f
3865 3867 A f
3866 3868 a (filelog !)
3867 3869 b (no-changeset no-compatibility no-filelog !)
3868 3870
3869 3871 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mJ,EA-change-m")' f
3870 3872 A f
3871 3873 a (filelog !)
3872 3874 b (no-changeset no-compatibility no-filelog !)
@@ -1,509 +1,514
1 1 #testcases extra sidedata
2 2
3 3 #if extra
4 4 $ cat >> $HGRCPATH << EOF
5 5 > [experimental]
6 6 > copies.write-to=changeset-only
7 7 > copies.read-from=changeset-only
8 8 > [alias]
9 9 > changesetcopies = log -r . -T 'files: {files}
10 10 > {extras % "{ifcontains("files", key, "{key}: {value}\n")}"}
11 11 > {extras % "{ifcontains("copies", key, "{key}: {value}\n")}"}'
12 12 > EOF
13 13 #endif
14 14
15 15 #if sidedata
16 16 $ cat >> $HGRCPATH << EOF
17 17 > [format]
18 18 > exp-use-copies-side-data-changeset = yes
19 19 > EOF
20 20 #endif
21 21
22 22 $ cat >> $HGRCPATH << EOF
23 23 > [alias]
24 24 > showcopies = log -r . -T '{file_copies % "{source} -> {name}\n"}'
25 25 > [extensions]
26 26 > rebase =
27 27 > split =
28 28 > EOF
29 29
30 30 Check that copies are recorded correctly
31 31
32 32 $ hg init repo
33 33 $ cd repo
34 34 #if sidedata
35 35 $ hg debugformat -v
36 36 format-variant repo config default
37 37 fncache: yes yes yes
38 dirstate-v2: no no no
38 39 dotencode: yes yes yes
39 40 generaldelta: yes yes yes
40 41 share-safe: no no no
41 42 sparserevlog: yes yes yes
42 43 persistent-nodemap: no no no (no-rust !)
43 44 persistent-nodemap: yes yes no (rust !)
44 45 copies-sdc: yes yes no
45 46 revlog-v2: no no no
46 47 changelog-v2: yes yes no
47 48 plain-cl-delta: yes yes yes
48 49 compression: zlib zlib zlib (no-zstd !)
49 50 compression: zstd zstd zstd (zstd !)
50 51 compression-level: default default default
51 52 #else
52 53 $ hg debugformat -v
53 54 format-variant repo config default
54 55 fncache: yes yes yes
56 dirstate-v2: no no no
55 57 dotencode: yes yes yes
56 58 generaldelta: yes yes yes
57 59 share-safe: no no no
58 60 sparserevlog: yes yes yes
59 61 persistent-nodemap: no no no (no-rust !)
60 62 persistent-nodemap: yes yes no (rust !)
61 63 copies-sdc: no no no
62 64 revlog-v2: no no no
63 65 changelog-v2: no no no
64 66 plain-cl-delta: yes yes yes
65 67 compression: zlib zlib zlib (no-zstd !)
66 68 compression: zstd zstd zstd (zstd !)
67 69 compression-level: default default default
68 70 #endif
69 71 $ echo a > a
70 72 $ hg add a
71 73 $ hg ci -m initial
72 74 $ hg cp a b
73 75 $ hg cp a c
74 76 $ hg cp a d
75 77 $ hg ci -m 'copy a to b, c, and d'
76 78
77 79 #if extra
78 80
79 81 $ hg changesetcopies
80 82 files: b c d
81 83 filesadded: 0
82 84 1
83 85 2
84 86
85 87 p1copies: 0\x00a (esc)
86 88 1\x00a (esc)
87 89 2\x00a (esc)
88 90 #else
89 91 $ hg debugsidedata -c -v -- -1
90 92 1 sidedata entries
91 93 entry-0014 size 44
92 94 '\x00\x00\x00\x04\x00\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00\x06\x00\x00\x00\x03\x00\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\x00abcd'
93 95 #endif
94 96
95 97 $ hg showcopies
96 98 a -> b
97 99 a -> c
98 100 a -> d
99 101
100 102 #if extra
101 103
102 104 $ hg showcopies --config experimental.copies.read-from=compatibility
103 105 a -> b
104 106 a -> c
105 107 a -> d
106 108 $ hg showcopies --config experimental.copies.read-from=filelog-only
107 109
108 110 #endif
109 111
110 112 Check that renames are recorded correctly
111 113
112 114 $ hg mv b b2
113 115 $ hg ci -m 'rename b to b2'
114 116
115 117 #if extra
116 118
117 119 $ hg changesetcopies
118 120 files: b b2
119 121 filesadded: 1
120 122 filesremoved: 0
121 123
122 124 p1copies: 1\x00b (esc)
123 125
124 126 #else
125 127 $ hg debugsidedata -c -v -- -1
126 128 1 sidedata entries
127 129 entry-0014 size 25
128 130 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x03\x00\x00\x00\x00bb2'
129 131 #endif
130 132
131 133 $ hg showcopies
132 134 b -> b2
133 135
134 136
135 137 Rename onto existing file. This should get recorded in the changeset files list and in the extras,
136 138 even though there is no filelog entry.
137 139
138 140 $ hg cp b2 c --force
139 141 $ hg st --copies
140 142 M c
141 143 b2
142 144
143 145 #if extra
144 146
145 147 $ hg debugindex c
146 148 rev linkrev nodeid p1 p2
147 149 0 1 b789fdd96dc2 000000000000 000000000000
148 150
149 151 #else
150 152
151 153 $ hg debugindex c
152 154 rev linkrev nodeid p1 p2
153 155 0 1 37d9b5d994ea 000000000000 000000000000
154 156
155 157 #endif
156 158
157 159
158 160 $ hg ci -m 'move b onto d'
159 161
160 162 #if extra
161 163
162 164 $ hg changesetcopies
163 165 files: c
164 166
165 167 p1copies: 0\x00b2 (esc)
166 168
167 169 #else
168 170 $ hg debugsidedata -c -v -- -1
169 171 1 sidedata entries
170 172 entry-0014 size 25
171 173 '\x00\x00\x00\x02\x00\x00\x00\x00\x02\x00\x00\x00\x00\x16\x00\x00\x00\x03\x00\x00\x00\x00b2c'
172 174 #endif
173 175
174 176 $ hg showcopies
175 177 b2 -> c
176 178
177 179 #if extra
178 180
179 181 $ hg debugindex c
180 182 rev linkrev nodeid p1 p2
181 183 0 1 b789fdd96dc2 000000000000 000000000000
182 184
183 185 #else
184 186
185 187 $ hg debugindex c
186 188 rev linkrev nodeid p1 p2
187 189 0 1 37d9b5d994ea 000000000000 000000000000
188 190 1 3 029625640347 000000000000 000000000000
189 191
190 192 #endif
191 193
192 194 Create a merge commit with copying done during merge.
193 195
194 196 $ hg co 0
195 197 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
196 198 $ hg cp a e
197 199 $ hg cp a f
198 200 $ hg ci -m 'copy a to e and f'
199 201 created new head
200 202 $ hg merge 3
201 203 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
202 204 (branch merge, don't forget to commit)
203 205 File 'a' exists on both sides, so 'g' could be recorded as being from p1 or p2, but we currently
204 206 always record it as being from p1
205 207 $ hg cp a g
206 208 File 'd' exists only in p2, so 'h' should be from p2
207 209 $ hg cp d h
208 210 File 'f' exists only in p1, so 'i' should be from p1
209 211 $ hg cp f i
210 212 $ hg ci -m 'merge'
211 213
212 214 #if extra
213 215
214 216 $ hg changesetcopies
215 217 files: g h i
216 218 filesadded: 0
217 219 1
218 220 2
219 221
220 222 p1copies: 0\x00a (esc)
221 223 2\x00f (esc)
222 224 p2copies: 1\x00d (esc)
223 225
224 226 #else
225 227 $ hg debugsidedata -c -v -- -1
226 228 1 sidedata entries
227 229 entry-0014 size 64
228 230 '\x00\x00\x00\x06\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\x00\x07\x00\x00\x00\x05\x00\x00\x00\x01\x06\x00\x00\x00\x06\x00\x00\x00\x02adfghi'
229 231 #endif
230 232
231 233 $ hg showcopies
232 234 a -> g
233 235 d -> h
234 236 f -> i
235 237
236 238 Test writing to both changeset and filelog
237 239
238 240 $ hg cp a j
239 241 #if extra
240 242 $ hg ci -m 'copy a to j' --config experimental.copies.write-to=compatibility
241 243 $ hg changesetcopies
242 244 files: j
243 245 filesadded: 0
244 246 filesremoved:
245 247
246 248 p1copies: 0\x00a (esc)
247 249 p2copies:
248 250 #else
249 251 $ hg ci -m 'copy a to j'
250 252 $ hg debugsidedata -c -v -- -1
251 253 1 sidedata entries
252 254 entry-0014 size 24
253 255 '\x00\x00\x00\x02\x00\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00aj'
254 256 #endif
255 257 $ hg debugdata j 0
256 258 \x01 (esc)
257 259 copy: a
258 260 copyrev: b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
259 261 \x01 (esc)
260 262 a
261 263 $ hg showcopies
262 264 a -> j
263 265 $ hg showcopies --config experimental.copies.read-from=compatibility
264 266 a -> j
265 267 $ hg showcopies --config experimental.copies.read-from=filelog-only
266 268 a -> j
267 269 Existing copy information in the changeset gets removed on amend and writing
268 270 copy information on to the filelog
269 271 #if extra
270 272 $ hg ci --amend -m 'copy a to j, v2' \
271 273 > --config experimental.copies.write-to=filelog-only
272 274 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-*-amend.hg (glob)
273 275 $ hg changesetcopies
274 276 files: j
275 277
276 278 #else
277 279 $ hg ci --amend -m 'copy a to j, v2'
278 280 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-*-amend.hg (glob)
279 281 $ hg debugsidedata -c -v -- -1
280 282 1 sidedata entries
281 283 entry-0014 size 24
282 284 '\x00\x00\x00\x02\x00\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00aj'
283 285 #endif
284 286 $ hg showcopies --config experimental.copies.read-from=filelog-only
285 287 a -> j
286 288 The entries should be written to extras even if they're empty (so the client
287 289 won't have to fall back to reading from filelogs)
288 290 $ echo x >> j
289 291 #if extra
290 292 $ hg ci -m 'modify j' --config experimental.copies.write-to=compatibility
291 293 $ hg changesetcopies
292 294 files: j
293 295 filesadded:
294 296 filesremoved:
295 297
296 298 p1copies:
297 299 p2copies:
298 300 #else
299 301 $ hg ci -m 'modify j'
300 302 $ hg debugsidedata -c -v -- -1
301 303 1 sidedata entries
302 304 entry-0014 size 14
303 305 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00j'
304 306 #endif
305 307
306 308 Test writing only to filelog
307 309
308 310 $ hg cp a k
309 311 #if extra
310 312 $ hg ci -m 'copy a to k' --config experimental.copies.write-to=filelog-only
311 313
312 314 $ hg changesetcopies
313 315 files: k
314 316
315 317 #else
316 318 $ hg ci -m 'copy a to k'
317 319 $ hg debugsidedata -c -v -- -1
318 320 1 sidedata entries
319 321 entry-0014 size 24
320 322 '\x00\x00\x00\x02\x00\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00ak'
321 323 #endif
322 324
323 325 $ hg debugdata k 0
324 326 \x01 (esc)
325 327 copy: a
326 328 copyrev: b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
327 329 \x01 (esc)
328 330 a
329 331 #if extra
330 332 $ hg showcopies
331 333
332 334 $ hg showcopies --config experimental.copies.read-from=compatibility
333 335 a -> k
334 336 $ hg showcopies --config experimental.copies.read-from=filelog-only
335 337 a -> k
336 338 #else
337 339 $ hg showcopies
338 340 a -> k
339 341 #endif
340 342
341 343 $ cd ..
342 344
343 345 Test rebasing a commit with copy information
344 346
345 347 $ hg init rebase-rename
346 348 $ cd rebase-rename
347 349 $ echo a > a
348 350 $ hg ci -Aqm 'add a'
349 351 $ echo a2 > a
350 352 $ hg ci -m 'modify a'
351 353 $ hg co -q 0
352 354 $ hg mv a b
353 355 $ hg ci -qm 'rename a to b'
354 356 Not only do we want this to run in-memory, it shouldn't fall back to
355 357 on-disk merge (no conflicts), so we force it to be in-memory
356 358 with no fallback.
357 359 $ hg rebase -d 1 --config rebase.experimental.inmemory=yes --config devel.rebase.force-in-memory-merge=yes
358 360 rebasing 2:* tip "rename a to b" (glob)
359 361 merging a and b to b
360 362 saved backup bundle to $TESTTMP/rebase-rename/.hg/strip-backup/*-*-rebase.hg (glob)
361 363 $ hg st --change . --copies
362 364 A b
363 365 a
364 366 R a
365 367 $ cd ..
366 368
367 369 Test splitting a commit
368 370
369 371 $ hg init split
370 372 $ cd split
371 373 $ echo a > a
372 374 $ echo b > b
373 375 $ hg ci -Aqm 'add a and b'
374 376 $ echo a2 > a
375 377 $ hg mv b c
376 378 $ hg ci -m 'modify a, move b to c'
377 379 $ hg --config ui.interactive=yes split <<EOF
378 380 > y
379 381 > y
380 382 > n
381 383 > y
382 384 > EOF
383 385 diff --git a/a b/a
384 386 1 hunks, 1 lines changed
385 387 examine changes to 'a'?
386 388 (enter ? for help) [Ynesfdaq?] y
387 389
388 390 @@ -1,1 +1,1 @@
389 391 -a
390 392 +a2
391 393 record this change to 'a'?
392 394 (enter ? for help) [Ynesfdaq?] y
393 395
394 396 diff --git a/b b/c
395 397 rename from b
396 398 rename to c
397 399 examine changes to 'b' and 'c'?
398 400 (enter ? for help) [Ynesfdaq?] n
399 401
400 402 created new head
401 403 diff --git a/b b/c
402 404 rename from b
403 405 rename to c
404 406 examine changes to 'b' and 'c'?
405 407 (enter ? for help) [Ynesfdaq?] y
406 408
407 409 saved backup bundle to $TESTTMP/split/.hg/strip-backup/*-*-split.hg (glob)
408 410 $ cd ..
409 411
410 412 Test committing half a rename
411 413
412 414 $ hg init partial
413 415 $ cd partial
414 416 $ echo a > a
415 417 $ hg ci -Aqm 'add a'
416 418 $ hg mv a b
417 419 $ hg ci -m 'remove a' a
418 420
419 421 #if sidedata
420 422
421 423 Test upgrading/downgrading to sidedata storage
422 424 ==============================================
423 425
424 426 downgrading
425 427
426 428 $ hg debugformat -v
427 429 format-variant repo config default
428 430 fncache: yes yes yes
431 dirstate-v2: no no no
429 432 dotencode: yes yes yes
430 433 generaldelta: yes yes yes
431 434 share-safe: no no no
432 435 sparserevlog: yes yes yes
433 436 persistent-nodemap: no no no (no-rust !)
434 437 persistent-nodemap: yes yes no (rust !)
435 438 copies-sdc: yes yes no
436 439 revlog-v2: no no no
437 440 changelog-v2: yes yes no
438 441 plain-cl-delta: yes yes yes
439 442 compression: zlib zlib zlib (no-zstd !)
440 443 compression: zstd zstd zstd (zstd !)
441 444 compression-level: default default default
442 445 $ hg debugsidedata -c -- 0
443 446 1 sidedata entries
444 447 entry-0014 size 14
445 448 $ hg debugsidedata -c -- 1
446 449 1 sidedata entries
447 450 entry-0014 size 14
448 451 $ hg debugsidedata -m -- 0
449 452 $ cat << EOF > .hg/hgrc
450 453 > [format]
451 454 > exp-use-copies-side-data-changeset = no
452 455 > [experimental]
453 456 > revlogv2 = enable-unstable-format-and-corrupt-my-data
454 457 > EOF
455 458 $ hg debugupgraderepo --run --quiet --no-backup > /dev/null
456 459 $ hg debugformat -v
457 460 format-variant repo config default
458 461 fncache: yes yes yes
462 dirstate-v2: no no no
459 463 dotencode: yes yes yes
460 464 generaldelta: yes yes yes
461 465 share-safe: no no no
462 466 sparserevlog: yes yes yes
463 467 persistent-nodemap: no no no (no-rust !)
464 468 persistent-nodemap: yes yes no (rust !)
465 469 copies-sdc: no no no
466 470 revlog-v2: yes yes no
467 471 changelog-v2: no no no
468 472 plain-cl-delta: yes yes yes
469 473 compression: zlib zlib zlib (no-zstd !)
470 474 compression: zstd zstd zstd (zstd !)
471 475 compression-level: default default default
472 476 $ hg debugsidedata -c -- 0
473 477 $ hg debugsidedata -c -- 1
474 478 $ hg debugsidedata -m -- 0
475 479
476 480 upgrading
477 481
478 482 $ cat << EOF > .hg/hgrc
479 483 > [format]
480 484 > exp-use-copies-side-data-changeset = yes
481 485 > EOF
482 486 $ hg debugupgraderepo --run --quiet --no-backup > /dev/null
483 487 $ hg debugformat -v
484 488 format-variant repo config default
485 489 fncache: yes yes yes
490 dirstate-v2: no no no
486 491 dotencode: yes yes yes
487 492 generaldelta: yes yes yes
488 493 share-safe: no no no
489 494 sparserevlog: yes yes yes
490 495 persistent-nodemap: no no no (no-rust !)
491 496 persistent-nodemap: yes yes no (rust !)
492 497 copies-sdc: yes yes no
493 498 revlog-v2: no no no
494 499 changelog-v2: yes yes no
495 500 plain-cl-delta: yes yes yes
496 501 compression: zlib zlib zlib (no-zstd !)
497 502 compression: zstd zstd zstd (zstd !)
498 503 compression-level: default default default
499 504 $ hg debugsidedata -c -- 0
500 505 1 sidedata entries
501 506 entry-0014 size 14
502 507 $ hg debugsidedata -c -- 1
503 508 1 sidedata entries
504 509 entry-0014 size 14
505 510 $ hg debugsidedata -m -- 0
506 511
507 512 #endif
508 513
509 514 $ cd ..
@@ -1,1059 +1,1062
1 1 ===================================
2 2 Test the persistent on-disk nodemap
3 3 ===================================
4 4
5 5
6 6 #if no-rust
7 7
8 8 $ cat << EOF >> $HGRCPATH
9 9 > [format]
10 10 > use-persistent-nodemap=yes
11 11 > [devel]
12 12 > persistent-nodemap=yes
13 13 > EOF
14 14
15 15 #endif
16 16
17 17 $ hg init test-repo --config storage.revlog.persistent-nodemap.slow-path=allow
18 18 $ cd test-repo
19 19
20 20 Check handling of the default slow-path value
21 21
22 22 #if no-pure no-rust
23 23
24 24 $ hg id
25 25 abort: accessing `persistent-nodemap` repository without associated fast implementation.
26 26 (check `hg help config.format.use-persistent-nodemap` for details)
27 27 [255]
28 28
29 29 Unlock further check (we are here to test the feature)
30 30
31 31 $ cat << EOF >> $HGRCPATH
32 32 > [storage]
33 33 > # to avoid spamming the test
34 34 > revlog.persistent-nodemap.slow-path=allow
35 35 > EOF
36 36
37 37 #endif
38 38
39 39 #if rust
40 40
41 41 Regression test for a previous bug in Rust/C FFI for the `Revlog_CAPI` capsule:
42 42 in places where `mercurial/cext/revlog.c` function signatures use `Py_ssize_t`
43 43 (64 bits on Linux x86_64), corresponding declarations in `rust/hg-cpython/src/cindex.rs`
44 44 incorrectly used `libc::c_int` (32 bits).
45 45 As a result, -1 passed from Rust for the null revision became 4294967295 in C.
46 46
47 47 $ hg log -r 00000000
48 48 changeset: -1:000000000000
49 49 tag: tip
50 50 user:
51 51 date: Thu Jan 01 00:00:00 1970 +0000
52 52
53 53
54 54 #endif
55 55
56 56
57 57 $ hg debugformat
58 58 format-variant repo
59 59 fncache: yes
60 dirstate-v2: no
60 61 dotencode: yes
61 62 generaldelta: yes
62 63 share-safe: no
63 64 sparserevlog: yes
64 65 persistent-nodemap: yes
65 66 copies-sdc: no
66 67 revlog-v2: no
67 68 changelog-v2: no
68 69 plain-cl-delta: yes
69 70 compression: zlib (no-zstd !)
70 71 compression: zstd (zstd !)
71 72 compression-level: default
72 73 $ hg debugbuilddag .+5000 --new-file
73 74
74 75 $ hg debugnodemap --metadata
75 76 uid: ???????? (glob)
76 77 tip-rev: 5000
77 78 tip-node: 6b02b8c7b96654c25e86ba69eda198d7e6ad8b3c
78 79 data-length: 121088
79 80 data-unused: 0
80 81 data-unused: 0.000%
81 82 $ f --size .hg/store/00changelog.n
82 83 .hg/store/00changelog.n: size=62
83 84
84 85 Simple lookup works
85 86
86 87 $ ANYNODE=`hg log --template '{node|short}\n' --rev tip`
87 88 $ hg log -r "$ANYNODE" --template '{rev}\n'
88 89 5000
89 90
90 91
91 92 #if rust
92 93
93 94 $ f --sha256 .hg/store/00changelog-*.nd
94 95 .hg/store/00changelog-????????.nd: sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd (glob)
95 96
96 97 $ f --sha256 .hg/store/00manifest-*.nd
97 98 .hg/store/00manifest-????????.nd: sha256=97117b1c064ea2f86664a124589e47db0e254e8d34739b5c5cc5bf31c9da2b51 (glob)
98 99 $ hg debugnodemap --dump-new | f --sha256 --size
99 100 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
100 101 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
101 102 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
102 103 0000: 00 00 00 91 00 00 00 20 00 00 00 bb 00 00 00 e7 |....... ........|
103 104 0010: 00 00 00 66 00 00 00 a1 00 00 01 13 00 00 01 22 |...f..........."|
104 105 0020: 00 00 00 23 00 00 00 fc 00 00 00 ba 00 00 00 5e |...#...........^|
105 106 0030: 00 00 00 df 00 00 01 4e 00 00 01 65 00 00 00 ab |.......N...e....|
106 107 0040: 00 00 00 a9 00 00 00 95 00 00 00 73 00 00 00 38 |...........s...8|
107 108 0050: 00 00 00 cc 00 00 00 92 00 00 00 90 00 00 00 69 |...............i|
108 109 0060: 00 00 00 ec 00 00 00 8d 00 00 01 4f 00 00 00 12 |...........O....|
109 110 0070: 00 00 02 0c 00 00 00 77 00 00 00 9c 00 00 00 8f |.......w........|
110 111 0080: 00 00 00 d5 00 00 00 6b 00 00 00 48 00 00 00 b3 |.......k...H....|
111 112 0090: 00 00 00 e5 00 00 00 b5 00 00 00 8e 00 00 00 ad |................|
112 113 00a0: 00 00 00 7b 00 00 00 7c 00 00 00 0b 00 00 00 2b |...{...|.......+|
113 114 00b0: 00 00 00 c6 00 00 00 1e 00 00 01 08 00 00 00 11 |................|
114 115 00c0: 00 00 01 30 00 00 00 26 00 00 01 9c 00 00 00 35 |...0...&.......5|
115 116 00d0: 00 00 00 b8 00 00 01 31 00 00 00 2c 00 00 00 55 |.......1...,...U|
116 117 00e0: 00 00 00 8a 00 00 00 9a 00 00 00 0c 00 00 01 1e |................|
117 118 00f0: 00 00 00 a4 00 00 00 83 00 00 00 c9 00 00 00 8c |................|
118 119
119 120
120 121 #else
121 122
122 123 $ f --sha256 .hg/store/00changelog-*.nd
123 124 .hg/store/00changelog-????????.nd: sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79 (glob)
124 125 $ hg debugnodemap --dump-new | f --sha256 --size
125 126 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
126 127 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
127 128 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
128 129 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
129 130 0010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
130 131 0020: ff ff ff ff ff ff f5 06 ff ff ff ff ff ff f3 e7 |................|
131 132 0030: ff ff ef ca ff ff ff ff ff ff ff ff ff ff ff ff |................|
132 133 0040: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
133 134 0050: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ed 08 |................|
134 135 0060: ff ff ed 66 ff ff ff ff ff ff ff ff ff ff ff ff |...f............|
135 136 0070: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
136 137 0080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
137 138 0090: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f6 ed |................|
138 139 00a0: ff ff ff ff ff ff fe 61 ff ff ff ff ff ff ff ff |.......a........|
139 140 00b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
140 141 00c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
141 142 00d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
142 143 00e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f1 02 |................|
143 144 00f0: ff ff ff ff ff ff ed 1b ff ff ff ff ff ff ff ff |................|
144 145
145 146 #endif
146 147
147 148 $ hg debugnodemap --check
148 149 revision in index: 5001
149 150 revision in nodemap: 5001
150 151
151 152 add a new commit
152 153
153 154 $ hg up
154 155 5001 files updated, 0 files merged, 0 files removed, 0 files unresolved
155 156 $ echo foo > foo
156 157 $ hg add foo
157 158
158 159
159 160 Check slow-path config value handling
160 161 -------------------------------------
161 162
162 163 #if no-pure no-rust
163 164
164 165 $ hg id --config "storage.revlog.persistent-nodemap.slow-path=invalid-value"
165 166 unknown value for config "storage.revlog.persistent-nodemap.slow-path": "invalid-value"
166 167 falling back to default value: abort
167 168 abort: accessing `persistent-nodemap` repository without associated fast implementation.
168 169 (check `hg help config.format.use-persistent-nodemap` for details)
169 170 [255]
170 171
171 172 $ hg log -r . --config "storage.revlog.persistent-nodemap.slow-path=warn"
172 173 warning: accessing `persistent-nodemap` repository without associated fast implementation.
173 174 (check `hg help config.format.use-persistent-nodemap` for details)
174 175 changeset: 5000:6b02b8c7b966
175 176 tag: tip
176 177 user: debugbuilddag
177 178 date: Thu Jan 01 01:23:20 1970 +0000
178 179 summary: r5000
179 180
180 181 $ hg ci -m 'foo' --config "storage.revlog.persistent-nodemap.slow-path=abort"
181 182 abort: accessing `persistent-nodemap` repository without associated fast implementation.
182 183 (check `hg help config.format.use-persistent-nodemap` for details)
183 184 [255]
184 185
185 186 #else
186 187
187 188 $ hg id --config "storage.revlog.persistent-nodemap.slow-path=invalid-value"
188 189 unknown value for config "storage.revlog.persistent-nodemap.slow-path": "invalid-value"
189 190 falling back to default value: abort
190 191 6b02b8c7b966+ tip
191 192
192 193 #endif
193 194
194 195 $ hg ci -m 'foo'
195 196
196 197 #if no-pure no-rust
197 198 $ hg debugnodemap --metadata
198 199 uid: ???????? (glob)
199 200 tip-rev: 5001
200 201 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
201 202 data-length: 121088
202 203 data-unused: 0
203 204 data-unused: 0.000%
204 205 #else
205 206 $ hg debugnodemap --metadata
206 207 uid: ???????? (glob)
207 208 tip-rev: 5001
208 209 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
209 210 data-length: 121344
210 211 data-unused: 256
211 212 data-unused: 0.211%
212 213 #endif
213 214
214 215 $ f --size .hg/store/00changelog.n
215 216 .hg/store/00changelog.n: size=62
216 217
217 218 (The pure code use the debug code that perform incremental update, the C code reencode from scratch)
218 219
219 220 #if pure
220 221 $ f --sha256 .hg/store/00changelog-*.nd --size
221 222 .hg/store/00changelog-????????.nd: size=121344, sha256=cce54c5da5bde3ad72a4938673ed4064c86231b9c64376b082b163fdb20f8f66 (glob)
222 223 #endif
223 224
224 225 #if rust
225 226 $ f --sha256 .hg/store/00changelog-*.nd --size
226 227 .hg/store/00changelog-????????.nd: size=121344, sha256=952b042fcf614ceb37b542b1b723e04f18f83efe99bee4e0f5ccd232ef470e58 (glob)
227 228 #endif
228 229
229 230 #if no-pure no-rust
230 231 $ f --sha256 .hg/store/00changelog-*.nd --size
231 232 .hg/store/00changelog-????????.nd: size=121088, sha256=df7c06a035b96cb28c7287d349d603baef43240be7736fe34eea419a49702e17 (glob)
232 233 #endif
233 234
234 235 $ hg debugnodemap --check
235 236 revision in index: 5002
236 237 revision in nodemap: 5002
237 238
238 239 Test code path without mmap
239 240 ---------------------------
240 241
241 242 $ echo bar > bar
242 243 $ hg add bar
243 244 $ hg ci -m 'bar' --config storage.revlog.persistent-nodemap.mmap=no
244 245
245 246 $ hg debugnodemap --check --config storage.revlog.persistent-nodemap.mmap=yes
246 247 revision in index: 5003
247 248 revision in nodemap: 5003
248 249 $ hg debugnodemap --check --config storage.revlog.persistent-nodemap.mmap=no
249 250 revision in index: 5003
250 251 revision in nodemap: 5003
251 252
252 253
253 254 #if pure
254 255 $ hg debugnodemap --metadata
255 256 uid: ???????? (glob)
256 257 tip-rev: 5002
257 258 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
258 259 data-length: 121600
259 260 data-unused: 512
260 261 data-unused: 0.421%
261 262 $ f --sha256 .hg/store/00changelog-*.nd --size
262 263 .hg/store/00changelog-????????.nd: size=121600, sha256=def52503d049ccb823974af313a98a935319ba61f40f3aa06a8be4d35c215054 (glob)
263 264 #endif
264 265 #if rust
265 266 $ hg debugnodemap --metadata
266 267 uid: ???????? (glob)
267 268 tip-rev: 5002
268 269 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
269 270 data-length: 121600
270 271 data-unused: 512
271 272 data-unused: 0.421%
272 273 $ f --sha256 .hg/store/00changelog-*.nd --size
273 274 .hg/store/00changelog-????????.nd: size=121600, sha256=dacf5b5f1d4585fee7527d0e67cad5b1ba0930e6a0928f650f779aefb04ce3fb (glob)
274 275 #endif
275 276 #if no-pure no-rust
276 277 $ hg debugnodemap --metadata
277 278 uid: ???????? (glob)
278 279 tip-rev: 5002
279 280 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
280 281 data-length: 121088
281 282 data-unused: 0
282 283 data-unused: 0.000%
283 284 $ f --sha256 .hg/store/00changelog-*.nd --size
284 285 .hg/store/00changelog-????????.nd: size=121088, sha256=59fcede3e3cc587755916ceed29e3c33748cd1aa7d2f91828ac83e7979d935e8 (glob)
285 286 #endif
286 287
287 288 Test force warming the cache
288 289
289 290 $ rm .hg/store/00changelog.n
290 291 $ hg debugnodemap --metadata
291 292 $ hg debugupdatecache
292 293 #if pure
293 294 $ hg debugnodemap --metadata
294 295 uid: ???????? (glob)
295 296 tip-rev: 5002
296 297 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
297 298 data-length: 121088
298 299 data-unused: 0
299 300 data-unused: 0.000%
300 301 #else
301 302 $ hg debugnodemap --metadata
302 303 uid: ???????? (glob)
303 304 tip-rev: 5002
304 305 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
305 306 data-length: 121088
306 307 data-unused: 0
307 308 data-unused: 0.000%
308 309 #endif
309 310
310 311 Check out of sync nodemap
311 312 =========================
312 313
313 314 First copy old data on the side.
314 315
315 316 $ mkdir ../tmp-copies
316 317 $ cp .hg/store/00changelog-????????.nd .hg/store/00changelog.n ../tmp-copies
317 318
318 319 Nodemap lagging behind
319 320 ----------------------
320 321
321 322 make a new commit
322 323
323 324 $ echo bar2 > bar
324 325 $ hg ci -m 'bar2'
325 326 $ NODE=`hg log -r tip -T '{node}\n'`
326 327 $ hg log -r "$NODE" -T '{rev}\n'
327 328 5003
328 329
329 330 If the nodemap is lagging behind, it can catch up fine
330 331
331 332 $ hg debugnodemap --metadata
332 333 uid: ???????? (glob)
333 334 tip-rev: 5003
334 335 tip-node: c9329770f979ade2d16912267c38ba5f82fd37b3
335 336 data-length: 121344 (pure !)
336 337 data-length: 121344 (rust !)
337 338 data-length: 121152 (no-rust no-pure !)
338 339 data-unused: 192 (pure !)
339 340 data-unused: 192 (rust !)
340 341 data-unused: 0 (no-rust no-pure !)
341 342 data-unused: 0.158% (pure !)
342 343 data-unused: 0.158% (rust !)
343 344 data-unused: 0.000% (no-rust no-pure !)
344 345 $ cp -f ../tmp-copies/* .hg/store/
345 346 $ hg debugnodemap --metadata
346 347 uid: ???????? (glob)
347 348 tip-rev: 5002
348 349 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
349 350 data-length: 121088
350 351 data-unused: 0
351 352 data-unused: 0.000%
352 353 $ hg log -r "$NODE" -T '{rev}\n'
353 354 5003
354 355
355 356 changelog altered
356 357 -----------------
357 358
358 359 If the nodemap is not gated behind a requirements, an unaware client can alter
359 360 the repository so the revlog used to generate the nodemap is not longer
360 361 compatible with the persistent nodemap. We need to detect that.
361 362
362 363 $ hg up "$NODE~5"
363 364 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
364 365 $ echo bar > babar
365 366 $ hg add babar
366 367 $ hg ci -m 'babar'
367 368 created new head
368 369 $ OTHERNODE=`hg log -r tip -T '{node}\n'`
369 370 $ hg log -r "$OTHERNODE" -T '{rev}\n'
370 371 5004
371 372
372 373 $ hg --config extensions.strip= strip --rev "$NODE~1" --no-backup
373 374
374 375 the nodemap should detect the changelog have been tampered with and recover.
375 376
376 377 $ hg debugnodemap --metadata
377 378 uid: ???????? (glob)
378 379 tip-rev: 5002
379 380 tip-node: b355ef8adce0949b8bdf6afc72ca853740d65944
380 381 data-length: 121536 (pure !)
381 382 data-length: 121088 (rust !)
382 383 data-length: 121088 (no-pure no-rust !)
383 384 data-unused: 448 (pure !)
384 385 data-unused: 0 (rust !)
385 386 data-unused: 0 (no-pure no-rust !)
386 387 data-unused: 0.000% (rust !)
387 388 data-unused: 0.369% (pure !)
388 389 data-unused: 0.000% (no-pure no-rust !)
389 390
390 391 $ cp -f ../tmp-copies/* .hg/store/
391 392 $ hg debugnodemap --metadata
392 393 uid: ???????? (glob)
393 394 tip-rev: 5002
394 395 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
395 396 data-length: 121088
396 397 data-unused: 0
397 398 data-unused: 0.000%
398 399 $ hg log -r "$OTHERNODE" -T '{rev}\n'
399 400 5002
400 401
401 402 missing data file
402 403 -----------------
403 404
404 405 $ UUID=`hg debugnodemap --metadata| grep 'uid:' | \
405 406 > sed 's/uid: //'`
406 407 $ FILE=.hg/store/00changelog-"${UUID}".nd
407 408 $ mv $FILE ../tmp-data-file
408 409 $ cp .hg/store/00changelog.n ../tmp-docket
409 410
410 411 mercurial don't crash
411 412
412 413 $ hg log -r .
413 414 changeset: 5002:b355ef8adce0
414 415 tag: tip
415 416 parent: 4998:d918ad6d18d3
416 417 user: test
417 418 date: Thu Jan 01 00:00:00 1970 +0000
418 419 summary: babar
419 420
420 421 $ hg debugnodemap --metadata
421 422
422 423 $ hg debugupdatecache
423 424 $ hg debugnodemap --metadata
424 425 uid: * (glob)
425 426 tip-rev: 5002
426 427 tip-node: b355ef8adce0949b8bdf6afc72ca853740d65944
427 428 data-length: 121088
428 429 data-unused: 0
429 430 data-unused: 0.000%
430 431 $ mv ../tmp-data-file $FILE
431 432 $ mv ../tmp-docket .hg/store/00changelog.n
432 433
433 434 Check transaction related property
434 435 ==================================
435 436
436 437 An up to date nodemap should be available to shell hooks,
437 438
438 439 $ echo dsljfl > a
439 440 $ hg add a
440 441 $ hg ci -m a
441 442 $ hg debugnodemap --metadata
442 443 uid: ???????? (glob)
443 444 tip-rev: 5003
444 445 tip-node: a52c5079765b5865d97b993b303a18740113bbb2
445 446 data-length: 121088
446 447 data-unused: 0
447 448 data-unused: 0.000%
448 449 $ echo babar2 > babar
449 450 $ hg ci -m 'babar2' --config "hooks.pretxnclose.nodemap-test=hg debugnodemap --metadata"
450 451 uid: ???????? (glob)
451 452 tip-rev: 5004
452 453 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
453 454 data-length: 121280 (pure !)
454 455 data-length: 121280 (rust !)
455 456 data-length: 121088 (no-pure no-rust !)
456 457 data-unused: 192 (pure !)
457 458 data-unused: 192 (rust !)
458 459 data-unused: 0 (no-pure no-rust !)
459 460 data-unused: 0.158% (pure !)
460 461 data-unused: 0.158% (rust !)
461 462 data-unused: 0.000% (no-pure no-rust !)
462 463 $ hg debugnodemap --metadata
463 464 uid: ???????? (glob)
464 465 tip-rev: 5004
465 466 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
466 467 data-length: 121280 (pure !)
467 468 data-length: 121280 (rust !)
468 469 data-length: 121088 (no-pure no-rust !)
469 470 data-unused: 192 (pure !)
470 471 data-unused: 192 (rust !)
471 472 data-unused: 0 (no-pure no-rust !)
472 473 data-unused: 0.158% (pure !)
473 474 data-unused: 0.158% (rust !)
474 475 data-unused: 0.000% (no-pure no-rust !)
475 476
476 477 Another process does not see the pending nodemap content during run.
477 478
478 479 $ echo qpoasp > a
479 480 $ hg ci -m a2 \
480 481 > --config "hooks.pretxnclose=sh \"$RUNTESTDIR/testlib/wait-on-file\" 20 sync-repo-read sync-txn-pending" \
481 482 > --config "hooks.txnclose=touch sync-txn-close" > output.txt 2>&1 &
482 483
483 484 (read the repository while the commit transaction is pending)
484 485
485 486 $ sh "$RUNTESTDIR/testlib/wait-on-file" 20 sync-txn-pending && \
486 487 > hg debugnodemap --metadata && \
487 488 > sh "$RUNTESTDIR/testlib/wait-on-file" 20 sync-txn-close sync-repo-read
488 489 uid: ???????? (glob)
489 490 tip-rev: 5004
490 491 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
491 492 data-length: 121280 (pure !)
492 493 data-length: 121280 (rust !)
493 494 data-length: 121088 (no-pure no-rust !)
494 495 data-unused: 192 (pure !)
495 496 data-unused: 192 (rust !)
496 497 data-unused: 0 (no-pure no-rust !)
497 498 data-unused: 0.158% (pure !)
498 499 data-unused: 0.158% (rust !)
499 500 data-unused: 0.000% (no-pure no-rust !)
500 501 $ hg debugnodemap --metadata
501 502 uid: ???????? (glob)
502 503 tip-rev: 5005
503 504 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
504 505 data-length: 121536 (pure !)
505 506 data-length: 121536 (rust !)
506 507 data-length: 121088 (no-pure no-rust !)
507 508 data-unused: 448 (pure !)
508 509 data-unused: 448 (rust !)
509 510 data-unused: 0 (no-pure no-rust !)
510 511 data-unused: 0.369% (pure !)
511 512 data-unused: 0.369% (rust !)
512 513 data-unused: 0.000% (no-pure no-rust !)
513 514
514 515 $ cat output.txt
515 516
516 517 Check that a failing transaction will properly revert the data
517 518
518 519 $ echo plakfe > a
519 520 $ f --size --sha256 .hg/store/00changelog-*.nd
520 521 .hg/store/00changelog-????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
521 522 .hg/store/00changelog-????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
522 523 .hg/store/00changelog-????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
523 524 $ hg ci -m a3 --config "extensions.abort=$RUNTESTDIR/testlib/crash_transaction_late.py"
524 525 transaction abort!
525 526 rollback completed
526 527 abort: This is a late abort
527 528 [255]
528 529 $ hg debugnodemap --metadata
529 530 uid: ???????? (glob)
530 531 tip-rev: 5005
531 532 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
532 533 data-length: 121536 (pure !)
533 534 data-length: 121536 (rust !)
534 535 data-length: 121088 (no-pure no-rust !)
535 536 data-unused: 448 (pure !)
536 537 data-unused: 448 (rust !)
537 538 data-unused: 0 (no-pure no-rust !)
538 539 data-unused: 0.369% (pure !)
539 540 data-unused: 0.369% (rust !)
540 541 data-unused: 0.000% (no-pure no-rust !)
541 542 $ f --size --sha256 .hg/store/00changelog-*.nd
542 543 .hg/store/00changelog-????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
543 544 .hg/store/00changelog-????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
544 545 .hg/store/00changelog-????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
545 546
546 547 Check that removing content does not confuse the nodemap
547 548 --------------------------------------------------------
548 549
549 550 removing data with rollback
550 551
551 552 $ echo aso > a
552 553 $ hg ci -m a4
553 554 $ hg rollback
554 555 repository tip rolled back to revision 5005 (undo commit)
555 556 working directory now based on revision 5005
556 557 $ hg id -r .
557 558 90d5d3ba2fc4 tip
558 559
559 560 roming data with strip
560 561
561 562 $ echo aso > a
562 563 $ hg ci -m a4
563 564 $ hg --config extensions.strip= strip -r . --no-backup
564 565 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
565 566 $ hg id -r . --traceback
566 567 90d5d3ba2fc4 tip
567 568
568 569 Test upgrade / downgrade
569 570 ========================
570 571
571 572 downgrading
572 573
573 574 $ cat << EOF >> .hg/hgrc
574 575 > [format]
575 576 > use-persistent-nodemap=no
576 577 > EOF
577 578 $ hg debugformat -v
578 579 format-variant repo config default
579 580 fncache: yes yes yes
581 dirstate-v2: no no no
580 582 dotencode: yes yes yes
581 583 generaldelta: yes yes yes
582 584 share-safe: no no no
583 585 sparserevlog: yes yes yes
584 586 persistent-nodemap: yes no no
585 587 copies-sdc: no no no
586 588 revlog-v2: no no no
587 589 changelog-v2: no no no
588 590 plain-cl-delta: yes yes yes
589 591 compression: zlib zlib zlib (no-zstd !)
590 592 compression: zstd zstd zstd (zstd !)
591 593 compression-level: default default default
592 594 $ hg debugupgraderepo --run --no-backup
593 595 upgrade will perform the following actions:
594 596
595 597 requirements
596 598 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store (no-zstd no-dirstate-v2 !)
597 599 preserved: dotencode, fncache, generaldelta, revlog-compression-zstd, revlogv1, sparserevlog, store (zstd no-dirstate-v2 !)
598 600 preserved: dotencode, exp-dirstate-v2, fncache, generaldelta, revlog-compression-zstd, revlogv1, sparserevlog, store (zstd dirstate-v2 !)
599 601 removed: persistent-nodemap
600 602
601 603 processed revlogs:
602 604 - all-filelogs
603 605 - changelog
604 606 - manifest
605 607
606 608 beginning upgrade...
607 609 repository locked and read-only
608 610 creating temporary repository to stage upgraded data: $TESTTMP/test-repo/.hg/upgrade.* (glob)
609 611 (it is safe to interrupt this process any time before data migration completes)
610 612 downgrading repository to not use persistent nodemap feature
611 613 removing temporary repository $TESTTMP/test-repo/.hg/upgrade.* (glob)
612 614 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
613 615 00changelog-*.nd (glob)
614 616 00manifest-*.nd (glob)
615 617 undo.backup.00changelog.n
616 618 undo.backup.00manifest.n
617 619 $ hg debugnodemap --metadata
618 620
619 621
620 622 upgrading
621 623
622 624 $ cat << EOF >> .hg/hgrc
623 625 > [format]
624 626 > use-persistent-nodemap=yes
625 627 > EOF
626 628 $ hg debugformat -v
627 629 format-variant repo config default
628 630 fncache: yes yes yes
631 dirstate-v2: no no no
629 632 dotencode: yes yes yes
630 633 generaldelta: yes yes yes
631 634 share-safe: no no no
632 635 sparserevlog: yes yes yes
633 636 persistent-nodemap: no yes no
634 637 copies-sdc: no no no
635 638 revlog-v2: no no no
636 639 changelog-v2: no no no
637 640 plain-cl-delta: yes yes yes
638 641 compression: zlib zlib zlib (no-zstd !)
639 642 compression: zstd zstd zstd (zstd !)
640 643 compression-level: default default default
641 644 $ hg debugupgraderepo --run --no-backup
642 645 upgrade will perform the following actions:
643 646
644 647 requirements
645 648 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store (no-zstd no-dirstate-v2 !)
646 649 preserved: dotencode, fncache, generaldelta, revlog-compression-zstd, revlogv1, sparserevlog, store (zstd no-dirstate-v2 !)
647 650 preserved: dotencode, exp-dirstate-v2, fncache, generaldelta, revlog-compression-zstd, revlogv1, sparserevlog, store (zstd dirstate-v2 !)
648 651 added: persistent-nodemap
649 652
650 653 persistent-nodemap
651 654 Speedup revision lookup by node id.
652 655
653 656 processed revlogs:
654 657 - all-filelogs
655 658 - changelog
656 659 - manifest
657 660
658 661 beginning upgrade...
659 662 repository locked and read-only
660 663 creating temporary repository to stage upgraded data: $TESTTMP/test-repo/.hg/upgrade.* (glob)
661 664 (it is safe to interrupt this process any time before data migration completes)
662 665 upgrading repository to use persistent nodemap feature
663 666 removing temporary repository $TESTTMP/test-repo/.hg/upgrade.* (glob)
664 667 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
665 668 00changelog-*.nd (glob)
666 669 00changelog.n
667 670 00manifest-*.nd (glob)
668 671 00manifest.n
669 672 undo.backup.00changelog.n
670 673 undo.backup.00manifest.n
671 674
672 675 $ hg debugnodemap --metadata
673 676 uid: * (glob)
674 677 tip-rev: 5005
675 678 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
676 679 data-length: 121088
677 680 data-unused: 0
678 681 data-unused: 0.000%
679 682
680 683 Running unrelated upgrade
681 684
682 685 $ hg debugupgraderepo --run --no-backup --quiet --optimize re-delta-all
683 686 upgrade will perform the following actions:
684 687
685 688 requirements
686 689 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, sparserevlog, store (no-zstd no-dirstate-v2 !)
687 690 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlog-compression-zstd, revlogv1, sparserevlog, store (zstd no-dirstate-v2 !)
688 691 preserved: dotencode, exp-dirstate-v2, fncache, generaldelta, persistent-nodemap, revlog-compression-zstd, revlogv1, sparserevlog, store (zstd dirstate-v2 !)
689 692
690 693 optimisations: re-delta-all
691 694
692 695 processed revlogs:
693 696 - all-filelogs
694 697 - changelog
695 698 - manifest
696 699
697 700 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
698 701 00changelog-*.nd (glob)
699 702 00changelog.n
700 703 00manifest-*.nd (glob)
701 704 00manifest.n
702 705
703 706 $ hg debugnodemap --metadata
704 707 uid: * (glob)
705 708 tip-rev: 5005
706 709 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
707 710 data-length: 121088
708 711 data-unused: 0
709 712 data-unused: 0.000%
710 713
711 714 Persistent nodemap and local/streaming clone
712 715 ============================================
713 716
714 717 $ cd ..
715 718
716 719 standard clone
717 720 --------------
718 721
719 722 The persistent nodemap should exist after a streaming clone
720 723
721 724 $ hg clone --pull --quiet -U test-repo standard-clone
722 725 $ ls -1 standard-clone/.hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
723 726 00changelog-*.nd (glob)
724 727 00changelog.n
725 728 00manifest-*.nd (glob)
726 729 00manifest.n
727 730 $ hg -R standard-clone debugnodemap --metadata
728 731 uid: * (glob)
729 732 tip-rev: 5005
730 733 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
731 734 data-length: 121088
732 735 data-unused: 0
733 736 data-unused: 0.000%
734 737
735 738
736 739 local clone
737 740 ------------
738 741
739 742 The persistent nodemap should exist after a streaming clone
740 743
741 744 $ hg clone -U test-repo local-clone
742 745 $ ls -1 local-clone/.hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
743 746 00changelog-*.nd (glob)
744 747 00changelog.n
745 748 00manifest-*.nd (glob)
746 749 00manifest.n
747 750 $ hg -R local-clone debugnodemap --metadata
748 751 uid: * (glob)
749 752 tip-rev: 5005
750 753 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
751 754 data-length: 121088
752 755 data-unused: 0
753 756 data-unused: 0.000%
754 757
755 758 Test various corruption case
756 759 ============================
757 760
758 761 Missing datafile
759 762 ----------------
760 763
761 764 Test behavior with a missing datafile
762 765
763 766 $ hg clone --quiet --pull test-repo corruption-test-repo
764 767 $ ls -1 corruption-test-repo/.hg/store/00changelog*
765 768 corruption-test-repo/.hg/store/00changelog-*.nd (glob)
766 769 corruption-test-repo/.hg/store/00changelog.d
767 770 corruption-test-repo/.hg/store/00changelog.i
768 771 corruption-test-repo/.hg/store/00changelog.n
769 772 $ rm corruption-test-repo/.hg/store/00changelog*.nd
770 773 $ hg log -R corruption-test-repo -r .
771 774 changeset: 5005:90d5d3ba2fc4
772 775 tag: tip
773 776 user: test
774 777 date: Thu Jan 01 00:00:00 1970 +0000
775 778 summary: a2
776 779
777 780 $ ls -1 corruption-test-repo/.hg/store/00changelog*
778 781 corruption-test-repo/.hg/store/00changelog.d
779 782 corruption-test-repo/.hg/store/00changelog.i
780 783 corruption-test-repo/.hg/store/00changelog.n
781 784
782 785 Truncated data file
783 786 -------------------
784 787
785 788 Test behavior with a too short datafile
786 789
787 790 rebuild the missing data
788 791 $ hg -R corruption-test-repo debugupdatecache
789 792 $ ls -1 corruption-test-repo/.hg/store/00changelog*
790 793 corruption-test-repo/.hg/store/00changelog-*.nd (glob)
791 794 corruption-test-repo/.hg/store/00changelog.d
792 795 corruption-test-repo/.hg/store/00changelog.i
793 796 corruption-test-repo/.hg/store/00changelog.n
794 797
795 798 truncate the file
796 799
797 800 $ datafilepath=`ls corruption-test-repo/.hg/store/00changelog*.nd`
798 801 $ f -s $datafilepath
799 802 corruption-test-repo/.hg/store/00changelog-*.nd: size=121088 (glob)
800 803 $ dd if=$datafilepath bs=1000 count=10 of=$datafilepath-tmp status=noxfer
801 804 10+0 records in
802 805 10+0 records out
803 806 $ mv $datafilepath-tmp $datafilepath
804 807 $ f -s $datafilepath
805 808 corruption-test-repo/.hg/store/00changelog-*.nd: size=10000 (glob)
806 809
807 810 Check that Mercurial reaction to this event
808 811
809 812 $ hg -R corruption-test-repo log -r . --traceback
810 813 changeset: 5005:90d5d3ba2fc4
811 814 tag: tip
812 815 user: test
813 816 date: Thu Jan 01 00:00:00 1970 +0000
814 817 summary: a2
815 818
816 819
817 820
818 821 stream clone
819 822 ============
820 823
821 824 The persistent nodemap should exist after a streaming clone
822 825
823 826 Simple case
824 827 -----------
825 828
826 829 No race condition
827 830
828 831 $ hg clone -U --stream --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/test-repo stream-clone --debug | egrep '00(changelog|manifest)'
829 832 adding [s] 00manifest.n (62 bytes)
830 833 adding [s] 00manifest-*.nd (118 KB) (glob)
831 834 adding [s] 00changelog.n (62 bytes)
832 835 adding [s] 00changelog-*.nd (118 KB) (glob)
833 836 adding [s] 00manifest.d (452 KB) (no-zstd !)
834 837 adding [s] 00manifest.d (491 KB) (zstd !)
835 838 adding [s] 00changelog.d (360 KB) (no-zstd !)
836 839 adding [s] 00changelog.d (368 KB) (zstd !)
837 840 adding [s] 00manifest.i (313 KB)
838 841 adding [s] 00changelog.i (313 KB)
839 842 $ ls -1 stream-clone/.hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
840 843 00changelog-*.nd (glob)
841 844 00changelog.n
842 845 00manifest-*.nd (glob)
843 846 00manifest.n
844 847 $ hg -R stream-clone debugnodemap --metadata
845 848 uid: * (glob)
846 849 tip-rev: 5005
847 850 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
848 851 data-length: 121088
849 852 data-unused: 0
850 853 data-unused: 0.000%
851 854
852 855 new data appened
853 856 -----------------
854 857
855 858 Other commit happening on the server during the stream clone
856 859
857 860 setup the step-by-step stream cloning
858 861
859 862 $ HG_TEST_STREAM_WALKED_FILE_1="$TESTTMP/sync_file_walked_1"
860 863 $ export HG_TEST_STREAM_WALKED_FILE_1
861 864 $ HG_TEST_STREAM_WALKED_FILE_2="$TESTTMP/sync_file_walked_2"
862 865 $ export HG_TEST_STREAM_WALKED_FILE_2
863 866 $ HG_TEST_STREAM_WALKED_FILE_3="$TESTTMP/sync_file_walked_3"
864 867 $ export HG_TEST_STREAM_WALKED_FILE_3
865 868 $ cat << EOF >> test-repo/.hg/hgrc
866 869 > [extensions]
867 870 > steps=$RUNTESTDIR/testlib/ext-stream-clone-steps.py
868 871 > EOF
869 872
870 873 Check and record file state beforehand
871 874
872 875 $ f --size test-repo/.hg/store/00changelog*
873 876 test-repo/.hg/store/00changelog-*.nd: size=121088 (glob)
874 877 test-repo/.hg/store/00changelog.d: size=376891 (zstd !)
875 878 test-repo/.hg/store/00changelog.d: size=368890 (no-zstd !)
876 879 test-repo/.hg/store/00changelog.i: size=320384
877 880 test-repo/.hg/store/00changelog.n: size=62
878 881 $ hg -R test-repo debugnodemap --metadata | tee server-metadata.txt
879 882 uid: * (glob)
880 883 tip-rev: 5005
881 884 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
882 885 data-length: 121088
883 886 data-unused: 0
884 887 data-unused: 0.000%
885 888
886 889 Prepare a commit
887 890
888 891 $ echo foo >> test-repo/foo
889 892 $ hg -R test-repo/ add test-repo/foo
890 893
891 894 Do a mix of clone and commit at the same time so that the file listed on disk differ at actual transfer time.
892 895
893 896 $ (hg clone -U --stream --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/test-repo stream-clone-race-1 --debug 2>> clone-output | egrep '00(changelog|manifest)' >> clone-output; touch $HG_TEST_STREAM_WALKED_FILE_3) &
894 897 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_1
895 898 $ hg -R test-repo/ commit -m foo
896 899 $ touch $HG_TEST_STREAM_WALKED_FILE_2
897 900 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_3
898 901 $ cat clone-output
899 902 adding [s] 00manifest.n (62 bytes)
900 903 adding [s] 00manifest-*.nd (118 KB) (glob)
901 904 adding [s] 00changelog.n (62 bytes)
902 905 adding [s] 00changelog-*.nd (118 KB) (glob)
903 906 adding [s] 00manifest.d (452 KB) (no-zstd !)
904 907 adding [s] 00manifest.d (491 KB) (zstd !)
905 908 adding [s] 00changelog.d (360 KB) (no-zstd !)
906 909 adding [s] 00changelog.d (368 KB) (zstd !)
907 910 adding [s] 00manifest.i (313 KB)
908 911 adding [s] 00changelog.i (313 KB)
909 912
910 913 Check the result state
911 914
912 915 $ f --size stream-clone-race-1/.hg/store/00changelog*
913 916 stream-clone-race-1/.hg/store/00changelog-*.nd: size=121088 (glob)
914 917 stream-clone-race-1/.hg/store/00changelog.d: size=368890 (no-zstd !)
915 918 stream-clone-race-1/.hg/store/00changelog.d: size=376891 (zstd !)
916 919 stream-clone-race-1/.hg/store/00changelog.i: size=320384
917 920 stream-clone-race-1/.hg/store/00changelog.n: size=62
918 921
919 922 $ hg -R stream-clone-race-1 debugnodemap --metadata | tee client-metadata.txt
920 923 uid: * (glob)
921 924 tip-rev: 5005
922 925 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
923 926 data-length: 121088
924 927 data-unused: 0
925 928 data-unused: 0.000%
926 929
927 930 We get a usable nodemap, so no rewrite would be needed and the metadata should be identical
928 931 (ie: the following diff should be empty)
929 932
930 933 This isn't the case for the `no-rust` `no-pure` implementation as it use a very minimal nodemap implementation that unconditionnaly rewrite the nodemap "all the time".
931 934
932 935 #if no-rust no-pure
933 936 $ diff -u server-metadata.txt client-metadata.txt
934 937 --- server-metadata.txt * (glob)
935 938 +++ client-metadata.txt * (glob)
936 939 @@ -1,4 +1,4 @@
937 940 -uid: * (glob)
938 941 +uid: * (glob)
939 942 tip-rev: 5005
940 943 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
941 944 data-length: 121088
942 945 [1]
943 946 #else
944 947 $ diff -u server-metadata.txt client-metadata.txt
945 948 #endif
946 949
947 950
948 951 Clean up after the test.
949 952
950 953 $ rm -f "$HG_TEST_STREAM_WALKED_FILE_1"
951 954 $ rm -f "$HG_TEST_STREAM_WALKED_FILE_2"
952 955 $ rm -f "$HG_TEST_STREAM_WALKED_FILE_3"
953 956
954 957 full regeneration
955 958 -----------------
956 959
957 960 A full nodemap is generated
958 961
959 962 (ideally this test would append enough data to make sure the nodemap data file
960 963 get changed, however to make thing simpler we will force the regeneration for
961 964 this test.
962 965
963 966 Check the initial state
964 967
965 968 $ f --size test-repo/.hg/store/00changelog*
966 969 test-repo/.hg/store/00changelog-*.nd: size=121344 (glob) (rust !)
967 970 test-repo/.hg/store/00changelog-*.nd: size=121344 (glob) (pure !)
968 971 test-repo/.hg/store/00changelog-*.nd: size=121152 (glob) (no-rust no-pure !)
969 972 test-repo/.hg/store/00changelog.d: size=376950 (zstd !)
970 973 test-repo/.hg/store/00changelog.d: size=368949 (no-zstd !)
971 974 test-repo/.hg/store/00changelog.i: size=320448
972 975 test-repo/.hg/store/00changelog.n: size=62
973 976 $ hg -R test-repo debugnodemap --metadata | tee server-metadata-2.txt
974 977 uid: * (glob)
975 978 tip-rev: 5006
976 979 tip-node: ed2ec1eef9aa2a0ec5057c51483bc148d03e810b
977 980 data-length: 121344 (rust !)
978 981 data-length: 121344 (pure !)
979 982 data-length: 121152 (no-rust no-pure !)
980 983 data-unused: 192 (rust !)
981 984 data-unused: 192 (pure !)
982 985 data-unused: 0 (no-rust no-pure !)
983 986 data-unused: 0.158% (rust !)
984 987 data-unused: 0.158% (pure !)
985 988 data-unused: 0.000% (no-rust no-pure !)
986 989
987 990 Performe the mix of clone and full refresh of the nodemap, so that the files
988 991 (and filenames) are different between listing time and actual transfer time.
989 992
990 993 $ (hg clone -U --stream --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/test-repo stream-clone-race-2 --debug 2>> clone-output-2 | egrep '00(changelog|manifest)' >> clone-output-2; touch $HG_TEST_STREAM_WALKED_FILE_3) &
991 994 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_1
992 995 $ rm test-repo/.hg/store/00changelog.n
993 996 $ rm test-repo/.hg/store/00changelog-*.nd
994 997 $ hg -R test-repo/ debugupdatecache
995 998 $ touch $HG_TEST_STREAM_WALKED_FILE_2
996 999 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_3
997 1000 $ cat clone-output-2
998 1001 adding [s] 00manifest.n (62 bytes)
999 1002 adding [s] 00manifest-*.nd (118 KB) (glob)
1000 1003 adding [s] 00changelog.n (62 bytes)
1001 1004 adding [s] 00changelog-*.nd (118 KB) (glob)
1002 1005 adding [s] 00manifest.d (492 KB) (zstd !)
1003 1006 adding [s] 00manifest.d (452 KB) (no-zstd !)
1004 1007 adding [s] 00changelog.d (360 KB) (no-zstd !)
1005 1008 adding [s] 00changelog.d (368 KB) (zstd !)
1006 1009 adding [s] 00manifest.i (313 KB)
1007 1010 adding [s] 00changelog.i (313 KB)
1008 1011
1009 1012 Check the result.
1010 1013
1011 1014 $ f --size stream-clone-race-2/.hg/store/00changelog*
1012 1015 stream-clone-race-2/.hg/store/00changelog-*.nd: size=121344 (glob) (rust !)
1013 1016 stream-clone-race-2/.hg/store/00changelog-*.nd: size=121344 (glob) (pure !)
1014 1017 stream-clone-race-2/.hg/store/00changelog-*.nd: size=121152 (glob) (no-rust no-pure !)
1015 1018 stream-clone-race-2/.hg/store/00changelog.d: size=376950 (zstd !)
1016 1019 stream-clone-race-2/.hg/store/00changelog.d: size=368949 (no-zstd !)
1017 1020 stream-clone-race-2/.hg/store/00changelog.i: size=320448
1018 1021 stream-clone-race-2/.hg/store/00changelog.n: size=62
1019 1022
1020 1023 $ hg -R stream-clone-race-2 debugnodemap --metadata | tee client-metadata-2.txt
1021 1024 uid: * (glob)
1022 1025 tip-rev: 5006
1023 1026 tip-node: ed2ec1eef9aa2a0ec5057c51483bc148d03e810b
1024 1027 data-length: 121344 (rust !)
1025 1028 data-unused: 192 (rust !)
1026 1029 data-unused: 0.158% (rust !)
1027 1030 data-length: 121152 (no-rust no-pure !)
1028 1031 data-unused: 0 (no-rust no-pure !)
1029 1032 data-unused: 0.000% (no-rust no-pure !)
1030 1033 data-length: 121344 (pure !)
1031 1034 data-unused: 192 (pure !)
1032 1035 data-unused: 0.158% (pure !)
1033 1036
1034 1037 We get a usable nodemap, so no rewrite would be needed and the metadata should be identical
1035 1038 (ie: the following diff should be empty)
1036 1039
1037 1040 This isn't the case for the `no-rust` `no-pure` implementation as it use a very minimal nodemap implementation that unconditionnaly rewrite the nodemap "all the time".
1038 1041
1039 1042 #if no-rust no-pure
1040 1043 $ diff -u server-metadata-2.txt client-metadata-2.txt
1041 1044 --- server-metadata-2.txt * (glob)
1042 1045 +++ client-metadata-2.txt * (glob)
1043 1046 @@ -1,4 +1,4 @@
1044 1047 -uid: * (glob)
1045 1048 +uid: * (glob)
1046 1049 tip-rev: 5006
1047 1050 tip-node: ed2ec1eef9aa2a0ec5057c51483bc148d03e810b
1048 1051 data-length: 121152
1049 1052 [1]
1050 1053 #else
1051 1054 $ diff -u server-metadata-2.txt client-metadata-2.txt
1052 1055 #endif
1053 1056
1054 1057 Clean up after the test
1055 1058
1056 1059 $ rm -f $HG_TEST_STREAM_WALKED_FILE_1
1057 1060 $ rm -f $HG_TEST_STREAM_WALKED_FILE_2
1058 1061 $ rm -f $HG_TEST_STREAM_WALKED_FILE_3
1059 1062
@@ -1,122 +1,126
1 1 ==========================================================
2 2 Test file dedicated to checking side-data related behavior
3 3 ==========================================================
4 4
5 5 Check data can be written/read from sidedata
6 6 ============================================
7 7
8 8 $ cat << EOF >> $HGRCPATH
9 9 > [extensions]
10 10 > testsidedata=$TESTDIR/testlib/ext-sidedata.py
11 11 > EOF
12 12
13 13 $ hg init test-sidedata --config experimental.revlogv2=enable-unstable-format-and-corrupt-my-data
14 14 $ cd test-sidedata
15 15 $ echo aaa > a
16 16 $ hg add a
17 17 $ hg commit -m a --traceback
18 18 $ echo aaa > b
19 19 $ hg add b
20 20 $ hg commit -m b
21 21 $ echo xxx >> a
22 22 $ hg commit -m aa
23 23
24 24 $ hg debugsidedata -c 0
25 25 2 sidedata entries
26 26 entry-0001 size 4
27 27 entry-0002 size 32
28 28 $ hg debugsidedata -c 1 -v
29 29 2 sidedata entries
30 30 entry-0001 size 4
31 31 '\x00\x00\x006'
32 32 entry-0002 size 32
33 33 '\x98\t\xf9\xc4v\xf0\xc5P\x90\xf7wRf\xe8\xe27e\xfc\xc1\x93\xa4\x96\xd0\x1d\x97\xaaG\x1d\xd7t\xfa\xde'
34 34 $ hg debugsidedata -m 2
35 35 2 sidedata entries
36 36 entry-0001 size 4
37 37 entry-0002 size 32
38 38 $ hg debugsidedata a 1
39 39 2 sidedata entries
40 40 entry-0001 size 4
41 41 entry-0002 size 32
42 42
43 43 Check upgrade behavior
44 44 ======================
45 45
46 46 Right now, sidedata has not upgrade support
47 47
48 48 Check that we can upgrade to sidedata
49 49 -------------------------------------
50 50
51 51 $ hg init up-no-side-data --config experimental.revlogv2=no
52 52 $ hg debugformat -v -R up-no-side-data
53 53 format-variant repo config default
54 54 fncache: yes yes yes
55 dirstate-v2: no no no
55 56 dotencode: yes yes yes
56 57 generaldelta: yes yes yes
57 58 share-safe: no no no
58 59 sparserevlog: yes yes yes
59 60 persistent-nodemap: no no no (no-rust !)
60 61 persistent-nodemap: yes yes no (rust !)
61 62 copies-sdc: no no no
62 63 revlog-v2: no no no
63 64 changelog-v2: no no no
64 65 plain-cl-delta: yes yes yes
65 66 compression: zlib zlib zlib (no-zstd !)
66 67 compression: zstd zstd zstd (zstd !)
67 68 compression-level: default default default
68 69 $ hg debugformat -v -R up-no-side-data --config experimental.revlogv2=enable-unstable-format-and-corrupt-my-data
69 70 format-variant repo config default
70 71 fncache: yes yes yes
72 dirstate-v2: no no no
71 73 dotencode: yes yes yes
72 74 generaldelta: yes yes yes
73 75 share-safe: no no no
74 76 sparserevlog: yes yes yes
75 77 persistent-nodemap: no no no (no-rust !)
76 78 persistent-nodemap: yes yes no (rust !)
77 79 copies-sdc: no no no
78 80 revlog-v2: no yes no
79 81 changelog-v2: no no no
80 82 plain-cl-delta: yes yes yes
81 83 compression: zlib zlib zlib (no-zstd !)
82 84 compression: zstd zstd zstd (zstd !)
83 85 compression-level: default default default
84 86 $ hg debugupgraderepo -R up-no-side-data --config experimental.revlogv2=enable-unstable-format-and-corrupt-my-data > /dev/null
85 87
86 88 Check that we can downgrade from sidedata
87 89 -----------------------------------------
88 90
89 91 $ hg init up-side-data --config experimental.revlogv2=enable-unstable-format-and-corrupt-my-data
90 92 $ hg debugformat -v -R up-side-data
91 93 format-variant repo config default
92 94 fncache: yes yes yes
95 dirstate-v2: no no no
93 96 dotencode: yes yes yes
94 97 generaldelta: yes yes yes
95 98 share-safe: no no no
96 99 sparserevlog: yes yes yes
97 100 persistent-nodemap: no no no (no-rust !)
98 101 persistent-nodemap: yes yes no (rust !)
99 102 copies-sdc: no no no
100 103 revlog-v2: yes no no
101 104 changelog-v2: no no no
102 105 plain-cl-delta: yes yes yes
103 106 compression: zlib zlib zlib (no-zstd !)
104 107 compression: zstd zstd zstd (zstd !)
105 108 compression-level: default default default
106 109 $ hg debugformat -v -R up-side-data --config experimental.revlogv2=no
107 110 format-variant repo config default
108 111 fncache: yes yes yes
112 dirstate-v2: no no no
109 113 dotencode: yes yes yes
110 114 generaldelta: yes yes yes
111 115 share-safe: no no no
112 116 sparserevlog: yes yes yes
113 117 persistent-nodemap: no no no (no-rust !)
114 118 persistent-nodemap: yes yes no (rust !)
115 119 copies-sdc: no no no
116 120 revlog-v2: yes no no
117 121 changelog-v2: no no no
118 122 plain-cl-delta: yes yes yes
119 123 compression: zlib zlib zlib (no-zstd !)
120 124 compression: zstd zstd zstd (zstd !)
121 125 compression-level: default default default
122 126 $ hg debugupgraderepo -R up-side-data --config experimental.revlogv2=no > /dev/null
@@ -1,1615 +1,1737
1 1 #require no-reposimplestore
2 2
3 3 $ cat >> $HGRCPATH << EOF
4 4 > [extensions]
5 5 > share =
6 6 > [format]
7 7 > # stabilize test accross variant
8 8 > revlog-compression=zlib
9 9 > EOF
10 10
11 11 store and revlogv1 are required in source
12 12
13 13 $ hg --config format.usestore=false init no-store
14 14 $ hg -R no-store debugupgraderepo
15 15 abort: cannot upgrade repository; requirement missing: store
16 16 [255]
17 17
18 18 $ hg init no-revlogv1
19 19 $ cat > no-revlogv1/.hg/requires << EOF
20 20 > dotencode
21 21 > fncache
22 22 > generaldelta
23 23 > store
24 24 > EOF
25 25
26 26 $ hg -R no-revlogv1 debugupgraderepo
27 27 abort: cannot upgrade repository; missing a revlog version
28 28 [255]
29 29
30 30 Cannot upgrade shared repositories
31 31
32 32 $ hg init share-parent
33 33 $ hg -q share share-parent share-child
34 34
35 35 $ hg -R share-child debugupgraderepo
36 36 abort: cannot upgrade repository; unsupported source requirement: shared
37 37 [255]
38 38
39 39 Do not yet support upgrading treemanifest repos
40 40
41 41 $ hg --config experimental.treemanifest=true init treemanifest
42 42 $ hg -R treemanifest debugupgraderepo
43 43 abort: cannot upgrade repository; unsupported source requirement: treemanifest
44 44 [255]
45 45
46 46 Cannot add treemanifest requirement during upgrade
47 47
48 48 $ hg init disallowaddedreq
49 49 $ hg -R disallowaddedreq --config experimental.treemanifest=true debugupgraderepo
50 50 abort: cannot upgrade repository; do not support adding requirement: treemanifest
51 51 [255]
52 52
53 53 An upgrade of a repository created with recommended settings only suggests optimizations
54 54
55 55 $ hg init empty
56 56 $ cd empty
57 57 $ hg debugformat
58 58 format-variant repo
59 59 fncache: yes
60 dirstate-v2: no
60 61 dotencode: yes
61 62 generaldelta: yes
62 63 share-safe: no
63 64 sparserevlog: yes
64 65 persistent-nodemap: no (no-rust !)
65 66 persistent-nodemap: yes (rust !)
66 67 copies-sdc: no
67 68 revlog-v2: no
68 69 changelog-v2: no
69 70 plain-cl-delta: yes
70 71 compression: zlib
71 72 compression-level: default
72 73 $ hg debugformat --verbose
73 74 format-variant repo config default
74 75 fncache: yes yes yes
76 dirstate-v2: no no no
75 77 dotencode: yes yes yes
76 78 generaldelta: yes yes yes
77 79 share-safe: no no no
78 80 sparserevlog: yes yes yes
79 81 persistent-nodemap: no no no (no-rust !)
80 82 persistent-nodemap: yes yes no (rust !)
81 83 copies-sdc: no no no
82 84 revlog-v2: no no no
83 85 changelog-v2: no no no
84 86 plain-cl-delta: yes yes yes
85 87 compression: zlib zlib zlib (no-zstd !)
86 88 compression: zlib zlib zstd (zstd !)
87 89 compression-level: default default default
88 90 $ hg debugformat --verbose --config format.usefncache=no
89 91 format-variant repo config default
90 92 fncache: yes no yes
93 dirstate-v2: no no no
91 94 dotencode: yes no yes
92 95 generaldelta: yes yes yes
93 96 share-safe: no no no
94 97 sparserevlog: yes yes yes
95 98 persistent-nodemap: no no no (no-rust !)
96 99 persistent-nodemap: yes yes no (rust !)
97 100 copies-sdc: no no no
98 101 revlog-v2: no no no
99 102 changelog-v2: no no no
100 103 plain-cl-delta: yes yes yes
101 104 compression: zlib zlib zlib (no-zstd !)
102 105 compression: zlib zlib zstd (zstd !)
103 106 compression-level: default default default
104 107 $ hg debugformat --verbose --config format.usefncache=no --color=debug
105 108 format-variant repo config default
106 109 [formatvariant.name.mismatchconfig|fncache: ][formatvariant.repo.mismatchconfig| yes][formatvariant.config.special| no][formatvariant.default| yes]
110 [formatvariant.name.uptodate|dirstate-v2: ][formatvariant.repo.uptodate| no][formatvariant.config.default| no][formatvariant.default| no]
107 111 [formatvariant.name.mismatchconfig|dotencode: ][formatvariant.repo.mismatchconfig| yes][formatvariant.config.special| no][formatvariant.default| yes]
108 112 [formatvariant.name.uptodate|generaldelta: ][formatvariant.repo.uptodate| yes][formatvariant.config.default| yes][formatvariant.default| yes]
109 113 [formatvariant.name.uptodate|share-safe: ][formatvariant.repo.uptodate| no][formatvariant.config.default| no][formatvariant.default| no]
110 114 [formatvariant.name.uptodate|sparserevlog: ][formatvariant.repo.uptodate| yes][formatvariant.config.default| yes][formatvariant.default| yes]
111 115 [formatvariant.name.uptodate|persistent-nodemap:][formatvariant.repo.uptodate| no][formatvariant.config.default| no][formatvariant.default| no] (no-rust !)
112 116 [formatvariant.name.mismatchdefault|persistent-nodemap:][formatvariant.repo.mismatchdefault| yes][formatvariant.config.special| yes][formatvariant.default| no] (rust !)
113 117 [formatvariant.name.uptodate|copies-sdc: ][formatvariant.repo.uptodate| no][formatvariant.config.default| no][formatvariant.default| no]
114 118 [formatvariant.name.uptodate|revlog-v2: ][formatvariant.repo.uptodate| no][formatvariant.config.default| no][formatvariant.default| no]
115 119 [formatvariant.name.uptodate|changelog-v2: ][formatvariant.repo.uptodate| no][formatvariant.config.default| no][formatvariant.default| no]
116 120 [formatvariant.name.uptodate|plain-cl-delta: ][formatvariant.repo.uptodate| yes][formatvariant.config.default| yes][formatvariant.default| yes]
117 121 [formatvariant.name.uptodate|compression: ][formatvariant.repo.uptodate| zlib][formatvariant.config.default| zlib][formatvariant.default| zlib] (no-zstd !)
118 122 [formatvariant.name.mismatchdefault|compression: ][formatvariant.repo.mismatchdefault| zlib][formatvariant.config.special| zlib][formatvariant.default| zstd] (zstd !)
119 123 [formatvariant.name.uptodate|compression-level: ][formatvariant.repo.uptodate| default][formatvariant.config.default| default][formatvariant.default| default]
120 124 $ hg debugformat -Tjson
121 125 [
122 126 {
123 127 "config": true,
124 128 "default": true,
125 129 "name": "fncache",
126 130 "repo": true
127 131 },
128 132 {
133 "config": false,
134 "default": false,
135 "name": "dirstate-v2",
136 "repo": false
137 },
138 {
129 139 "config": true,
130 140 "default": true,
131 141 "name": "dotencode",
132 142 "repo": true
133 143 },
134 144 {
135 145 "config": true,
136 146 "default": true,
137 147 "name": "generaldelta",
138 148 "repo": true
139 149 },
140 150 {
141 151 "config": false,
142 152 "default": false,
143 153 "name": "share-safe",
144 154 "repo": false
145 155 },
146 156 {
147 157 "config": true,
148 158 "default": true,
149 159 "name": "sparserevlog",
150 160 "repo": true
151 161 },
152 162 {
153 163 "config": false, (no-rust !)
154 164 "config": true, (rust !)
155 165 "default": false,
156 166 "name": "persistent-nodemap",
157 167 "repo": false (no-rust !)
158 168 "repo": true (rust !)
159 169 },
160 170 {
161 171 "config": false,
162 172 "default": false,
163 173 "name": "copies-sdc",
164 174 "repo": false
165 175 },
166 176 {
167 177 "config": false,
168 178 "default": false,
169 179 "name": "revlog-v2",
170 180 "repo": false
171 181 },
172 182 {
173 183 "config": false,
174 184 "default": false,
175 185 "name": "changelog-v2",
176 186 "repo": false
177 187 },
178 188 {
179 189 "config": true,
180 190 "default": true,
181 191 "name": "plain-cl-delta",
182 192 "repo": true
183 193 },
184 194 {
185 195 "config": "zlib",
186 196 "default": "zlib", (no-zstd !)
187 197 "default": "zstd", (zstd !)
188 198 "name": "compression",
189 199 "repo": "zlib"
190 200 },
191 201 {
192 202 "config": "default",
193 203 "default": "default",
194 204 "name": "compression-level",
195 205 "repo": "default"
196 206 }
197 207 ]
198 208 $ hg debugupgraderepo
199 209 (no format upgrades found in existing repository)
200 210 performing an upgrade with "--run" will make the following changes:
201 211
202 212 requirements
203 213 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store (no-rust !)
204 214 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, sparserevlog, store (rust !)
205 215
206 216 processed revlogs:
207 217 - all-filelogs
208 218 - changelog
209 219 - manifest
210 220
211 221 additional optimizations are available by specifying "--optimize <name>":
212 222
213 223 re-delta-parent
214 224 deltas within internal storage will be recalculated to choose an optimal base revision where this was not already done; the size of the repository may shrink and various operations may become faster; the first time this optimization is performed could slow down upgrade execution considerably; subsequent invocations should not run noticeably slower
215 225
216 226 re-delta-multibase
217 227 deltas within internal storage will be recalculated against multiple base revision and the smallest difference will be used; the size of the repository may shrink significantly when there are many merges; this optimization will slow down execution in proportion to the number of merges in the repository and the amount of files in the repository; this slow down should not be significant unless there are tens of thousands of files and thousands of merges
218 228
219 229 re-delta-all
220 230 deltas within internal storage will always be recalculated without reusing prior deltas; this will likely make execution run several times slower; this optimization is typically not needed
221 231
222 232 re-delta-fulladd
223 233 every revision will be re-added as if it was new content. It will go through the full storage mechanism giving extensions a chance to process it (eg. lfs). This is similar to "re-delta-all" but even slower since more logic is involved.
224 234
225 235
226 236 $ hg debugupgraderepo --quiet
227 237 requirements
228 238 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store (no-rust !)
229 239 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, sparserevlog, store (rust !)
230 240
231 241 processed revlogs:
232 242 - all-filelogs
233 243 - changelog
234 244 - manifest
235 245
236 246
237 247 --optimize can be used to add optimizations
238 248
239 249 $ hg debugupgrade --optimize 're-delta-parent'
240 250 (no format upgrades found in existing repository)
241 251 performing an upgrade with "--run" will make the following changes:
242 252
243 253 requirements
244 254 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store (no-rust !)
245 255 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, sparserevlog, store (rust !)
246 256
247 257 optimisations: re-delta-parent
248 258
249 259 re-delta-parent
250 260 deltas within internal storage will choose a new base revision if needed
251 261
252 262 processed revlogs:
253 263 - all-filelogs
254 264 - changelog
255 265 - manifest
256 266
257 267 additional optimizations are available by specifying "--optimize <name>":
258 268
259 269 re-delta-multibase
260 270 deltas within internal storage will be recalculated against multiple base revision and the smallest difference will be used; the size of the repository may shrink significantly when there are many merges; this optimization will slow down execution in proportion to the number of merges in the repository and the amount of files in the repository; this slow down should not be significant unless there are tens of thousands of files and thousands of merges
261 271
262 272 re-delta-all
263 273 deltas within internal storage will always be recalculated without reusing prior deltas; this will likely make execution run several times slower; this optimization is typically not needed
264 274
265 275 re-delta-fulladd
266 276 every revision will be re-added as if it was new content. It will go through the full storage mechanism giving extensions a chance to process it (eg. lfs). This is similar to "re-delta-all" but even slower since more logic is involved.
267 277
268 278
269 279 modern form of the option
270 280
271 281 $ hg debugupgrade --optimize re-delta-parent
272 282 (no format upgrades found in existing repository)
273 283 performing an upgrade with "--run" will make the following changes:
274 284
275 285 requirements
276 286 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store (no-rust !)
277 287 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, sparserevlog, store (rust !)
278 288
279 289 optimisations: re-delta-parent
280 290
281 291 re-delta-parent
282 292 deltas within internal storage will choose a new base revision if needed
283 293
284 294 processed revlogs:
285 295 - all-filelogs
286 296 - changelog
287 297 - manifest
288 298
289 299 additional optimizations are available by specifying "--optimize <name>":
290 300
291 301 re-delta-multibase
292 302 deltas within internal storage will be recalculated against multiple base revision and the smallest difference will be used; the size of the repository may shrink significantly when there are many merges; this optimization will slow down execution in proportion to the number of merges in the repository and the amount of files in the repository; this slow down should not be significant unless there are tens of thousands of files and thousands of merges
293 303
294 304 re-delta-all
295 305 deltas within internal storage will always be recalculated without reusing prior deltas; this will likely make execution run several times slower; this optimization is typically not needed
296 306
297 307 re-delta-fulladd
298 308 every revision will be re-added as if it was new content. It will go through the full storage mechanism giving extensions a chance to process it (eg. lfs). This is similar to "re-delta-all" but even slower since more logic is involved.
299 309
300 310 $ hg debugupgrade --optimize re-delta-parent --quiet
301 311 requirements
302 312 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store (no-rust !)
303 313 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, sparserevlog, store (rust !)
304 314
305 315 optimisations: re-delta-parent
306 316
307 317 processed revlogs:
308 318 - all-filelogs
309 319 - changelog
310 320 - manifest
311 321
312 322
313 323 unknown optimization:
314 324
315 325 $ hg debugupgrade --optimize foobar
316 326 abort: unknown optimization action requested: foobar
317 327 (run without arguments to see valid optimizations)
318 328 [255]
319 329
320 330 Various sub-optimal detections work
321 331
322 332 $ cat > .hg/requires << EOF
323 333 > revlogv1
324 334 > store
325 335 > EOF
326 336
327 337 $ hg debugformat
328 338 format-variant repo
329 339 fncache: no
340 dirstate-v2: no
330 341 dotencode: no
331 342 generaldelta: no
332 343 share-safe: no
333 344 sparserevlog: no
334 345 persistent-nodemap: no
335 346 copies-sdc: no
336 347 revlog-v2: no
337 348 changelog-v2: no
338 349 plain-cl-delta: yes
339 350 compression: zlib
340 351 compression-level: default
341 352 $ hg debugformat --verbose
342 353 format-variant repo config default
343 354 fncache: no yes yes
355 dirstate-v2: no no no
344 356 dotencode: no yes yes
345 357 generaldelta: no yes yes
346 358 share-safe: no no no
347 359 sparserevlog: no yes yes
348 360 persistent-nodemap: no no no (no-rust !)
349 361 persistent-nodemap: no yes no (rust !)
350 362 copies-sdc: no no no
351 363 revlog-v2: no no no
352 364 changelog-v2: no no no
353 365 plain-cl-delta: yes yes yes
354 366 compression: zlib zlib zlib (no-zstd !)
355 367 compression: zlib zlib zstd (zstd !)
356 368 compression-level: default default default
357 369 $ hg debugformat --verbose --config format.usegeneraldelta=no
358 370 format-variant repo config default
359 371 fncache: no yes yes
372 dirstate-v2: no no no
360 373 dotencode: no yes yes
361 374 generaldelta: no no yes
362 375 share-safe: no no no
363 376 sparserevlog: no no yes
364 377 persistent-nodemap: no no no (no-rust !)
365 378 persistent-nodemap: no yes no (rust !)
366 379 copies-sdc: no no no
367 380 revlog-v2: no no no
368 381 changelog-v2: no no no
369 382 plain-cl-delta: yes yes yes
370 383 compression: zlib zlib zlib (no-zstd !)
371 384 compression: zlib zlib zstd (zstd !)
372 385 compression-level: default default default
373 386 $ hg debugformat --verbose --config format.usegeneraldelta=no --color=debug
374 387 format-variant repo config default
375 388 [formatvariant.name.mismatchconfig|fncache: ][formatvariant.repo.mismatchconfig| no][formatvariant.config.default| yes][formatvariant.default| yes]
389 [formatvariant.name.uptodate|dirstate-v2: ][formatvariant.repo.uptodate| no][formatvariant.config.default| no][formatvariant.default| no]
376 390 [formatvariant.name.mismatchconfig|dotencode: ][formatvariant.repo.mismatchconfig| no][formatvariant.config.default| yes][formatvariant.default| yes]
377 391 [formatvariant.name.mismatchdefault|generaldelta: ][formatvariant.repo.mismatchdefault| no][formatvariant.config.special| no][formatvariant.default| yes]
378 392 [formatvariant.name.uptodate|share-safe: ][formatvariant.repo.uptodate| no][formatvariant.config.default| no][formatvariant.default| no]
379 393 [formatvariant.name.mismatchdefault|sparserevlog: ][formatvariant.repo.mismatchdefault| no][formatvariant.config.special| no][formatvariant.default| yes]
380 394 [formatvariant.name.uptodate|persistent-nodemap:][formatvariant.repo.uptodate| no][formatvariant.config.default| no][formatvariant.default| no] (no-rust !)
381 395 [formatvariant.name.mismatchconfig|persistent-nodemap:][formatvariant.repo.mismatchconfig| no][formatvariant.config.special| yes][formatvariant.default| no] (rust !)
382 396 [formatvariant.name.uptodate|copies-sdc: ][formatvariant.repo.uptodate| no][formatvariant.config.default| no][formatvariant.default| no]
383 397 [formatvariant.name.uptodate|revlog-v2: ][formatvariant.repo.uptodate| no][formatvariant.config.default| no][formatvariant.default| no]
384 398 [formatvariant.name.uptodate|changelog-v2: ][formatvariant.repo.uptodate| no][formatvariant.config.default| no][formatvariant.default| no]
385 399 [formatvariant.name.uptodate|plain-cl-delta: ][formatvariant.repo.uptodate| yes][formatvariant.config.default| yes][formatvariant.default| yes]
386 400 [formatvariant.name.uptodate|compression: ][formatvariant.repo.uptodate| zlib][formatvariant.config.default| zlib][formatvariant.default| zlib] (no-zstd !)
387 401 [formatvariant.name.mismatchdefault|compression: ][formatvariant.repo.mismatchdefault| zlib][formatvariant.config.special| zlib][formatvariant.default| zstd] (zstd !)
388 402 [formatvariant.name.uptodate|compression-level: ][formatvariant.repo.uptodate| default][formatvariant.config.default| default][formatvariant.default| default]
389 403 $ hg debugupgraderepo
390 404 repository lacks features recommended by current config options:
391 405
392 406 fncache
393 407 long and reserved filenames may not work correctly; repository performance is sub-optimal
394 408
395 409 dotencode
396 410 storage of filenames beginning with a period or space may not work correctly
397 411
398 412 generaldelta
399 413 deltas within internal storage are unable to choose optimal revisions; repository is larger and slower than it could be; interaction with other repositories may require extra network and CPU resources, making "hg push" and "hg pull" slower
400 414
401 415 sparserevlog
402 416 in order to limit disk reading and memory usage on older version, the span of a delta chain from its root to its end is limited, whatever the relevant data in this span. This can severly limit Mercurial ability to build good chain of delta resulting is much more storage space being taken and limit reusability of on disk delta during exchange.
403 417
404 418 persistent-nodemap (rust !)
405 419 persist the node -> rev mapping on disk to speedup lookup (rust !)
406 420 (rust !)
407 421
408 422 performing an upgrade with "--run" will make the following changes:
409 423
410 424 requirements
411 425 preserved: revlogv1, store
412 426 added: dotencode, fncache, generaldelta, sparserevlog (no-rust !)
413 427 added: dotencode, fncache, generaldelta, persistent-nodemap, sparserevlog (rust !)
414 428
415 429 fncache
416 430 repository will be more resilient to storing certain paths and performance of certain operations should be improved
417 431
418 432 dotencode
419 433 repository will be better able to store files beginning with a space or period
420 434
421 435 generaldelta
422 436 repository storage will be able to create optimal deltas; new repository data will be smaller and read times should decrease; interacting with other repositories using this storage model should require less network and CPU resources, making "hg push" and "hg pull" faster
423 437
424 438 sparserevlog
425 439 Revlog supports delta chain with more unused data between payload. These gaps will be skipped at read time. This allows for better delta chains, making a better compression and faster exchange with server.
426 440
427 441 persistent-nodemap (rust !)
428 442 Speedup revision lookup by node id. (rust !)
429 443 (rust !)
430 444 processed revlogs:
431 445 - all-filelogs
432 446 - changelog
433 447 - manifest
434 448
435 449 additional optimizations are available by specifying "--optimize <name>":
436 450
437 451 re-delta-parent
438 452 deltas within internal storage will be recalculated to choose an optimal base revision where this was not already done; the size of the repository may shrink and various operations may become faster; the first time this optimization is performed could slow down upgrade execution considerably; subsequent invocations should not run noticeably slower
439 453
440 454 re-delta-multibase
441 455 deltas within internal storage will be recalculated against multiple base revision and the smallest difference will be used; the size of the repository may shrink significantly when there are many merges; this optimization will slow down execution in proportion to the number of merges in the repository and the amount of files in the repository; this slow down should not be significant unless there are tens of thousands of files and thousands of merges
442 456
443 457 re-delta-all
444 458 deltas within internal storage will always be recalculated without reusing prior deltas; this will likely make execution run several times slower; this optimization is typically not needed
445 459
446 460 re-delta-fulladd
447 461 every revision will be re-added as if it was new content. It will go through the full storage mechanism giving extensions a chance to process it (eg. lfs). This is similar to "re-delta-all" but even slower since more logic is involved.
448 462
449 463 $ hg debugupgraderepo --quiet
450 464 requirements
451 465 preserved: revlogv1, store
452 466 added: dotencode, fncache, generaldelta, sparserevlog (no-rust !)
453 467 added: dotencode, fncache, generaldelta, persistent-nodemap, sparserevlog (rust !)
454 468
455 469 processed revlogs:
456 470 - all-filelogs
457 471 - changelog
458 472 - manifest
459 473
460 474
461 475 $ hg --config format.dotencode=false debugupgraderepo
462 476 repository lacks features recommended by current config options:
463 477
464 478 fncache
465 479 long and reserved filenames may not work correctly; repository performance is sub-optimal
466 480
467 481 generaldelta
468 482 deltas within internal storage are unable to choose optimal revisions; repository is larger and slower than it could be; interaction with other repositories may require extra network and CPU resources, making "hg push" and "hg pull" slower
469 483
470 484 sparserevlog
471 485 in order to limit disk reading and memory usage on older version, the span of a delta chain from its root to its end is limited, whatever the relevant data in this span. This can severly limit Mercurial ability to build good chain of delta resulting is much more storage space being taken and limit reusability of on disk delta during exchange.
472 486
473 487 persistent-nodemap (rust !)
474 488 persist the node -> rev mapping on disk to speedup lookup (rust !)
475 489 (rust !)
476 490 repository lacks features used by the default config options:
477 491
478 492 dotencode
479 493 storage of filenames beginning with a period or space may not work correctly
480 494
481 495
482 496 performing an upgrade with "--run" will make the following changes:
483 497
484 498 requirements
485 499 preserved: revlogv1, store
486 500 added: fncache, generaldelta, sparserevlog (no-rust !)
487 501 added: fncache, generaldelta, persistent-nodemap, sparserevlog (rust !)
488 502
489 503 fncache
490 504 repository will be more resilient to storing certain paths and performance of certain operations should be improved
491 505
492 506 generaldelta
493 507 repository storage will be able to create optimal deltas; new repository data will be smaller and read times should decrease; interacting with other repositories using this storage model should require less network and CPU resources, making "hg push" and "hg pull" faster
494 508
495 509 sparserevlog
496 510 Revlog supports delta chain with more unused data between payload. These gaps will be skipped at read time. This allows for better delta chains, making a better compression and faster exchange with server.
497 511
498 512 persistent-nodemap (rust !)
499 513 Speedup revision lookup by node id. (rust !)
500 514 (rust !)
501 515 processed revlogs:
502 516 - all-filelogs
503 517 - changelog
504 518 - manifest
505 519
506 520 additional optimizations are available by specifying "--optimize <name>":
507 521
508 522 re-delta-parent
509 523 deltas within internal storage will be recalculated to choose an optimal base revision where this was not already done; the size of the repository may shrink and various operations may become faster; the first time this optimization is performed could slow down upgrade execution considerably; subsequent invocations should not run noticeably slower
510 524
511 525 re-delta-multibase
512 526 deltas within internal storage will be recalculated against multiple base revision and the smallest difference will be used; the size of the repository may shrink significantly when there are many merges; this optimization will slow down execution in proportion to the number of merges in the repository and the amount of files in the repository; this slow down should not be significant unless there are tens of thousands of files and thousands of merges
513 527
514 528 re-delta-all
515 529 deltas within internal storage will always be recalculated without reusing prior deltas; this will likely make execution run several times slower; this optimization is typically not needed
516 530
517 531 re-delta-fulladd
518 532 every revision will be re-added as if it was new content. It will go through the full storage mechanism giving extensions a chance to process it (eg. lfs). This is similar to "re-delta-all" but even slower since more logic is involved.
519 533
520 534
521 535 $ cd ..
522 536
523 537 Upgrading a repository that is already modern essentially no-ops
524 538
525 539 $ hg init modern
526 540 $ hg -R modern debugupgraderepo --run
527 541 nothing to do
528 542
529 543 Upgrading a repository to generaldelta works
530 544
531 545 $ hg --config format.usegeneraldelta=false init upgradegd
532 546 $ cd upgradegd
533 547 $ touch f0
534 548 $ hg -q commit -A -m initial
535 549 $ mkdir FooBarDirectory.d
536 550 $ touch FooBarDirectory.d/f1
537 551 $ hg -q commit -A -m 'add f1'
538 552 $ hg -q up -r 0
539 553 >>> from __future__ import absolute_import, print_function
540 554 >>> import random
541 555 >>> random.seed(0) # have a reproducible content
542 556 >>> with open("f2", "wb") as f:
543 557 ... for i in range(100000):
544 558 ... f.write(b"%d\n" % random.randint(1000000000, 9999999999)) and None
545 559 $ hg -q commit -A -m 'add f2'
546 560
547 561 make sure we have a .d file
548 562
549 563 $ ls -d .hg/store/data/*
550 564 .hg/store/data/_foo_bar_directory.d.hg
551 565 .hg/store/data/f0.i
552 566 .hg/store/data/f2.d
553 567 .hg/store/data/f2.i
554 568
555 569 $ hg debugupgraderepo --run --config format.sparse-revlog=false
556 570 upgrade will perform the following actions:
557 571
558 572 requirements
559 573 preserved: dotencode, fncache, revlogv1, store (no-rust !)
560 574 preserved: dotencode, fncache, persistent-nodemap, revlogv1, store (rust !)
561 575 added: generaldelta
562 576
563 577 generaldelta
564 578 repository storage will be able to create optimal deltas; new repository data will be smaller and read times should decrease; interacting with other repositories using this storage model should require less network and CPU resources, making "hg push" and "hg pull" faster
565 579
566 580 processed revlogs:
567 581 - all-filelogs
568 582 - changelog
569 583 - manifest
570 584
571 585 beginning upgrade...
572 586 repository locked and read-only
573 587 creating temporary repository to stage upgraded data: $TESTTMP/upgradegd/.hg/upgrade.* (glob)
574 588 (it is safe to interrupt this process any time before data migration completes)
575 589 migrating 9 total revisions (3 in filelogs, 3 in manifests, 3 in changelog)
576 590 migrating 519 KB in store; 1.05 MB tracked data
577 591 migrating 3 filelogs containing 3 revisions (518 KB in store; 1.05 MB tracked data)
578 592 finished migrating 3 filelog revisions across 3 filelogs; change in size: 0 bytes
579 593 migrating 1 manifests containing 3 revisions (384 bytes in store; 238 bytes tracked data)
580 594 finished migrating 3 manifest revisions across 1 manifests; change in size: -17 bytes
581 595 migrating changelog containing 3 revisions (394 bytes in store; 199 bytes tracked data)
582 596 finished migrating 3 changelog revisions; change in size: 0 bytes
583 597 finished migrating 9 total revisions; total change in store size: -17 bytes
584 598 copying phaseroots
585 599 data fully upgraded in a temporary repository
586 600 marking source repository as being upgraded; clients will be unable to read from repository
587 601 starting in-place swap of repository data
588 602 replaced files will be backed up at $TESTTMP/upgradegd/.hg/upgradebackup.* (glob)
589 603 replacing store...
590 604 store replacement complete; repository was inconsistent for *s (glob)
591 605 finalizing requirements file and making repository readable again
592 606 removing temporary repository $TESTTMP/upgradegd/.hg/upgrade.* (glob)
593 607 copy of old repository backed up at $TESTTMP/upgradegd/.hg/upgradebackup.* (glob)
594 608 the old repository will not be deleted; remove it to free up disk space once the upgraded repository is verified
595 609
596 610 Original requirements backed up
597 611
598 612 $ cat .hg/upgradebackup.*/requires
599 613 dotencode
600 614 fncache
601 615 persistent-nodemap (rust !)
602 616 revlogv1
603 617 store
604 618
605 619 generaldelta added to original requirements files
606 620
607 621 $ cat .hg/requires
608 622 dotencode
609 623 fncache
610 624 generaldelta
611 625 persistent-nodemap (rust !)
612 626 revlogv1
613 627 store
614 628
615 629 store directory has files we expect
616 630
617 631 $ ls .hg/store
618 632 00changelog.i
619 633 00manifest.i
620 634 data
621 635 fncache
622 636 phaseroots
623 637 undo
624 638 undo.backupfiles
625 639 undo.phaseroots
626 640
627 641 manifest should be generaldelta
628 642
629 643 $ hg debugrevlog -m | grep flags
630 644 flags : inline, generaldelta
631 645
632 646 verify should be happy
633 647
634 648 $ hg verify
635 649 checking changesets
636 650 checking manifests
637 651 crosschecking files in changesets and manifests
638 652 checking files
639 653 checked 3 changesets with 3 changes to 3 files
640 654
641 655 old store should be backed up
642 656
643 657 $ ls -d .hg/upgradebackup.*/
644 658 .hg/upgradebackup.*/ (glob)
645 659 $ ls .hg/upgradebackup.*/store
646 660 00changelog.i
647 661 00manifest.i
648 662 data
649 663 fncache
650 664 phaseroots
651 665 undo
652 666 undo.backup.fncache
653 667 undo.backupfiles
654 668 undo.phaseroots
655 669
656 670 unless --no-backup is passed
657 671
658 672 $ rm -rf .hg/upgradebackup.*/
659 673 $ hg debugupgraderepo --run --no-backup
660 674 upgrade will perform the following actions:
661 675
662 676 requirements
663 677 preserved: dotencode, fncache, generaldelta, revlogv1, store (no-rust !)
664 678 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, store (rust !)
665 679 added: sparserevlog
666 680
667 681 sparserevlog
668 682 Revlog supports delta chain with more unused data between payload. These gaps will be skipped at read time. This allows for better delta chains, making a better compression and faster exchange with server.
669 683
670 684 processed revlogs:
671 685 - all-filelogs
672 686 - changelog
673 687 - manifest
674 688
675 689 beginning upgrade...
676 690 repository locked and read-only
677 691 creating temporary repository to stage upgraded data: $TESTTMP/upgradegd/.hg/upgrade.* (glob)
678 692 (it is safe to interrupt this process any time before data migration completes)
679 693 migrating 9 total revisions (3 in filelogs, 3 in manifests, 3 in changelog)
680 694 migrating 519 KB in store; 1.05 MB tracked data
681 695 migrating 3 filelogs containing 3 revisions (518 KB in store; 1.05 MB tracked data)
682 696 finished migrating 3 filelog revisions across 3 filelogs; change in size: 0 bytes
683 697 migrating 1 manifests containing 3 revisions (367 bytes in store; 238 bytes tracked data)
684 698 finished migrating 3 manifest revisions across 1 manifests; change in size: 0 bytes
685 699 migrating changelog containing 3 revisions (394 bytes in store; 199 bytes tracked data)
686 700 finished migrating 3 changelog revisions; change in size: 0 bytes
687 701 finished migrating 9 total revisions; total change in store size: 0 bytes
688 702 copying phaseroots
689 703 data fully upgraded in a temporary repository
690 704 marking source repository as being upgraded; clients will be unable to read from repository
691 705 starting in-place swap of repository data
692 706 replacing store...
693 707 store replacement complete; repository was inconsistent for * (glob)
694 708 finalizing requirements file and making repository readable again
695 709 removing temporary repository $TESTTMP/upgradegd/.hg/upgrade.* (glob)
696 710 $ ls -1 .hg/ | grep upgradebackup
697 711 [1]
698 712
699 713 We can restrict optimization to some revlog:
700 714
701 715 $ hg debugupgrade --optimize re-delta-parent --run --manifest --no-backup --debug --traceback
702 716 upgrade will perform the following actions:
703 717
704 718 requirements
705 719 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store (no-rust !)
706 720 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, sparserevlog, store (rust !)
707 721
708 722 optimisations: re-delta-parent
709 723
710 724 re-delta-parent
711 725 deltas within internal storage will choose a new base revision if needed
712 726
713 727 processed revlogs:
714 728 - manifest
715 729
716 730 beginning upgrade...
717 731 repository locked and read-only
718 732 creating temporary repository to stage upgraded data: $TESTTMP/upgradegd/.hg/upgrade.* (glob)
719 733 (it is safe to interrupt this process any time before data migration completes)
720 734 migrating 9 total revisions (3 in filelogs, 3 in manifests, 3 in changelog)
721 735 migrating 519 KB in store; 1.05 MB tracked data
722 736 migrating 3 filelogs containing 3 revisions (518 KB in store; 1.05 MB tracked data)
723 737 blindly copying data/FooBarDirectory.d/f1.i containing 1 revisions
724 738 blindly copying data/f0.i containing 1 revisions
725 739 blindly copying data/f2.i containing 1 revisions
726 740 finished migrating 3 filelog revisions across 3 filelogs; change in size: 0 bytes
727 741 migrating 1 manifests containing 3 revisions (367 bytes in store; 238 bytes tracked data)
728 742 cloning 3 revisions from 00manifest.i
729 743 finished migrating 3 manifest revisions across 1 manifests; change in size: 0 bytes
730 744 migrating changelog containing 3 revisions (394 bytes in store; 199 bytes tracked data)
731 745 blindly copying 00changelog.i containing 3 revisions
732 746 finished migrating 3 changelog revisions; change in size: 0 bytes
733 747 finished migrating 9 total revisions; total change in store size: 0 bytes
734 748 copying phaseroots
735 749 data fully upgraded in a temporary repository
736 750 marking source repository as being upgraded; clients will be unable to read from repository
737 751 starting in-place swap of repository data
738 752 replacing store...
739 753 store replacement complete; repository was inconsistent for *s (glob)
740 754 finalizing requirements file and making repository readable again
741 755 removing temporary repository $TESTTMP/upgradegd/.hg/upgrade.* (glob)
742 756
743 757 Check that the repo still works fine
744 758
745 759 $ hg log -G --stat
746 760 @ changeset: 2:76d4395f5413 (no-py3 !)
747 761 @ changeset: 2:fca376863211 (py3 !)
748 762 | tag: tip
749 763 | parent: 0:ba592bf28da2
750 764 | user: test
751 765 | date: Thu Jan 01 00:00:00 1970 +0000
752 766 | summary: add f2
753 767 |
754 768 | f2 | 100000 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
755 769 | 1 files changed, 100000 insertions(+), 0 deletions(-)
756 770 |
757 771 | o changeset: 1:2029ce2354e2
758 772 |/ user: test
759 773 | date: Thu Jan 01 00:00:00 1970 +0000
760 774 | summary: add f1
761 775 |
762 776 |
763 777 o changeset: 0:ba592bf28da2
764 778 user: test
765 779 date: Thu Jan 01 00:00:00 1970 +0000
766 780 summary: initial
767 781
768 782
769 783
770 784 $ hg verify
771 785 checking changesets
772 786 checking manifests
773 787 crosschecking files in changesets and manifests
774 788 checking files
775 789 checked 3 changesets with 3 changes to 3 files
776 790
777 791 Check we can select negatively
778 792
779 793 $ hg debugupgrade --optimize re-delta-parent --run --no-manifest --no-backup --debug --traceback
780 794 upgrade will perform the following actions:
781 795
782 796 requirements
783 797 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store (no-rust !)
784 798 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, sparserevlog, store (rust !)
785 799
786 800 optimisations: re-delta-parent
787 801
788 802 re-delta-parent
789 803 deltas within internal storage will choose a new base revision if needed
790 804
791 805 processed revlogs:
792 806 - all-filelogs
793 807 - changelog
794 808
795 809 beginning upgrade...
796 810 repository locked and read-only
797 811 creating temporary repository to stage upgraded data: $TESTTMP/upgradegd/.hg/upgrade.* (glob)
798 812 (it is safe to interrupt this process any time before data migration completes)
799 813 migrating 9 total revisions (3 in filelogs, 3 in manifests, 3 in changelog)
800 814 migrating 519 KB in store; 1.05 MB tracked data
801 815 migrating 3 filelogs containing 3 revisions (518 KB in store; 1.05 MB tracked data)
802 816 cloning 1 revisions from data/FooBarDirectory.d/f1.i
803 817 cloning 1 revisions from data/f0.i
804 818 cloning 1 revisions from data/f2.i
805 819 finished migrating 3 filelog revisions across 3 filelogs; change in size: 0 bytes
806 820 migrating 1 manifests containing 3 revisions (367 bytes in store; 238 bytes tracked data)
807 821 blindly copying 00manifest.i containing 3 revisions
808 822 finished migrating 3 manifest revisions across 1 manifests; change in size: 0 bytes
809 823 migrating changelog containing 3 revisions (394 bytes in store; 199 bytes tracked data)
810 824 cloning 3 revisions from 00changelog.i
811 825 finished migrating 3 changelog revisions; change in size: 0 bytes
812 826 finished migrating 9 total revisions; total change in store size: 0 bytes
813 827 copying phaseroots
814 828 data fully upgraded in a temporary repository
815 829 marking source repository as being upgraded; clients will be unable to read from repository
816 830 starting in-place swap of repository data
817 831 replacing store...
818 832 store replacement complete; repository was inconsistent for *s (glob)
819 833 finalizing requirements file and making repository readable again
820 834 removing temporary repository $TESTTMP/upgradegd/.hg/upgrade.* (glob)
821 835 $ hg verify
822 836 checking changesets
823 837 checking manifests
824 838 crosschecking files in changesets and manifests
825 839 checking files
826 840 checked 3 changesets with 3 changes to 3 files
827 841
828 842 Check that we can select changelog only
829 843
830 844 $ hg debugupgrade --optimize re-delta-parent --run --changelog --no-backup --debug --traceback
831 845 upgrade will perform the following actions:
832 846
833 847 requirements
834 848 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store (no-rust !)
835 849 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, sparserevlog, store (rust !)
836 850
837 851 optimisations: re-delta-parent
838 852
839 853 re-delta-parent
840 854 deltas within internal storage will choose a new base revision if needed
841 855
842 856 processed revlogs:
843 857 - changelog
844 858
845 859 beginning upgrade...
846 860 repository locked and read-only
847 861 creating temporary repository to stage upgraded data: $TESTTMP/upgradegd/.hg/upgrade.* (glob)
848 862 (it is safe to interrupt this process any time before data migration completes)
849 863 migrating 9 total revisions (3 in filelogs, 3 in manifests, 3 in changelog)
850 864 migrating 519 KB in store; 1.05 MB tracked data
851 865 migrating 3 filelogs containing 3 revisions (518 KB in store; 1.05 MB tracked data)
852 866 blindly copying data/FooBarDirectory.d/f1.i containing 1 revisions
853 867 blindly copying data/f0.i containing 1 revisions
854 868 blindly copying data/f2.i containing 1 revisions
855 869 finished migrating 3 filelog revisions across 3 filelogs; change in size: 0 bytes
856 870 migrating 1 manifests containing 3 revisions (367 bytes in store; 238 bytes tracked data)
857 871 blindly copying 00manifest.i containing 3 revisions
858 872 finished migrating 3 manifest revisions across 1 manifests; change in size: 0 bytes
859 873 migrating changelog containing 3 revisions (394 bytes in store; 199 bytes tracked data)
860 874 cloning 3 revisions from 00changelog.i
861 875 finished migrating 3 changelog revisions; change in size: 0 bytes
862 876 finished migrating 9 total revisions; total change in store size: 0 bytes
863 877 copying phaseroots
864 878 data fully upgraded in a temporary repository
865 879 marking source repository as being upgraded; clients will be unable to read from repository
866 880 starting in-place swap of repository data
867 881 replacing store...
868 882 store replacement complete; repository was inconsistent for *s (glob)
869 883 finalizing requirements file and making repository readable again
870 884 removing temporary repository $TESTTMP/upgradegd/.hg/upgrade.* (glob)
871 885 $ hg verify
872 886 checking changesets
873 887 checking manifests
874 888 crosschecking files in changesets and manifests
875 889 checking files
876 890 checked 3 changesets with 3 changes to 3 files
877 891
878 892 Check that we can select filelog only
879 893
880 894 $ hg debugupgrade --optimize re-delta-parent --run --no-changelog --no-manifest --no-backup --debug --traceback
881 895 upgrade will perform the following actions:
882 896
883 897 requirements
884 898 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store (no-rust !)
885 899 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, sparserevlog, store (rust !)
886 900
887 901 optimisations: re-delta-parent
888 902
889 903 re-delta-parent
890 904 deltas within internal storage will choose a new base revision if needed
891 905
892 906 processed revlogs:
893 907 - all-filelogs
894 908
895 909 beginning upgrade...
896 910 repository locked and read-only
897 911 creating temporary repository to stage upgraded data: $TESTTMP/upgradegd/.hg/upgrade.* (glob)
898 912 (it is safe to interrupt this process any time before data migration completes)
899 913 migrating 9 total revisions (3 in filelogs, 3 in manifests, 3 in changelog)
900 914 migrating 519 KB in store; 1.05 MB tracked data
901 915 migrating 3 filelogs containing 3 revisions (518 KB in store; 1.05 MB tracked data)
902 916 cloning 1 revisions from data/FooBarDirectory.d/f1.i
903 917 cloning 1 revisions from data/f0.i
904 918 cloning 1 revisions from data/f2.i
905 919 finished migrating 3 filelog revisions across 3 filelogs; change in size: 0 bytes
906 920 migrating 1 manifests containing 3 revisions (367 bytes in store; 238 bytes tracked data)
907 921 blindly copying 00manifest.i containing 3 revisions
908 922 finished migrating 3 manifest revisions across 1 manifests; change in size: 0 bytes
909 923 migrating changelog containing 3 revisions (394 bytes in store; 199 bytes tracked data)
910 924 blindly copying 00changelog.i containing 3 revisions
911 925 finished migrating 3 changelog revisions; change in size: 0 bytes
912 926 finished migrating 9 total revisions; total change in store size: 0 bytes
913 927 copying phaseroots
914 928 data fully upgraded in a temporary repository
915 929 marking source repository as being upgraded; clients will be unable to read from repository
916 930 starting in-place swap of repository data
917 931 replacing store...
918 932 store replacement complete; repository was inconsistent for *s (glob)
919 933 finalizing requirements file and making repository readable again
920 934 removing temporary repository $TESTTMP/upgradegd/.hg/upgrade.* (glob)
921 935 $ hg verify
922 936 checking changesets
923 937 checking manifests
924 938 crosschecking files in changesets and manifests
925 939 checking files
926 940 checked 3 changesets with 3 changes to 3 files
927 941
928 942
929 943 Check you can't skip revlog clone during important format downgrade
930 944
931 945 $ echo "[format]" > .hg/hgrc
932 946 $ echo "sparse-revlog=no" >> .hg/hgrc
933 947 $ hg debugupgrade --optimize re-delta-parent --run --manifest --no-backup --debug --traceback
934 948 ignoring revlogs selection flags, format requirements change: sparserevlog
935 949 upgrade will perform the following actions:
936 950
937 951 requirements
938 952 preserved: dotencode, fncache, generaldelta, revlogv1, store (no-rust !)
939 953 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, store (rust !)
940 954 removed: sparserevlog
941 955
942 956 optimisations: re-delta-parent
943 957
944 958 re-delta-parent
945 959 deltas within internal storage will choose a new base revision if needed
946 960
947 961 processed revlogs:
948 962 - all-filelogs
949 963 - changelog
950 964 - manifest
951 965
952 966 beginning upgrade...
953 967 repository locked and read-only
954 968 creating temporary repository to stage upgraded data: $TESTTMP/upgradegd/.hg/upgrade.* (glob)
955 969 (it is safe to interrupt this process any time before data migration completes)
956 970 migrating 9 total revisions (3 in filelogs, 3 in manifests, 3 in changelog)
957 971 migrating 519 KB in store; 1.05 MB tracked data
958 972 migrating 3 filelogs containing 3 revisions (518 KB in store; 1.05 MB tracked data)
959 973 cloning 1 revisions from data/FooBarDirectory.d/f1.i
960 974 cloning 1 revisions from data/f0.i
961 975 cloning 1 revisions from data/f2.i
962 976 finished migrating 3 filelog revisions across 3 filelogs; change in size: 0 bytes
963 977 migrating 1 manifests containing 3 revisions (367 bytes in store; 238 bytes tracked data)
964 978 cloning 3 revisions from 00manifest.i
965 979 finished migrating 3 manifest revisions across 1 manifests; change in size: 0 bytes
966 980 migrating changelog containing 3 revisions (394 bytes in store; 199 bytes tracked data)
967 981 cloning 3 revisions from 00changelog.i
968 982 finished migrating 3 changelog revisions; change in size: 0 bytes
969 983 finished migrating 9 total revisions; total change in store size: 0 bytes
970 984 copying phaseroots
971 985 data fully upgraded in a temporary repository
972 986 marking source repository as being upgraded; clients will be unable to read from repository
973 987 starting in-place swap of repository data
974 988 replacing store...
975 989 store replacement complete; repository was inconsistent for *s (glob)
976 990 finalizing requirements file and making repository readable again
977 991 removing temporary repository $TESTTMP/upgradegd/.hg/upgrade.* (glob)
978 992 $ hg verify
979 993 checking changesets
980 994 checking manifests
981 995 crosschecking files in changesets and manifests
982 996 checking files
983 997 checked 3 changesets with 3 changes to 3 files
984 998
985 999 Check you can't skip revlog clone during important format upgrade
986 1000
987 1001 $ echo "sparse-revlog=yes" >> .hg/hgrc
988 1002 $ hg debugupgrade --optimize re-delta-parent --run --manifest --no-backup --debug --traceback
989 1003 ignoring revlogs selection flags, format requirements change: sparserevlog
990 1004 upgrade will perform the following actions:
991 1005
992 1006 requirements
993 1007 preserved: dotencode, fncache, generaldelta, revlogv1, store (no-rust !)
994 1008 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, store (rust !)
995 1009 added: sparserevlog
996 1010
997 1011 optimisations: re-delta-parent
998 1012
999 1013 sparserevlog
1000 1014 Revlog supports delta chain with more unused data between payload. These gaps will be skipped at read time. This allows for better delta chains, making a better compression and faster exchange with server.
1001 1015
1002 1016 re-delta-parent
1003 1017 deltas within internal storage will choose a new base revision if needed
1004 1018
1005 1019 processed revlogs:
1006 1020 - all-filelogs
1007 1021 - changelog
1008 1022 - manifest
1009 1023
1010 1024 beginning upgrade...
1011 1025 repository locked and read-only
1012 1026 creating temporary repository to stage upgraded data: $TESTTMP/upgradegd/.hg/upgrade.* (glob)
1013 1027 (it is safe to interrupt this process any time before data migration completes)
1014 1028 migrating 9 total revisions (3 in filelogs, 3 in manifests, 3 in changelog)
1015 1029 migrating 519 KB in store; 1.05 MB tracked data
1016 1030 migrating 3 filelogs containing 3 revisions (518 KB in store; 1.05 MB tracked data)
1017 1031 cloning 1 revisions from data/FooBarDirectory.d/f1.i
1018 1032 cloning 1 revisions from data/f0.i
1019 1033 cloning 1 revisions from data/f2.i
1020 1034 finished migrating 3 filelog revisions across 3 filelogs; change in size: 0 bytes
1021 1035 migrating 1 manifests containing 3 revisions (367 bytes in store; 238 bytes tracked data)
1022 1036 cloning 3 revisions from 00manifest.i
1023 1037 finished migrating 3 manifest revisions across 1 manifests; change in size: 0 bytes
1024 1038 migrating changelog containing 3 revisions (394 bytes in store; 199 bytes tracked data)
1025 1039 cloning 3 revisions from 00changelog.i
1026 1040 finished migrating 3 changelog revisions; change in size: 0 bytes
1027 1041 finished migrating 9 total revisions; total change in store size: 0 bytes
1028 1042 copying phaseroots
1029 1043 data fully upgraded in a temporary repository
1030 1044 marking source repository as being upgraded; clients will be unable to read from repository
1031 1045 starting in-place swap of repository data
1032 1046 replacing store...
1033 1047 store replacement complete; repository was inconsistent for *s (glob)
1034 1048 finalizing requirements file and making repository readable again
1035 1049 removing temporary repository $TESTTMP/upgradegd/.hg/upgrade.* (glob)
1036 1050 $ hg verify
1037 1051 checking changesets
1038 1052 checking manifests
1039 1053 crosschecking files in changesets and manifests
1040 1054 checking files
1041 1055 checked 3 changesets with 3 changes to 3 files
1042 1056
1043 1057 $ cd ..
1044 1058
1045 1059 store files with special filenames aren't encoded during copy
1046 1060
1047 1061 $ hg init store-filenames
1048 1062 $ cd store-filenames
1049 1063 $ touch foo
1050 1064 $ hg -q commit -A -m initial
1051 1065 $ touch .hg/store/.XX_special_filename
1052 1066
1053 1067 $ hg debugupgraderepo --run
1054 1068 nothing to do
1055 1069 $ hg debugupgraderepo --run --optimize 're-delta-fulladd'
1056 1070 upgrade will perform the following actions:
1057 1071
1058 1072 requirements
1059 1073 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store (no-rust !)
1060 1074 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, sparserevlog, store (rust !)
1061 1075
1062 1076 optimisations: re-delta-fulladd
1063 1077
1064 1078 re-delta-fulladd
1065 1079 each revision will be added as new content to the internal storage; this will likely drastically slow down execution time, but some extensions might need it
1066 1080
1067 1081 processed revlogs:
1068 1082 - all-filelogs
1069 1083 - changelog
1070 1084 - manifest
1071 1085
1072 1086 beginning upgrade...
1073 1087 repository locked and read-only
1074 1088 creating temporary repository to stage upgraded data: $TESTTMP/store-filenames/.hg/upgrade.* (glob)
1075 1089 (it is safe to interrupt this process any time before data migration completes)
1076 1090 migrating 3 total revisions (1 in filelogs, 1 in manifests, 1 in changelog)
1077 1091 migrating 301 bytes in store; 107 bytes tracked data
1078 1092 migrating 1 filelogs containing 1 revisions (64 bytes in store; 0 bytes tracked data)
1079 1093 finished migrating 1 filelog revisions across 1 filelogs; change in size: 0 bytes
1080 1094 migrating 1 manifests containing 1 revisions (110 bytes in store; 45 bytes tracked data)
1081 1095 finished migrating 1 manifest revisions across 1 manifests; change in size: 0 bytes
1082 1096 migrating changelog containing 1 revisions (127 bytes in store; 62 bytes tracked data)
1083 1097 finished migrating 1 changelog revisions; change in size: 0 bytes
1084 1098 finished migrating 3 total revisions; total change in store size: 0 bytes
1085 1099 copying .XX_special_filename
1086 1100 copying phaseroots
1087 1101 data fully upgraded in a temporary repository
1088 1102 marking source repository as being upgraded; clients will be unable to read from repository
1089 1103 starting in-place swap of repository data
1090 1104 replaced files will be backed up at $TESTTMP/store-filenames/.hg/upgradebackup.* (glob)
1091 1105 replacing store...
1092 1106 store replacement complete; repository was inconsistent for *s (glob)
1093 1107 finalizing requirements file and making repository readable again
1094 1108 removing temporary repository $TESTTMP/store-filenames/.hg/upgrade.* (glob)
1095 1109 copy of old repository backed up at $TESTTMP/store-filenames/.hg/upgradebackup.* (glob)
1096 1110 the old repository will not be deleted; remove it to free up disk space once the upgraded repository is verified
1097 1111
1098 1112 fncache is valid after upgrade
1099 1113
1100 1114 $ hg debugrebuildfncache
1101 1115 fncache already up to date
1102 1116
1103 1117 $ cd ..
1104 1118
1105 1119 Check upgrading a large file repository
1106 1120 ---------------------------------------
1107 1121
1108 1122 $ hg init largefilesrepo
1109 1123 $ cat << EOF >> largefilesrepo/.hg/hgrc
1110 1124 > [extensions]
1111 1125 > largefiles =
1112 1126 > EOF
1113 1127
1114 1128 $ cd largefilesrepo
1115 1129 $ touch foo
1116 1130 $ hg add --large foo
1117 1131 $ hg -q commit -m initial
1118 1132 $ cat .hg/requires
1119 1133 dotencode
1120 1134 fncache
1121 1135 generaldelta
1122 1136 largefiles
1123 1137 persistent-nodemap (rust !)
1124 1138 revlogv1
1125 1139 sparserevlog
1126 1140 store
1127 1141
1128 1142 $ hg debugupgraderepo --run
1129 1143 nothing to do
1130 1144 $ cat .hg/requires
1131 1145 dotencode
1132 1146 fncache
1133 1147 generaldelta
1134 1148 largefiles
1135 1149 persistent-nodemap (rust !)
1136 1150 revlogv1
1137 1151 sparserevlog
1138 1152 store
1139 1153
1140 1154 $ cat << EOF >> .hg/hgrc
1141 1155 > [extensions]
1142 1156 > lfs =
1143 1157 > [lfs]
1144 1158 > threshold = 10
1145 1159 > EOF
1146 1160 $ echo '123456789012345' > lfs.bin
1147 1161 $ hg ci -Am 'lfs.bin'
1148 1162 adding lfs.bin
1149 1163 $ grep lfs .hg/requires
1150 1164 lfs
1151 1165 $ find .hg/store/lfs -type f
1152 1166 .hg/store/lfs/objects/d0/beab232adff5ba365880366ad30b1edb85c4c5372442b5d2fe27adc96d653f
1153 1167
1154 1168 $ hg debugupgraderepo --run
1155 1169 nothing to do
1156 1170
1157 1171 $ grep lfs .hg/requires
1158 1172 lfs
1159 1173 $ find .hg/store/lfs -type f
1160 1174 .hg/store/lfs/objects/d0/beab232adff5ba365880366ad30b1edb85c4c5372442b5d2fe27adc96d653f
1161 1175 $ hg verify
1162 1176 checking changesets
1163 1177 checking manifests
1164 1178 crosschecking files in changesets and manifests
1165 1179 checking files
1166 1180 checked 2 changesets with 2 changes to 2 files
1167 1181 $ hg debugdata lfs.bin 0
1168 1182 version https://git-lfs.github.com/spec/v1
1169 1183 oid sha256:d0beab232adff5ba365880366ad30b1edb85c4c5372442b5d2fe27adc96d653f
1170 1184 size 16
1171 1185 x-is-binary 0
1172 1186
1173 1187 $ cd ..
1174 1188
1175 1189 repository config is taken in account
1176 1190 -------------------------------------
1177 1191
1178 1192 $ cat << EOF >> $HGRCPATH
1179 1193 > [format]
1180 1194 > maxchainlen = 1
1181 1195 > EOF
1182 1196
1183 1197 $ hg init localconfig
1184 1198 $ cd localconfig
1185 1199 $ cat << EOF > file
1186 1200 > some content
1187 1201 > with some length
1188 1202 > to make sure we get a delta
1189 1203 > after changes
1190 1204 > very long
1191 1205 > very long
1192 1206 > very long
1193 1207 > very long
1194 1208 > very long
1195 1209 > very long
1196 1210 > very long
1197 1211 > very long
1198 1212 > very long
1199 1213 > very long
1200 1214 > very long
1201 1215 > EOF
1202 1216 $ hg -q commit -A -m A
1203 1217 $ echo "new line" >> file
1204 1218 $ hg -q commit -m B
1205 1219 $ echo "new line" >> file
1206 1220 $ hg -q commit -m C
1207 1221
1208 1222 $ cat << EOF >> .hg/hgrc
1209 1223 > [format]
1210 1224 > maxchainlen = 9001
1211 1225 > EOF
1212 1226 $ hg config format
1213 1227 format.revlog-compression=$BUNDLE2_COMPRESSIONS$
1214 1228 format.maxchainlen=9001
1215 1229 $ hg debugdeltachain file
1216 1230 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio readsize largestblk rddensity srchunks
1217 1231 0 1 1 -1 base 77 182 77 0.42308 77 0 0.00000 77 77 1.00000 1
1218 1232 1 1 2 0 p1 21 191 98 0.51309 98 0 0.00000 98 98 1.00000 1
1219 1233 2 1 2 0 other 30 200 107 0.53500 128 21 0.19626 128 128 0.83594 1
1220 1234
1221 1235 $ hg debugupgraderepo --run --optimize 're-delta-all'
1222 1236 upgrade will perform the following actions:
1223 1237
1224 1238 requirements
1225 1239 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store (no-rust !)
1226 1240 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, sparserevlog, store (rust !)
1227 1241
1228 1242 optimisations: re-delta-all
1229 1243
1230 1244 re-delta-all
1231 1245 deltas within internal storage will be fully recomputed; this will likely drastically slow down execution time
1232 1246
1233 1247 processed revlogs:
1234 1248 - all-filelogs
1235 1249 - changelog
1236 1250 - manifest
1237 1251
1238 1252 beginning upgrade...
1239 1253 repository locked and read-only
1240 1254 creating temporary repository to stage upgraded data: $TESTTMP/localconfig/.hg/upgrade.* (glob)
1241 1255 (it is safe to interrupt this process any time before data migration completes)
1242 1256 migrating 9 total revisions (3 in filelogs, 3 in manifests, 3 in changelog)
1243 1257 migrating 1019 bytes in store; 882 bytes tracked data
1244 1258 migrating 1 filelogs containing 3 revisions (320 bytes in store; 573 bytes tracked data)
1245 1259 finished migrating 3 filelog revisions across 1 filelogs; change in size: -9 bytes
1246 1260 migrating 1 manifests containing 3 revisions (333 bytes in store; 138 bytes tracked data)
1247 1261 finished migrating 3 manifest revisions across 1 manifests; change in size: 0 bytes
1248 1262 migrating changelog containing 3 revisions (366 bytes in store; 171 bytes tracked data)
1249 1263 finished migrating 3 changelog revisions; change in size: 0 bytes
1250 1264 finished migrating 9 total revisions; total change in store size: -9 bytes
1251 1265 copying phaseroots
1252 1266 data fully upgraded in a temporary repository
1253 1267 marking source repository as being upgraded; clients will be unable to read from repository
1254 1268 starting in-place swap of repository data
1255 1269 replaced files will be backed up at $TESTTMP/localconfig/.hg/upgradebackup.* (glob)
1256 1270 replacing store...
1257 1271 store replacement complete; repository was inconsistent for *s (glob)
1258 1272 finalizing requirements file and making repository readable again
1259 1273 removing temporary repository $TESTTMP/localconfig/.hg/upgrade.* (glob)
1260 1274 copy of old repository backed up at $TESTTMP/localconfig/.hg/upgradebackup.* (glob)
1261 1275 the old repository will not be deleted; remove it to free up disk space once the upgraded repository is verified
1262 1276 $ hg debugdeltachain file
1263 1277 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio readsize largestblk rddensity srchunks
1264 1278 0 1 1 -1 base 77 182 77 0.42308 77 0 0.00000 77 77 1.00000 1
1265 1279 1 1 2 0 p1 21 191 98 0.51309 98 0 0.00000 98 98 1.00000 1
1266 1280 2 1 3 1 p1 21 200 119 0.59500 119 0 0.00000 119 119 1.00000 1
1267 1281 $ cd ..
1268 1282
1269 1283 $ cat << EOF >> $HGRCPATH
1270 1284 > [format]
1271 1285 > maxchainlen = 9001
1272 1286 > EOF
1273 1287
1274 1288 Check upgrading a sparse-revlog repository
1275 1289 ---------------------------------------
1276 1290
1277 1291 $ hg init sparserevlogrepo --config format.sparse-revlog=no
1278 1292 $ cd sparserevlogrepo
1279 1293 $ touch foo
1280 1294 $ hg add foo
1281 1295 $ hg -q commit -m "foo"
1282 1296 $ cat .hg/requires
1283 1297 dotencode
1284 1298 fncache
1285 1299 generaldelta
1286 1300 persistent-nodemap (rust !)
1287 1301 revlogv1
1288 1302 store
1289 1303
1290 1304 Check that we can add the sparse-revlog format requirement
1291 1305 $ hg --config format.sparse-revlog=yes debugupgraderepo --run --quiet
1292 1306 upgrade will perform the following actions:
1293 1307
1294 1308 requirements
1295 1309 preserved: dotencode, fncache, generaldelta, revlogv1, store (no-rust !)
1296 1310 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, store (rust !)
1297 1311 added: sparserevlog
1298 1312
1299 1313 processed revlogs:
1300 1314 - all-filelogs
1301 1315 - changelog
1302 1316 - manifest
1303 1317
1304 1318 $ cat .hg/requires
1305 1319 dotencode
1306 1320 fncache
1307 1321 generaldelta
1308 1322 persistent-nodemap (rust !)
1309 1323 revlogv1
1310 1324 sparserevlog
1311 1325 store
1312 1326
1313 1327 Check that we can remove the sparse-revlog format requirement
1314 1328 $ hg --config format.sparse-revlog=no debugupgraderepo --run --quiet
1315 1329 upgrade will perform the following actions:
1316 1330
1317 1331 requirements
1318 1332 preserved: dotencode, fncache, generaldelta, revlogv1, store (no-rust !)
1319 1333 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, store (rust !)
1320 1334 removed: sparserevlog
1321 1335
1322 1336 processed revlogs:
1323 1337 - all-filelogs
1324 1338 - changelog
1325 1339 - manifest
1326 1340
1327 1341 $ cat .hg/requires
1328 1342 dotencode
1329 1343 fncache
1330 1344 generaldelta
1331 1345 persistent-nodemap (rust !)
1332 1346 revlogv1
1333 1347 store
1334 1348
1335 1349 #if zstd
1336 1350
1337 1351 Check upgrading to a zstd revlog
1338 1352 --------------------------------
1339 1353
1340 1354 upgrade
1341 1355
1342 1356 $ hg --config format.revlog-compression=zstd debugupgraderepo --run --no-backup --quiet
1343 1357 upgrade will perform the following actions:
1344 1358
1345 1359 requirements
1346 1360 preserved: dotencode, fncache, generaldelta, revlogv1, store (no-rust !)
1347 1361 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, store (rust !)
1348 1362 added: revlog-compression-zstd, sparserevlog
1349 1363
1350 1364 processed revlogs:
1351 1365 - all-filelogs
1352 1366 - changelog
1353 1367 - manifest
1354 1368
1355 1369 $ hg debugformat -v
1356 1370 format-variant repo config default
1357 1371 fncache: yes yes yes
1372 dirstate-v2: no no no
1358 1373 dotencode: yes yes yes
1359 1374 generaldelta: yes yes yes
1360 1375 share-safe: no no no
1361 1376 sparserevlog: yes yes yes
1362 1377 persistent-nodemap: no no no (no-rust !)
1363 1378 persistent-nodemap: yes yes no (rust !)
1364 1379 copies-sdc: no no no
1365 1380 revlog-v2: no no no
1366 1381 changelog-v2: no no no
1367 1382 plain-cl-delta: yes yes yes
1368 1383 compression: zlib zlib zlib (no-zstd !)
1369 1384 compression: zstd zlib zstd (zstd !)
1370 1385 compression-level: default default default
1371 1386 $ cat .hg/requires
1372 1387 dotencode
1373 1388 fncache
1374 1389 generaldelta
1375 1390 persistent-nodemap (rust !)
1376 1391 revlog-compression-zstd
1377 1392 revlogv1
1378 1393 sparserevlog
1379 1394 store
1380 1395
1381 1396 downgrade
1382 1397
1383 1398 $ hg debugupgraderepo --run --no-backup --quiet
1384 1399 upgrade will perform the following actions:
1385 1400
1386 1401 requirements
1387 1402 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store (no-rust !)
1388 1403 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, sparserevlog, store (rust !)
1389 1404 removed: revlog-compression-zstd
1390 1405
1391 1406 processed revlogs:
1392 1407 - all-filelogs
1393 1408 - changelog
1394 1409 - manifest
1395 1410
1396 1411 $ hg debugformat -v
1397 1412 format-variant repo config default
1398 1413 fncache: yes yes yes
1414 dirstate-v2: no no no
1399 1415 dotencode: yes yes yes
1400 1416 generaldelta: yes yes yes
1401 1417 share-safe: no no no
1402 1418 sparserevlog: yes yes yes
1403 1419 persistent-nodemap: no no no (no-rust !)
1404 1420 persistent-nodemap: yes yes no (rust !)
1405 1421 copies-sdc: no no no
1406 1422 revlog-v2: no no no
1407 1423 changelog-v2: no no no
1408 1424 plain-cl-delta: yes yes yes
1409 1425 compression: zlib zlib zlib (no-zstd !)
1410 1426 compression: zlib zlib zstd (zstd !)
1411 1427 compression-level: default default default
1412 1428 $ cat .hg/requires
1413 1429 dotencode
1414 1430 fncache
1415 1431 generaldelta
1416 1432 persistent-nodemap (rust !)
1417 1433 revlogv1
1418 1434 sparserevlog
1419 1435 store
1420 1436
1421 1437 upgrade from hgrc
1422 1438
1423 1439 $ cat >> .hg/hgrc << EOF
1424 1440 > [format]
1425 1441 > revlog-compression=zstd
1426 1442 > EOF
1427 1443 $ hg debugupgraderepo --run --no-backup --quiet
1428 1444 upgrade will perform the following actions:
1429 1445
1430 1446 requirements
1431 1447 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store (no-rust !)
1432 1448 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, sparserevlog, store (rust !)
1433 1449 added: revlog-compression-zstd
1434 1450
1435 1451 processed revlogs:
1436 1452 - all-filelogs
1437 1453 - changelog
1438 1454 - manifest
1439 1455
1440 1456 $ hg debugformat -v
1441 1457 format-variant repo config default
1442 1458 fncache: yes yes yes
1459 dirstate-v2: no no no
1443 1460 dotencode: yes yes yes
1444 1461 generaldelta: yes yes yes
1445 1462 share-safe: no no no
1446 1463 sparserevlog: yes yes yes
1447 1464 persistent-nodemap: no no no (no-rust !)
1448 1465 persistent-nodemap: yes yes no (rust !)
1449 1466 copies-sdc: no no no
1450 1467 revlog-v2: no no no
1451 1468 changelog-v2: no no no
1452 1469 plain-cl-delta: yes yes yes
1453 1470 compression: zlib zlib zlib (no-zstd !)
1454 1471 compression: zstd zstd zstd (zstd !)
1455 1472 compression-level: default default default
1456 1473 $ cat .hg/requires
1457 1474 dotencode
1458 1475 fncache
1459 1476 generaldelta
1460 1477 persistent-nodemap (rust !)
1461 1478 revlog-compression-zstd
1462 1479 revlogv1
1463 1480 sparserevlog
1464 1481 store
1465 1482
1466 1483 #endif
1467 1484
1468 1485 Check upgrading to a revlog format supporting sidedata
1469 1486 ------------------------------------------------------
1470 1487
1471 1488 upgrade
1472 1489
1473 1490 $ hg debugsidedata -c 0
1474 1491 $ hg --config experimental.revlogv2=enable-unstable-format-and-corrupt-my-data debugupgraderepo --run --no-backup --config "extensions.sidedata=$TESTDIR/testlib/ext-sidedata.py" --quiet
1475 1492 upgrade will perform the following actions:
1476 1493
1477 1494 requirements
1478 1495 preserved: dotencode, fncache, generaldelta, store (no-zstd !)
1479 1496 preserved: dotencode, fncache, generaldelta, revlog-compression-zstd, sparserevlog, store (zstd no-rust !)
1480 1497 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlog-compression-zstd, sparserevlog, store (rust !)
1481 1498 removed: revlogv1
1482 1499 added: exp-revlogv2.2 (zstd !)
1483 1500 added: exp-revlogv2.2, sparserevlog (no-zstd !)
1484 1501
1485 1502 processed revlogs:
1486 1503 - all-filelogs
1487 1504 - changelog
1488 1505 - manifest
1489 1506
1490 1507 $ hg debugformat -v
1491 1508 format-variant repo config default
1492 1509 fncache: yes yes yes
1510 dirstate-v2: no no no
1493 1511 dotencode: yes yes yes
1494 1512 generaldelta: yes yes yes
1495 1513 share-safe: no no no
1496 1514 sparserevlog: yes yes yes
1497 1515 persistent-nodemap: no no no (no-rust !)
1498 1516 persistent-nodemap: yes yes no (rust !)
1499 1517 copies-sdc: no no no
1500 1518 revlog-v2: yes no no
1501 1519 changelog-v2: no no no
1502 1520 plain-cl-delta: yes yes yes
1503 1521 compression: zlib zlib zlib (no-zstd !)
1504 1522 compression: zstd zstd zstd (zstd !)
1505 1523 compression-level: default default default
1506 1524 $ cat .hg/requires
1507 1525 dotencode
1508 1526 exp-revlogv2.2
1509 1527 fncache
1510 1528 generaldelta
1511 1529 persistent-nodemap (rust !)
1512 1530 revlog-compression-zstd (zstd !)
1513 1531 sparserevlog
1514 1532 store
1515 1533 $ hg debugsidedata -c 0
1516 1534 2 sidedata entries
1517 1535 entry-0001 size 4
1518 1536 entry-0002 size 32
1519 1537
1520 1538 downgrade
1521 1539
1522 1540 $ hg debugupgraderepo --config experimental.revlogv2=no --run --no-backup --quiet
1523 1541 upgrade will perform the following actions:
1524 1542
1525 1543 requirements
1526 1544 preserved: dotencode, fncache, generaldelta, sparserevlog, store (no-zstd !)
1527 1545 preserved: dotencode, fncache, generaldelta, revlog-compression-zstd, sparserevlog, store (zstd no-rust !)
1528 1546 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlog-compression-zstd, sparserevlog, store (rust !)
1529 1547 removed: exp-revlogv2.2
1530 1548 added: revlogv1
1531 1549
1532 1550 processed revlogs:
1533 1551 - all-filelogs
1534 1552 - changelog
1535 1553 - manifest
1536 1554
1537 1555 $ hg debugformat -v
1538 1556 format-variant repo config default
1539 1557 fncache: yes yes yes
1558 dirstate-v2: no no no
1540 1559 dotencode: yes yes yes
1541 1560 generaldelta: yes yes yes
1542 1561 share-safe: no no no
1543 1562 sparserevlog: yes yes yes
1544 1563 persistent-nodemap: no no no (no-rust !)
1545 1564 persistent-nodemap: yes yes no (rust !)
1546 1565 copies-sdc: no no no
1547 1566 revlog-v2: no no no
1548 1567 changelog-v2: no no no
1549 1568 plain-cl-delta: yes yes yes
1550 1569 compression: zlib zlib zlib (no-zstd !)
1551 1570 compression: zstd zstd zstd (zstd !)
1552 1571 compression-level: default default default
1553 1572 $ cat .hg/requires
1554 1573 dotencode
1555 1574 fncache
1556 1575 generaldelta
1557 1576 persistent-nodemap (rust !)
1558 1577 revlog-compression-zstd (zstd !)
1559 1578 revlogv1
1560 1579 sparserevlog
1561 1580 store
1562 1581 $ hg debugsidedata -c 0
1563 1582
1564 1583 upgrade from hgrc
1565 1584
1566 1585 $ cat >> .hg/hgrc << EOF
1567 1586 > [experimental]
1568 1587 > revlogv2=enable-unstable-format-and-corrupt-my-data
1569 1588 > EOF
1570 1589 $ hg debugupgraderepo --run --no-backup --quiet
1571 1590 upgrade will perform the following actions:
1572 1591
1573 1592 requirements
1574 1593 preserved: dotencode, fncache, generaldelta, sparserevlog, store (no-zstd !)
1575 1594 preserved: dotencode, fncache, generaldelta, revlog-compression-zstd, sparserevlog, store (zstd no-rust !)
1576 1595 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlog-compression-zstd, sparserevlog, store (rust !)
1577 1596 removed: revlogv1
1578 1597 added: exp-revlogv2.2
1579 1598
1580 1599 processed revlogs:
1581 1600 - all-filelogs
1582 1601 - changelog
1583 1602 - manifest
1584 1603
1585 1604 $ hg debugformat -v
1586 1605 format-variant repo config default
1587 1606 fncache: yes yes yes
1607 dirstate-v2: no no no
1588 1608 dotencode: yes yes yes
1589 1609 generaldelta: yes yes yes
1590 1610 share-safe: no no no
1591 1611 sparserevlog: yes yes yes
1592 1612 persistent-nodemap: no no no (no-rust !)
1593 1613 persistent-nodemap: yes yes no (rust !)
1594 1614 copies-sdc: no no no
1595 1615 revlog-v2: yes yes no
1596 1616 changelog-v2: no no no
1597 1617 plain-cl-delta: yes yes yes
1598 1618 compression: zlib zlib zlib (no-zstd !)
1599 1619 compression: zstd zstd zstd (zstd !)
1600 1620 compression-level: default default default
1601 1621 $ cat .hg/requires
1602 1622 dotencode
1603 1623 exp-revlogv2.2
1604 1624 fncache
1605 1625 generaldelta
1606 1626 persistent-nodemap (rust !)
1607 1627 revlog-compression-zstd (zstd !)
1608 1628 sparserevlog
1609 1629 store
1610 1630 $ hg debugsidedata -c 0
1611 1631
1612 1632 Demonstrate that nothing to perform upgrade will still run all the way through
1613 1633
1614 1634 $ hg debugupgraderepo --run
1615 1635 nothing to do
1636
1637 #if rust
1638
1639 Upgrade to dirstate-v2
1640
1641 $ hg debugformat -v --config format.exp-dirstate-v2=1
1642 format-variant repo config default
1643 fncache: yes yes yes
1644 dirstate-v2: no yes no
1645 dotencode: yes yes yes
1646 generaldelta: yes yes yes
1647 share-safe: no no no
1648 sparserevlog: yes yes yes
1649 persistent-nodemap: yes yes no
1650 copies-sdc: no no no
1651 revlog-v2: yes yes no
1652 changelog-v2: no no no
1653 plain-cl-delta: yes yes yes
1654 compression: zstd zstd zstd
1655 compression-level: default default default
1656 $ hg debugupgraderepo --config format.exp-dirstate-v2=1 --run
1657 upgrade will perform the following actions:
1658
1659 requirements
1660 preserved: dotencode, exp-revlogv2.2, fncache, generaldelta, persistent-nodemap, revlog-compression-zstd, sparserevlog, store
1661 added: exp-dirstate-v2
1662
1663 dirstate-v2
1664 "hg status" will be faster
1665
1666 processed revlogs:
1667 - all-filelogs
1668 - changelog
1669 - manifest
1670
1671 beginning upgrade...
1672 repository locked and read-only
1673 creating temporary repository to stage upgraded data: $TESTTMP/sparserevlogrepo/.hg/upgrade.* (glob)
1674 (it is safe to interrupt this process any time before data migration completes)
1675 upgrading to dirstate-v2 from v1
1676 replaced files will be backed up at $TESTTMP/sparserevlogrepo/.hg/upgradebackup.* (glob)
1677 removing temporary repository $TESTTMP/sparserevlogrepo/.hg/upgrade.* (glob)
1678 $ ls .hg/upgradebackup.*/dirstate
1679 .hg/upgradebackup.*/dirstate (glob)
1680 $ hg debugformat -v
1681 format-variant repo config default
1682 fncache: yes yes yes
1683 dirstate-v2: yes no no
1684 dotencode: yes yes yes
1685 generaldelta: yes yes yes
1686 share-safe: no no no
1687 sparserevlog: yes yes yes
1688 persistent-nodemap: yes yes no
1689 copies-sdc: no no no
1690 revlog-v2: yes yes no
1691 changelog-v2: no no no
1692 plain-cl-delta: yes yes yes
1693 compression: zstd zstd zstd
1694 compression-level: default default default
1695 $ hg status
1696 $ dd status=none bs=12 count=1 if=.hg/dirstate
1697 dirstate-v2
1698
1699 Downgrade from dirstate-v2
1700
1701 $ hg debugupgraderepo --run
1702 upgrade will perform the following actions:
1703
1704 requirements
1705 preserved: dotencode, exp-revlogv2.2, fncache, generaldelta, persistent-nodemap, revlog-compression-zstd, sparserevlog, store
1706 removed: exp-dirstate-v2
1707
1708 processed revlogs:
1709 - all-filelogs
1710 - changelog
1711 - manifest
1712
1713 beginning upgrade...
1714 repository locked and read-only
1715 creating temporary repository to stage upgraded data: $TESTTMP/sparserevlogrepo/.hg/upgrade.* (glob)
1716 (it is safe to interrupt this process any time before data migration completes)
1717 downgrading from dirstate-v2 to v1
1718 replaced files will be backed up at $TESTTMP/sparserevlogrepo/.hg/upgradebackup.* (glob)
1719 removing temporary repository $TESTTMP/sparserevlogrepo/.hg/upgrade.* (glob)
1720 $ hg debugformat -v
1721 format-variant repo config default
1722 fncache: yes yes yes
1723 dirstate-v2: no no no
1724 dotencode: yes yes yes
1725 generaldelta: yes yes yes
1726 share-safe: no no no
1727 sparserevlog: yes yes yes
1728 persistent-nodemap: yes yes no
1729 copies-sdc: no no no
1730 revlog-v2: yes yes no
1731 changelog-v2: no no no
1732 plain-cl-delta: yes yes yes
1733 compression: zstd zstd zstd
1734 compression-level: default default default
1735 $ hg status
1736
1737 #endif
General Comments 0
You need to be logged in to leave comments. Login now