##// END OF EJS Templates
strip: fix bug with treemanifests and unordered linkrevs...
Martin von Zweigbergk -
r43183:a8b249b2 default
parent child Browse files
Show More
@@ -1,480 +1,478 b''
1 1 # repair.py - functions for repository repair for mercurial
2 2 #
3 3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 4 # Copyright 2007 Matt Mackall
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 from __future__ import absolute_import
10 10
11 11 import errno
12 12 import hashlib
13 13
14 14 from .i18n import _
15 15 from .node import (
16 16 hex,
17 17 short,
18 18 )
19 19 from . import (
20 20 bundle2,
21 21 changegroup,
22 22 discovery,
23 23 error,
24 24 exchange,
25 25 obsolete,
26 26 obsutil,
27 27 phases,
28 28 pycompat,
29 29 util,
30 30 )
31 31 from .utils import (
32 32 stringutil,
33 33 )
34 34
35 35 def backupbundle(repo, bases, heads, node, suffix, compress=True,
36 36 obsolescence=True):
37 37 """create a bundle with the specified revisions as a backup"""
38 38
39 39 backupdir = "strip-backup"
40 40 vfs = repo.vfs
41 41 if not vfs.isdir(backupdir):
42 42 vfs.mkdir(backupdir)
43 43
44 44 # Include a hash of all the nodes in the filename for uniqueness
45 45 allcommits = repo.set('%ln::%ln', bases, heads)
46 46 allhashes = sorted(c.hex() for c in allcommits)
47 47 totalhash = hashlib.sha1(''.join(allhashes)).digest()
48 48 name = "%s/%s-%s-%s.hg" % (backupdir, short(node),
49 49 hex(totalhash[:4]), suffix)
50 50
51 51 cgversion = changegroup.localversion(repo)
52 52 comp = None
53 53 if cgversion != '01':
54 54 bundletype = "HG20"
55 55 if compress:
56 56 comp = 'BZ'
57 57 elif compress:
58 58 bundletype = "HG10BZ"
59 59 else:
60 60 bundletype = "HG10UN"
61 61
62 62 outgoing = discovery.outgoing(repo, missingroots=bases, missingheads=heads)
63 63 contentopts = {
64 64 'cg.version': cgversion,
65 65 'obsolescence': obsolescence,
66 66 'phases': True,
67 67 }
68 68 return bundle2.writenewbundle(repo.ui, repo, 'strip', name, bundletype,
69 69 outgoing, contentopts, vfs, compression=comp)
70 70
71 71 def _collectfiles(repo, striprev):
72 72 """find out the filelogs affected by the strip"""
73 73 files = set()
74 74
75 75 for x in pycompat.xrange(striprev, len(repo)):
76 76 files.update(repo[x].files())
77 77
78 78 return sorted(files)
79 79
80 80 def _collectrevlog(revlog, striprev):
81 81 _, brokenset = revlog.getstrippoint(striprev)
82 82 return [revlog.linkrev(r) for r in brokenset]
83 83
84 def _collectmanifest(repo, striprev):
85 return _collectrevlog(repo.manifestlog.getstorage(b''), striprev)
86
87 84 def _collectbrokencsets(repo, files, striprev):
88 85 """return the changesets which will be broken by the truncation"""
89 86 s = set()
90 87
91 s.update(_collectmanifest(repo, striprev))
88 for revlog in manifestrevlogs(repo):
89 s.update(_collectrevlog(revlog, striprev))
92 90 for fname in files:
93 91 s.update(_collectrevlog(repo.file(fname), striprev))
94 92
95 93 return s
96 94
97 95 def strip(ui, repo, nodelist, backup=True, topic='backup'):
98 96 # This function requires the caller to lock the repo, but it operates
99 97 # within a transaction of its own, and thus requires there to be no current
100 98 # transaction when it is called.
101 99 if repo.currenttransaction() is not None:
102 100 raise error.ProgrammingError('cannot strip from inside a transaction')
103 101
104 102 # Simple way to maintain backwards compatibility for this
105 103 # argument.
106 104 if backup in ['none', 'strip']:
107 105 backup = False
108 106
109 107 repo = repo.unfiltered()
110 108 repo.destroying()
111 109 vfs = repo.vfs
112 110 # load bookmark before changelog to avoid side effect from outdated
113 111 # changelog (see repo._refreshchangelog)
114 112 repo._bookmarks
115 113 cl = repo.changelog
116 114
117 115 # TODO handle undo of merge sets
118 116 if isinstance(nodelist, str):
119 117 nodelist = [nodelist]
120 118 striplist = [cl.rev(node) for node in nodelist]
121 119 striprev = min(striplist)
122 120
123 121 files = _collectfiles(repo, striprev)
124 122 saverevs = _collectbrokencsets(repo, files, striprev)
125 123
126 124 # Some revisions with rev > striprev may not be descendants of striprev.
127 125 # We have to find these revisions and put them in a bundle, so that
128 126 # we can restore them after the truncations.
129 127 # To create the bundle we use repo.changegroupsubset which requires
130 128 # the list of heads and bases of the set of interesting revisions.
131 129 # (head = revision in the set that has no descendant in the set;
132 130 # base = revision in the set that has no ancestor in the set)
133 131 tostrip = set(striplist)
134 132 saveheads = set(saverevs)
135 133 for r in cl.revs(start=striprev + 1):
136 134 if any(p in tostrip for p in cl.parentrevs(r)):
137 135 tostrip.add(r)
138 136
139 137 if r not in tostrip:
140 138 saverevs.add(r)
141 139 saveheads.difference_update(cl.parentrevs(r))
142 140 saveheads.add(r)
143 141 saveheads = [cl.node(r) for r in saveheads]
144 142
145 143 # compute base nodes
146 144 if saverevs:
147 145 descendants = set(cl.descendants(saverevs))
148 146 saverevs.difference_update(descendants)
149 147 savebases = [cl.node(r) for r in saverevs]
150 148 stripbases = [cl.node(r) for r in tostrip]
151 149
152 150 stripobsidx = obsmarkers = ()
153 151 if repo.ui.configbool('devel', 'strip-obsmarkers'):
154 152 obsmarkers = obsutil.exclusivemarkers(repo, stripbases)
155 153 if obsmarkers:
156 154 stripobsidx = [i for i, m in enumerate(repo.obsstore)
157 155 if m in obsmarkers]
158 156
159 157 newbmtarget, updatebm = _bookmarkmovements(repo, tostrip)
160 158
161 159 backupfile = None
162 160 node = nodelist[-1]
163 161 if backup:
164 162 backupfile = _createstripbackup(repo, stripbases, node, topic)
165 163 # create a changegroup for all the branches we need to keep
166 164 tmpbundlefile = None
167 165 if saveheads:
168 166 # do not compress temporary bundle if we remove it from disk later
169 167 #
170 168 # We do not include obsolescence, it might re-introduce prune markers
171 169 # we are trying to strip. This is harmless since the stripped markers
172 170 # are already backed up and we did not touched the markers for the
173 171 # saved changesets.
174 172 tmpbundlefile = backupbundle(repo, savebases, saveheads, node, 'temp',
175 173 compress=False, obsolescence=False)
176 174
177 175 with ui.uninterruptible():
178 176 try:
179 177 with repo.transaction("strip") as tr:
180 178 # TODO this code violates the interface abstraction of the
181 179 # transaction and makes assumptions that file storage is
182 180 # using append-only files. We'll need some kind of storage
183 181 # API to handle stripping for us.
184 182 offset = len(tr._entries)
185 183
186 184 tr.startgroup()
187 185 cl.strip(striprev, tr)
188 186 stripmanifest(repo, striprev, tr, files)
189 187
190 188 for fn in files:
191 189 repo.file(fn).strip(striprev, tr)
192 190 tr.endgroup()
193 191
194 192 for i in pycompat.xrange(offset, len(tr._entries)):
195 193 file, troffset, ignore = tr._entries[i]
196 194 with repo.svfs(file, 'a', checkambig=True) as fp:
197 195 fp.truncate(troffset)
198 196 if troffset == 0:
199 197 repo.store.markremoved(file)
200 198
201 199 deleteobsmarkers(repo.obsstore, stripobsidx)
202 200 del repo.obsstore
203 201 repo.invalidatevolatilesets()
204 202 repo._phasecache.filterunknown(repo)
205 203
206 204 if tmpbundlefile:
207 205 ui.note(_("adding branch\n"))
208 206 f = vfs.open(tmpbundlefile, "rb")
209 207 gen = exchange.readbundle(ui, f, tmpbundlefile, vfs)
210 208 if not repo.ui.verbose:
211 209 # silence internal shuffling chatter
212 210 repo.ui.pushbuffer()
213 211 tmpbundleurl = 'bundle:' + vfs.join(tmpbundlefile)
214 212 txnname = 'strip'
215 213 if not isinstance(gen, bundle2.unbundle20):
216 214 txnname = "strip\n%s" % util.hidepassword(tmpbundleurl)
217 215 with repo.transaction(txnname) as tr:
218 216 bundle2.applybundle(repo, gen, tr, source='strip',
219 217 url=tmpbundleurl)
220 218 if not repo.ui.verbose:
221 219 repo.ui.popbuffer()
222 220 f.close()
223 221
224 222 with repo.transaction('repair') as tr:
225 223 bmchanges = [(m, repo[newbmtarget].node()) for m in updatebm]
226 224 repo._bookmarks.applychanges(repo, tr, bmchanges)
227 225
228 226 # remove undo files
229 227 for undovfs, undofile in repo.undofiles():
230 228 try:
231 229 undovfs.unlink(undofile)
232 230 except OSError as e:
233 231 if e.errno != errno.ENOENT:
234 232 ui.warn(_('error removing %s: %s\n') %
235 233 (undovfs.join(undofile),
236 234 stringutil.forcebytestr(e)))
237 235
238 236 except: # re-raises
239 237 if backupfile:
240 238 ui.warn(_("strip failed, backup bundle stored in '%s'\n")
241 239 % vfs.join(backupfile))
242 240 if tmpbundlefile:
243 241 ui.warn(_("strip failed, unrecovered changes stored in '%s'\n")
244 242 % vfs.join(tmpbundlefile))
245 243 ui.warn(_("(fix the problem, then recover the changesets with "
246 244 "\"hg unbundle '%s'\")\n") % vfs.join(tmpbundlefile))
247 245 raise
248 246 else:
249 247 if tmpbundlefile:
250 248 # Remove temporary bundle only if there were no exceptions
251 249 vfs.unlink(tmpbundlefile)
252 250
253 251 repo.destroyed()
254 252 # return the backup file path (or None if 'backup' was False) so
255 253 # extensions can use it
256 254 return backupfile
257 255
258 256 def softstrip(ui, repo, nodelist, backup=True, topic='backup'):
259 257 """perform a "soft" strip using the archived phase"""
260 258 tostrip = [c.node() for c in repo.set('sort(%ln::)', nodelist)]
261 259 if not tostrip:
262 260 return None
263 261
264 262 newbmtarget, updatebm = _bookmarkmovements(repo, tostrip)
265 263 if backup:
266 264 node = tostrip[0]
267 265 backupfile = _createstripbackup(repo, tostrip, node, topic)
268 266
269 267 with repo.transaction('strip') as tr:
270 268 phases.retractboundary(repo, tr, phases.archived, tostrip)
271 269 bmchanges = [(m, repo[newbmtarget].node()) for m in updatebm]
272 270 repo._bookmarks.applychanges(repo, tr, bmchanges)
273 271 return backupfile
274 272
275 273
276 274 def _bookmarkmovements(repo, tostrip):
277 275 # compute necessary bookmark movement
278 276 bm = repo._bookmarks
279 277 updatebm = []
280 278 for m in bm:
281 279 rev = repo[bm[m]].rev()
282 280 if rev in tostrip:
283 281 updatebm.append(m)
284 282 newbmtarget = None
285 283 # If we need to move bookmarks, compute bookmark
286 284 # targets. Otherwise we can skip doing this logic.
287 285 if updatebm:
288 286 # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)),
289 287 # but is much faster
290 288 newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip)
291 289 if newbmtarget:
292 290 newbmtarget = repo[newbmtarget.first()].node()
293 291 else:
294 292 newbmtarget = '.'
295 293 return newbmtarget, updatebm
296 294
297 295 def _createstripbackup(repo, stripbases, node, topic):
298 296 # backup the changeset we are about to strip
299 297 vfs = repo.vfs
300 298 cl = repo.changelog
301 299 backupfile = backupbundle(repo, stripbases, cl.heads(), node, topic)
302 300 repo.ui.status(_("saved backup bundle to %s\n") %
303 301 vfs.join(backupfile))
304 302 repo.ui.log("backupbundle", "saved backup bundle to %s\n",
305 303 vfs.join(backupfile))
306 304 return backupfile
307 305
308 306 def safestriproots(ui, repo, nodes):
309 307 """return list of roots of nodes where descendants are covered by nodes"""
310 308 torev = repo.unfiltered().changelog.rev
311 309 revs = set(torev(n) for n in nodes)
312 310 # tostrip = wanted - unsafe = wanted - ancestors(orphaned)
313 311 # orphaned = affected - wanted
314 312 # affected = descendants(roots(wanted))
315 313 # wanted = revs
316 314 revset = '%ld - ( ::( (roots(%ld):: and not _phase(%s)) -%ld) )'
317 315 tostrip = set(repo.revs(revset, revs, revs, phases.internal, revs))
318 316 notstrip = revs - tostrip
319 317 if notstrip:
320 318 nodestr = ', '.join(sorted(short(repo[n].node()) for n in notstrip))
321 319 ui.warn(_('warning: orphaned descendants detected, '
322 320 'not stripping %s\n') % nodestr)
323 321 return [c.node() for c in repo.set('roots(%ld)', tostrip)]
324 322
325 323 class stripcallback(object):
326 324 """used as a transaction postclose callback"""
327 325
328 326 def __init__(self, ui, repo, backup, topic):
329 327 self.ui = ui
330 328 self.repo = repo
331 329 self.backup = backup
332 330 self.topic = topic or 'backup'
333 331 self.nodelist = []
334 332
335 333 def addnodes(self, nodes):
336 334 self.nodelist.extend(nodes)
337 335
338 336 def __call__(self, tr):
339 337 roots = safestriproots(self.ui, self.repo, self.nodelist)
340 338 if roots:
341 339 strip(self.ui, self.repo, roots, self.backup, self.topic)
342 340
343 341 def delayedstrip(ui, repo, nodelist, topic=None, backup=True):
344 342 """like strip, but works inside transaction and won't strip irreverent revs
345 343
346 344 nodelist must explicitly contain all descendants. Otherwise a warning will
347 345 be printed that some nodes are not stripped.
348 346
349 347 Will do a backup if `backup` is True. The last non-None "topic" will be
350 348 used as the backup topic name. The default backup topic name is "backup".
351 349 """
352 350 tr = repo.currenttransaction()
353 351 if not tr:
354 352 nodes = safestriproots(ui, repo, nodelist)
355 353 return strip(ui, repo, nodes, backup=backup, topic=topic)
356 354 # transaction postclose callbacks are called in alphabet order.
357 355 # use '\xff' as prefix so we are likely to be called last.
358 356 callback = tr.getpostclose('\xffstrip')
359 357 if callback is None:
360 358 callback = stripcallback(ui, repo, backup=backup, topic=topic)
361 359 tr.addpostclose('\xffstrip', callback)
362 360 if topic:
363 361 callback.topic = topic
364 362 callback.addnodes(nodelist)
365 363
366 364 def stripmanifest(repo, striprev, tr, files):
367 365 for revlog in manifestrevlogs(repo):
368 366 revlog.strip(striprev, tr)
369 367
370 368 def manifestrevlogs(repo):
371 369 yield repo.manifestlog.getstorage(b'')
372 370 if 'treemanifest' in repo.requirements:
373 371 # This logic is safe if treemanifest isn't enabled, but also
374 372 # pointless, so we skip it if treemanifest isn't enabled.
375 373 for unencoded, encoded, size in repo.store.datafiles():
376 374 if (unencoded.startswith('meta/') and
377 375 unencoded.endswith('00manifest.i')):
378 376 dir = unencoded[5:-12]
379 377 yield repo.manifestlog.getstorage(dir)
380 378
381 379 def rebuildfncache(ui, repo):
382 380 """Rebuilds the fncache file from repo history.
383 381
384 382 Missing entries will be added. Extra entries will be removed.
385 383 """
386 384 repo = repo.unfiltered()
387 385
388 386 if 'fncache' not in repo.requirements:
389 387 ui.warn(_('(not rebuilding fncache because repository does not '
390 388 'support fncache)\n'))
391 389 return
392 390
393 391 with repo.lock():
394 392 fnc = repo.store.fncache
395 393 fnc.ensureloaded(warn=ui.warn)
396 394
397 395 oldentries = set(fnc.entries)
398 396 newentries = set()
399 397 seenfiles = set()
400 398
401 399 progress = ui.makeprogress(_('rebuilding'), unit=_('changesets'),
402 400 total=len(repo))
403 401 for rev in repo:
404 402 progress.update(rev)
405 403
406 404 ctx = repo[rev]
407 405 for f in ctx.files():
408 406 # This is to minimize I/O.
409 407 if f in seenfiles:
410 408 continue
411 409 seenfiles.add(f)
412 410
413 411 i = 'data/%s.i' % f
414 412 d = 'data/%s.d' % f
415 413
416 414 if repo.store._exists(i):
417 415 newentries.add(i)
418 416 if repo.store._exists(d):
419 417 newentries.add(d)
420 418
421 419 progress.complete()
422 420
423 421 if 'treemanifest' in repo.requirements:
424 422 # This logic is safe if treemanifest isn't enabled, but also
425 423 # pointless, so we skip it if treemanifest isn't enabled.
426 424 for dir in util.dirs(seenfiles):
427 425 i = 'meta/%s/00manifest.i' % dir
428 426 d = 'meta/%s/00manifest.d' % dir
429 427
430 428 if repo.store._exists(i):
431 429 newentries.add(i)
432 430 if repo.store._exists(d):
433 431 newentries.add(d)
434 432
435 433 addcount = len(newentries - oldentries)
436 434 removecount = len(oldentries - newentries)
437 435 for p in sorted(oldentries - newentries):
438 436 ui.write(_('removing %s\n') % p)
439 437 for p in sorted(newentries - oldentries):
440 438 ui.write(_('adding %s\n') % p)
441 439
442 440 if addcount or removecount:
443 441 ui.write(_('%d items added, %d removed from fncache\n') %
444 442 (addcount, removecount))
445 443 fnc.entries = newentries
446 444 fnc._dirty = True
447 445
448 446 with repo.transaction('fncache') as tr:
449 447 fnc.write(tr)
450 448 else:
451 449 ui.write(_('fncache already up to date\n'))
452 450
453 451 def deleteobsmarkers(obsstore, indices):
454 452 """Delete some obsmarkers from obsstore and return how many were deleted
455 453
456 454 'indices' is a list of ints which are the indices
457 455 of the markers to be deleted.
458 456
459 457 Every invocation of this function completely rewrites the obsstore file,
460 458 skipping the markers we want to be removed. The new temporary file is
461 459 created, remaining markers are written there and on .close() this file
462 460 gets atomically renamed to obsstore, thus guaranteeing consistency."""
463 461 if not indices:
464 462 # we don't want to rewrite the obsstore with the same content
465 463 return
466 464
467 465 left = []
468 466 current = obsstore._all
469 467 n = 0
470 468 for i, m in enumerate(current):
471 469 if i in indices:
472 470 n += 1
473 471 continue
474 472 left.append(m)
475 473
476 474 newobsstorefile = obsstore.svfs('obsstore', 'w', atomictemp=True)
477 475 for bytes in obsolete.encodemarkers(left, True, obsstore._version):
478 476 newobsstorefile.write(bytes)
479 477 newobsstorefile.close()
480 478 return n
@@ -1,225 +1,220 b''
1 1 test stripping of filelogs where the linkrev doesn't always increase
2 2
3 3 $ echo '[extensions]' >> $HGRCPATH
4 4 $ echo 'strip =' >> $HGRCPATH
5 5 $ commit()
6 6 > {
7 7 > hg up -qC null
8 8 > count=1
9 9 > for i in "$@"; do
10 10 > for f in $i; do
11 11 > mkdir -p `dirname $f`
12 12 > echo $count > $f
13 13 > done
14 14 > count=`expr $count + 1`
15 15 > done
16 16 > hg commit -qAm "$*"
17 17 > }
18 18
19 19 2 1 0 2 0 1 2
20 20
21 21 $ mkdir files
22 22 $ cd files
23 23 $ hg init orig
24 24 $ cd orig
25 25 $ commit '201 210'
26 26 $ commit '102 120' '210'
27 27 $ commit '021'
28 28 $ commit '201' '021 120'
29 29 $ commit '012 021' '102 201' '120 210'
30 30 $ commit '102 120' '012 210' '021 201'
31 31 $ commit '201 210' '021 120' '012 102'
32 32 $ cd ..
33 33 $ hg clone -q -U -r 4 -r 5 -r 6 orig crossed
34 34 $ cd crossed
35 35
36 36 $ for i in 012 021 102 120 201 210; do
37 37 > echo $i
38 38 > hg debugindex $i
39 39 > echo
40 40 > done
41 41 012
42 42 rev linkrev nodeid p1 p2
43 43 0 0 b8e02f643373 000000000000 000000000000
44 44 1 1 5d9299349fc0 000000000000 000000000000
45 45 2 2 2661d26c6496 000000000000 000000000000
46 46
47 47 021
48 48 rev linkrev nodeid p1 p2
49 49 0 0 b8e02f643373 000000000000 000000000000
50 50 1 2 5d9299349fc0 000000000000 000000000000
51 51 2 1 2661d26c6496 000000000000 000000000000
52 52
53 53 102
54 54 rev linkrev nodeid p1 p2
55 55 0 1 b8e02f643373 000000000000 000000000000
56 56 1 0 5d9299349fc0 000000000000 000000000000
57 57 2 2 2661d26c6496 000000000000 000000000000
58 58
59 59 120
60 60 rev linkrev nodeid p1 p2
61 61 0 1 b8e02f643373 000000000000 000000000000
62 62 1 2 5d9299349fc0 000000000000 000000000000
63 63 2 0 2661d26c6496 000000000000 000000000000
64 64
65 65 201
66 66 rev linkrev nodeid p1 p2
67 67 0 2 b8e02f643373 000000000000 000000000000
68 68 1 0 5d9299349fc0 000000000000 000000000000
69 69 2 1 2661d26c6496 000000000000 000000000000
70 70
71 71 210
72 72 rev linkrev nodeid p1 p2
73 73 0 2 b8e02f643373 000000000000 000000000000
74 74 1 1 5d9299349fc0 000000000000 000000000000
75 75 2 0 2661d26c6496 000000000000 000000000000
76 76
77 77 $ cd ..
78 78 $ for i in 0 1 2; do
79 79 > hg clone -q -U --pull crossed $i
80 80 > echo "% Trying to strip revision $i"
81 81 > hg --cwd $i strip $i
82 82 > echo "% Verifying"
83 83 > hg --cwd $i verify
84 84 > echo
85 85 > done
86 86 % Trying to strip revision 0
87 87 saved backup bundle to $TESTTMP/files/0/.hg/strip-backup/cbb8c2f0a2e3-239800b9-backup.hg
88 88 % Verifying
89 89 checking changesets
90 90 checking manifests
91 91 crosschecking files in changesets and manifests
92 92 checking files
93 93 checked 2 changesets with 12 changes to 6 files
94 94
95 95 % Trying to strip revision 1
96 96 saved backup bundle to $TESTTMP/files/1/.hg/strip-backup/124ecc0cbec9-6104543f-backup.hg
97 97 % Verifying
98 98 checking changesets
99 99 checking manifests
100 100 crosschecking files in changesets and manifests
101 101 checking files
102 102 checked 2 changesets with 12 changes to 6 files
103 103
104 104 % Trying to strip revision 2
105 105 saved backup bundle to $TESTTMP/files/2/.hg/strip-backup/f6439b304a1a-c6505a5f-backup.hg
106 106 % Verifying
107 107 checking changesets
108 108 checking manifests
109 109 crosschecking files in changesets and manifests
110 110 checking files
111 111 checked 2 changesets with 12 changes to 6 files
112 112
113 113 $ cd ..
114 114
115 115 Do a similar test where the manifest revlog has unordered linkrevs
116 116 $ mkdir manifests
117 117 $ cd manifests
118 118 $ hg init orig
119 119 $ cd orig
120 120 $ commit 'file'
121 121 $ commit 'other'
122 122 $ commit '' 'other'
123 123 $ HGUSER=another-user; export HGUSER
124 124 $ commit 'file'
125 125 $ commit 'other' 'file'
126 126 $ cd ..
127 127 $ hg clone -q -U -r 1 -r 2 -r 3 -r 4 orig crossed
128 128 $ cd crossed
129 129 $ hg debugindex --manifest
130 130 rev linkrev nodeid p1 p2
131 131 0 2 6bbc6fee55c2 000000000000 000000000000
132 132 1 0 1c556153fe54 000000000000 000000000000
133 133 2 1 1f76dba919fd 000000000000 000000000000
134 134 3 3 bbee06ad59d5 000000000000 000000000000
135 135
136 136 $ cd ..
137 137 $ for i in 2 3; do
138 138 > hg clone -q -U --pull crossed $i
139 139 > echo "% Trying to strip revision $i"
140 140 > hg --cwd $i strip $i
141 141 > echo "% Verifying"
142 142 > hg --cwd $i verify
143 143 > echo
144 144 > done
145 145 % Trying to strip revision 2
146 146 saved backup bundle to $TESTTMP/manifests/2/.hg/strip-backup/f3015ad03c03-4d98bdc2-backup.hg
147 147 % Verifying
148 148 checking changesets
149 149 checking manifests
150 150 crosschecking files in changesets and manifests
151 151 checking files
152 152 checked 3 changesets with 3 changes to 2 files
153 153
154 154 % Trying to strip revision 3
155 155 saved backup bundle to $TESTTMP/manifests/3/.hg/strip-backup/9632aa303aa4-69192e3f-backup.hg
156 156 % Verifying
157 157 checking changesets
158 158 checking manifests
159 159 crosschecking files in changesets and manifests
160 160 checking files
161 161 checked 3 changesets with 3 changes to 2 files
162 162
163 163 $ cd ..
164 164
165 165 Now a similar test for a non-root manifest revlog
166 166 $ cat >> $HGRCPATH <<EOF
167 167 > [experimental]
168 168 > treemanifests = yes
169 169 > EOF
170 170 $ mkdir treemanifests
171 171 $ cd treemanifests
172 172 $
173 173 $ hg --config experimental.treemanifest=True init orig
174 174 $ cd orig
175 175 $ commit 'dir/file'
176 176 $ commit 'dir/other'
177 177 $ commit '' 'dir/other'
178 178 $ HGUSER=yet-another-user; export HGUSER
179 179 $ commit 'otherdir dir/file'
180 180 $ commit 'otherdir dir/other' 'otherdir dir/file'
181 181 $ cd ..
182 182 $ hg --config experimental.treemanifest=True clone -q -U -r 1 -r 2 -r 3 -r 4 orig crossed
183 183 $ cd crossed
184 184 $ hg debugindex --dir dir
185 185 rev linkrev nodeid p1 p2
186 186 0 2 6bbc6fee55c2 000000000000 000000000000
187 187 1 0 1c556153fe54 000000000000 000000000000
188 188 2 1 1f76dba919fd 000000000000 000000000000
189 189 3 3 bbee06ad59d5 000000000000 000000000000
190 190
191 191 $ cd ..
192 192 $ for i in 2 3; do
193 193 > hg --config experimental.treemanifest=True clone -q -U --pull crossed $i
194 194 > echo "% Trying to strip revision $i"
195 195 > hg --cwd $i strip $i
196 196 > echo "% Verifying"
197 197 > hg --cwd $i verify
198 198 > echo
199 199 > done
200 200 % Trying to strip revision 2
201 201 saved backup bundle to $TESTTMP/treemanifests/2/.hg/strip-backup/145f5c75f9ac-a105cfbe-backup.hg
202 202 % Verifying
203 203 checking changesets
204 204 checking manifests
205 205 checking directory manifests
206 dir/@0: parent-directory manifest refers to unknown revision 1c556153fe54
207 dir/@1: parent-directory manifest refers to unknown revision 1f76dba919fd
208 206 crosschecking files in changesets and manifests
209 207 checking files
210 dir/other@1: 5d9299349fc0 not in manifests
211 208 checked 3 changesets with 4 changes to 3 files
212 3 integrity errors encountered!
213 (first damaged changeset appears to be 0)
214 209
215 210 % Trying to strip revision 3
216 211 saved backup bundle to $TESTTMP/treemanifests/3/.hg/strip-backup/e4e3de5c3cb2-f4c70376-backup.hg
217 212 % Verifying
218 213 checking changesets
219 214 checking manifests
220 215 checking directory manifests
221 216 crosschecking files in changesets and manifests
222 217 checking files
223 218 checked 3 changesets with 4 changes to 3 files
224 219
225 220 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now