##// END OF EJS Templates
verify: check for orphaned dirlogs...
Martin von Zweigbergk -
r28204:962921c3 default
parent child Browse files
Show More
@@ -1,408 +1,422
1 1 # verify.py - repository integrity checking for Mercurial
2 2 #
3 3 # Copyright 2006, 2007 Matt Mackall <mpm@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 os
11 11
12 12 from .i18n import _
13 13 from .node import (
14 14 nullid,
15 15 short,
16 16 )
17 17
18 18 from . import (
19 19 error,
20 20 revlog,
21 21 util,
22 22 )
23 23
24 24 def verify(repo):
25 25 with repo.lock():
26 26 return verifier(repo).verify()
27 27
28 28 def _normpath(f):
29 29 # under hg < 2.4, convert didn't sanitize paths properly, so a
30 30 # converted repo may contain repeated slashes
31 31 while '//' in f:
32 32 f = f.replace('//', '/')
33 33 return f
34 34
35 35 def _validpath(repo, path):
36 36 """Returns False if a path should NOT be treated as part of a repo.
37 37
38 38 For all in-core cases, this returns True, as we have no way for a
39 39 path to be mentioned in the history but not actually be
40 40 relevant. For narrow clones, this is important because many
41 41 filelogs will be missing, and changelog entries may mention
42 42 modified files that are outside the narrow scope.
43 43 """
44 44 return True
45 45
46 46 class verifier(object):
47 47 def __init__(self, repo):
48 48 self.repo = repo.unfiltered()
49 49 self.ui = repo.ui
50 50 self.badrevs = set()
51 51 self.errors = 0
52 52 self.warnings = 0
53 53 self.havecl = len(repo.changelog) > 0
54 54 self.havemf = len(repo.manifest) > 0
55 55 self.revlogv1 = repo.changelog.version != revlog.REVLOGV0
56 56 self.lrugetctx = util.lrucachefunc(repo.changectx)
57 57 self.refersmf = False
58 58 self.fncachewarned = False
59 59
60 60 def warn(self, msg):
61 61 self.ui.warn(msg + "\n")
62 62 self.warnings += 1
63 63
64 64 def err(self, linkrev, msg, filename=None):
65 65 if linkrev is not None:
66 66 self.badrevs.add(linkrev)
67 67 else:
68 68 linkrev = '?'
69 69 msg = "%s: %s" % (linkrev, msg)
70 70 if filename:
71 71 msg = "%s@%s" % (filename, msg)
72 72 self.ui.warn(" " + msg + "\n")
73 73 self.errors += 1
74 74
75 75 def exc(self, linkrev, msg, inst, filename=None):
76 76 if not str(inst):
77 77 inst = repr(inst)
78 78 self.err(linkrev, "%s: %s" % (msg, inst), filename)
79 79
80 80 def checklog(self, obj, name, linkrev):
81 81 if not len(obj) and (self.havecl or self.havemf):
82 82 self.err(linkrev, _("empty or missing %s") % name)
83 83 return
84 84
85 85 d = obj.checksize()
86 86 if d[0]:
87 87 self.err(None, _("data length off by %d bytes") % d[0], name)
88 88 if d[1]:
89 89 self.err(None, _("index contains %d extra bytes") % d[1], name)
90 90
91 91 if obj.version != revlog.REVLOGV0:
92 92 if not self.revlogv1:
93 93 self.warn(_("warning: `%s' uses revlog format 1") % name)
94 94 elif self.revlogv1:
95 95 self.warn(_("warning: `%s' uses revlog format 0") % name)
96 96
97 97 def checkentry(self, obj, i, node, seen, linkrevs, f):
98 98 lr = obj.linkrev(obj.rev(node))
99 99 if lr < 0 or (self.havecl and lr not in linkrevs):
100 100 if lr < 0 or lr >= len(self.repo.changelog):
101 101 msg = _("rev %d points to nonexistent changeset %d")
102 102 else:
103 103 msg = _("rev %d points to unexpected changeset %d")
104 104 self.err(None, msg % (i, lr), f)
105 105 if linkrevs:
106 106 if f and len(linkrevs) > 1:
107 107 try:
108 108 # attempt to filter down to real linkrevs
109 109 linkrevs = [l for l in linkrevs
110 110 if self.lrugetctx(l)[f].filenode() == node]
111 111 except Exception:
112 112 pass
113 113 self.warn(_(" (expected %s)") % " ".join(map(str, linkrevs)))
114 114 lr = None # can't be trusted
115 115
116 116 try:
117 117 p1, p2 = obj.parents(node)
118 118 if p1 not in seen and p1 != nullid:
119 119 self.err(lr, _("unknown parent 1 %s of %s") %
120 120 (short(p1), short(node)), f)
121 121 if p2 not in seen and p2 != nullid:
122 122 self.err(lr, _("unknown parent 2 %s of %s") %
123 123 (short(p2), short(node)), f)
124 124 except Exception as inst:
125 125 self.exc(lr, _("checking parents of %s") % short(node), inst, f)
126 126
127 127 if node in seen:
128 128 self.err(lr, _("duplicate revision %d (%d)") % (i, seen[node]), f)
129 129 seen[node] = i
130 130 return lr
131 131
132 132 def verify(self):
133 133 repo = self.repo
134 134
135 135 ui = repo.ui
136 136
137 137 if not repo.url().startswith('file:'):
138 138 raise error.Abort(_("cannot verify bundle or remote repos"))
139 139
140 140 if os.path.exists(repo.sjoin("journal")):
141 141 ui.warn(_("abandoned transaction found - run hg recover\n"))
142 142
143 143 if ui.verbose or not self.revlogv1:
144 144 ui.status(_("repository uses revlog format %d\n") %
145 145 (self.revlogv1 and 1 or 0))
146 146
147 147 mflinkrevs, filelinkrevs = self._verifychangelog()
148 148
149 149 filenodes = self._verifymanifest(mflinkrevs)
150 150 del mflinkrevs
151 151
152 152 self._crosscheckfiles(filelinkrevs, filenodes)
153 153
154 154 totalfiles, filerevisions = self._verifyfiles(filenodes, filelinkrevs)
155 155
156 156 ui.status(_("%d files, %d changesets, %d total revisions\n") %
157 157 (totalfiles, len(repo.changelog), filerevisions))
158 158 if self.warnings:
159 159 ui.warn(_("%d warnings encountered!\n") % self.warnings)
160 160 if self.fncachewarned:
161 161 ui.warn(_('hint: run "hg debugrebuildfncache" to recover from '
162 162 'corrupt fncache\n'))
163 163 if self.errors:
164 164 ui.warn(_("%d integrity errors encountered!\n") % self.errors)
165 165 if self.badrevs:
166 166 ui.warn(_("(first damaged changeset appears to be %d)\n")
167 167 % min(self.badrevs))
168 168 return 1
169 169
170 170 def _verifychangelog(self):
171 171 ui = self.ui
172 172 repo = self.repo
173 173 cl = repo.changelog
174 174
175 175 ui.status(_("checking changesets\n"))
176 176 mflinkrevs = {}
177 177 filelinkrevs = {}
178 178 seen = {}
179 179 self.checklog(cl, "changelog", 0)
180 180 total = len(repo)
181 181 for i in repo:
182 182 ui.progress(_('checking'), i, total=total, unit=_('changesets'))
183 183 n = cl.node(i)
184 184 self.checkentry(cl, i, n, seen, [i], "changelog")
185 185
186 186 try:
187 187 changes = cl.read(n)
188 188 if changes[0] != nullid:
189 189 mflinkrevs.setdefault(changes[0], []).append(i)
190 190 self.refersmf = True
191 191 for f in changes[3]:
192 192 if _validpath(repo, f):
193 193 filelinkrevs.setdefault(_normpath(f), []).append(i)
194 194 except Exception as inst:
195 195 self.refersmf = True
196 196 self.exc(i, _("unpacking changeset %s") % short(n), inst)
197 197 ui.progress(_('checking'), None)
198 198 return mflinkrevs, filelinkrevs
199 199
200 def _verifymanifest(self, mflinkrevs, dir=""):
200 def _verifymanifest(self, mflinkrevs, dir="", storefiles=None):
201 201 repo = self.repo
202 202 ui = self.ui
203 203 mf = self.repo.manifest.dirlog(dir)
204 204
205 205 if not dir:
206 206 self.ui.status(_("checking manifests\n"))
207 207
208 208 filenodes = {}
209 209 subdirnodes = {}
210 210 seen = {}
211 211 label = "manifest"
212 212 if dir:
213 213 label = dir
214 revlogfiles = mf.files()
215 storefiles.difference_update(revlogfiles)
214 216 if self.refersmf:
215 217 # Do not check manifest if there are only changelog entries with
216 218 # null manifests.
217 219 self.checklog(mf, label, 0)
218 220 total = len(mf)
219 221 for i in mf:
220 222 if not dir:
221 223 ui.progress(_('checking'), i, total=total, unit=_('manifests'))
222 224 n = mf.node(i)
223 225 lr = self.checkentry(mf, i, n, seen, mflinkrevs.get(n, []), label)
224 226 if n in mflinkrevs:
225 227 del mflinkrevs[n]
226 228 elif dir:
227 229 self.err(lr, _("%s not in parent-directory manifest") %
228 230 short(n), label)
229 231 else:
230 232 self.err(lr, _("%s not in changesets") % short(n), label)
231 233
232 234 try:
233 235 for f, fn, fl in mf.readshallowdelta(n).iterentries():
234 236 if not f:
235 237 self.err(lr, _("entry without name in manifest"))
236 238 elif f == "/dev/null": # ignore this in very old repos
237 239 continue
238 240 fullpath = dir + _normpath(f)
239 241 if not _validpath(repo, fullpath):
240 242 continue
241 243 if fl == 't':
242 244 subdirnodes.setdefault(fullpath + '/', {}).setdefault(
243 245 fn, []).append(lr)
244 246 else:
245 247 filenodes.setdefault(fullpath, {}).setdefault(fn, lr)
246 248 except Exception as inst:
247 249 self.exc(lr, _("reading delta %s") % short(n), inst, label)
248 250 if not dir:
249 251 ui.progress(_('checking'), None)
250 252
251 253 if self.havemf:
252 254 for c, m in sorted([(c, m) for m in mflinkrevs
253 255 for c in mflinkrevs[m]]):
254 256 if dir:
255 257 self.err(c, _("parent-directory manifest refers to unknown "
256 258 "revision %s") % short(m), label)
257 259 else:
258 260 self.err(c, _("changeset refers to unknown revision %s") %
259 261 short(m), label)
260 262
261 263 if not dir and subdirnodes:
262 264 self.ui.status(_("checking directory manifests\n"))
265 storefiles = set()
266 revlogv1 = self.revlogv1
267 for f, f2, size in repo.store.datafiles():
268 if not f:
269 self.err(None, _("cannot decode filename '%s'") % f2)
270 elif (size > 0 or not revlogv1) and f.startswith('meta/'):
271 storefiles.add(_normpath(f))
272
263 273 for subdir, linkrevs in subdirnodes.iteritems():
264 subdirfilenodes = self._verifymanifest(linkrevs, subdir)
274 subdirfilenodes = self._verifymanifest(linkrevs, subdir, storefiles)
265 275 for f, onefilenodes in subdirfilenodes.iteritems():
266 276 filenodes.setdefault(f, {}).update(onefilenodes)
267 277
278 if not dir and subdirnodes:
279 for f in sorted(storefiles):
280 self.warn(_("warning: orphan revlog '%s'") % f)
281
268 282 return filenodes
269 283
270 284 def _crosscheckfiles(self, filelinkrevs, filenodes):
271 285 repo = self.repo
272 286 ui = self.ui
273 287 ui.status(_("crosschecking files in changesets and manifests\n"))
274 288
275 289 total = len(filelinkrevs) + len(filenodes)
276 290 count = 0
277 291 if self.havemf:
278 292 for f in sorted(filelinkrevs):
279 293 count += 1
280 294 ui.progress(_('crosschecking'), count, total=total)
281 295 if f not in filenodes:
282 296 lr = filelinkrevs[f][0]
283 297 self.err(lr, _("in changeset but not in manifest"), f)
284 298
285 299 if self.havecl:
286 300 for f in sorted(filenodes):
287 301 count += 1
288 302 ui.progress(_('crosschecking'), count, total=total)
289 303 if f not in filelinkrevs:
290 304 try:
291 305 fl = repo.file(f)
292 306 lr = min([fl.linkrev(fl.rev(n)) for n in filenodes[f]])
293 307 except Exception:
294 308 lr = None
295 309 self.err(lr, _("in manifest but not in changeset"), f)
296 310
297 311 ui.progress(_('crosschecking'), None)
298 312
299 313 def _verifyfiles(self, filenodes, filelinkrevs):
300 314 repo = self.repo
301 315 ui = self.ui
302 316 lrugetctx = self.lrugetctx
303 317 revlogv1 = self.revlogv1
304 318 havemf = self.havemf
305 319 ui.status(_("checking files\n"))
306 320
307 321 storefiles = set()
308 322 for f, f2, size in repo.store.datafiles():
309 323 if not f:
310 324 self.err(None, _("cannot decode filename '%s'") % f2)
311 325 elif (size > 0 or not revlogv1) and f.startswith('data/'):
312 326 storefiles.add(_normpath(f))
313 327
314 328 files = sorted(set(filenodes) | set(filelinkrevs))
315 329 total = len(files)
316 330 revisions = 0
317 331 for i, f in enumerate(files):
318 332 ui.progress(_('checking'), i, item=f, total=total)
319 333 try:
320 334 linkrevs = filelinkrevs[f]
321 335 except KeyError:
322 336 # in manifest but not in changelog
323 337 linkrevs = []
324 338
325 339 if linkrevs:
326 340 lr = linkrevs[0]
327 341 else:
328 342 lr = None
329 343
330 344 try:
331 345 fl = repo.file(f)
332 346 except error.RevlogError as e:
333 347 self.err(lr, _("broken revlog! (%s)") % e, f)
334 348 continue
335 349
336 350 for ff in fl.files():
337 351 try:
338 352 storefiles.remove(ff)
339 353 except KeyError:
340 354 self.warn(_(" warning: revlog '%s' not in fncache!") % ff)
341 355 self.fncachewarned = True
342 356
343 357 self.checklog(fl, f, lr)
344 358 seen = {}
345 359 rp = None
346 360 for i in fl:
347 361 revisions += 1
348 362 n = fl.node(i)
349 363 lr = self.checkentry(fl, i, n, seen, linkrevs, f)
350 364 if f in filenodes:
351 365 if havemf and n not in filenodes[f]:
352 366 self.err(lr, _("%s not in manifests") % (short(n)), f)
353 367 else:
354 368 del filenodes[f][n]
355 369
356 370 # verify contents
357 371 try:
358 372 l = len(fl.read(n))
359 373 rp = fl.renamed(n)
360 374 if l != fl.size(i):
361 375 if len(fl.revision(n)) != fl.size(i):
362 376 self.err(lr, _("unpacked size is %s, %s expected") %
363 377 (l, fl.size(i)), f)
364 378 except error.CensoredNodeError:
365 379 # experimental config: censor.policy
366 380 if ui.config("censor", "policy", "abort") == "abort":
367 381 self.err(lr, _("censored file data"), f)
368 382 except Exception as inst:
369 383 self.exc(lr, _("unpacking %s") % short(n), inst, f)
370 384
371 385 # check renames
372 386 try:
373 387 if rp:
374 388 if lr is not None and ui.verbose:
375 389 ctx = lrugetctx(lr)
376 390 found = False
377 391 for pctx in ctx.parents():
378 392 if rp[0] in pctx:
379 393 found = True
380 394 break
381 395 if not found:
382 396 self.warn(_("warning: copy source of '%s' not"
383 397 " in parents of %s") % (f, ctx))
384 398 fl2 = repo.file(rp[0])
385 399 if not len(fl2):
386 400 self.err(lr, _("empty or missing copy source "
387 401 "revlog %s:%s") % (rp[0], short(rp[1])), f)
388 402 elif rp[1] == nullid:
389 403 ui.note(_("warning: %s@%s: copy source"
390 404 " revision is nullid %s:%s\n")
391 405 % (f, lr, rp[0], short(rp[1])))
392 406 else:
393 407 fl2.rev(rp[1])
394 408 except Exception as inst:
395 409 self.exc(lr, _("checking rename of %s") % short(n), inst, f)
396 410
397 411 # cross-check
398 412 if f in filenodes:
399 413 fns = [(lr, n) for n, lr in filenodes[f].iteritems()]
400 414 for lr, node in sorted(fns):
401 415 self.err(lr, _("manifest refers to unknown revision %s") %
402 416 short(node), f)
403 417 ui.progress(_('checking'), None)
404 418
405 for f in storefiles:
419 for f in sorted(storefiles):
406 420 self.warn(_("warning: orphan revlog '%s'") % f)
407 421
408 422 return len(files), revisions
@@ -1,724 +1,731
1 1 #require killdaemons
2 2
3 3 $ cat << EOF >> $HGRCPATH
4 4 > [format]
5 5 > usegeneraldelta=yes
6 6 > [ui]
7 7 > ssh=python "$TESTDIR/dummyssh"
8 8 > EOF
9 9
10 10 Set up repo
11 11
12 12 $ hg --config experimental.treemanifest=True init repo
13 13 $ cd repo
14 14
15 15 Requirements get set on init
16 16
17 17 $ grep treemanifest .hg/requires
18 18 treemanifest
19 19
20 20 Without directories, looks like any other repo
21 21
22 22 $ echo 0 > a
23 23 $ echo 0 > b
24 24 $ hg ci -Aqm initial
25 25 $ hg debugdata -m 0
26 26 a\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
27 27 b\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
28 28
29 29 Submanifest is stored in separate revlog
30 30
31 31 $ mkdir dir1
32 32 $ echo 1 > dir1/a
33 33 $ echo 1 > dir1/b
34 34 $ echo 1 > e
35 35 $ hg ci -Aqm 'add dir1'
36 36 $ hg debugdata -m 1
37 37 a\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
38 38 b\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
39 39 dir1\x008b3ffd73f901e83304c83d33132c8e774ceac44et (esc)
40 40 e\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
41 41 $ hg debugdata --dir dir1 0
42 42 a\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
43 43 b\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
44 44
45 45 Can add nested directories
46 46
47 47 $ mkdir dir1/dir1
48 48 $ echo 2 > dir1/dir1/a
49 49 $ echo 2 > dir1/dir1/b
50 50 $ mkdir dir1/dir2
51 51 $ echo 2 > dir1/dir2/a
52 52 $ echo 2 > dir1/dir2/b
53 53 $ hg ci -Aqm 'add dir1/dir1'
54 54 $ hg files -r .
55 55 a
56 56 b
57 57 dir1/a (glob)
58 58 dir1/b (glob)
59 59 dir1/dir1/a (glob)
60 60 dir1/dir1/b (glob)
61 61 dir1/dir2/a (glob)
62 62 dir1/dir2/b (glob)
63 63 e
64 64
65 65 Revision is not created for unchanged directory
66 66
67 67 $ mkdir dir2
68 68 $ echo 3 > dir2/a
69 69 $ hg add dir2
70 70 adding dir2/a (glob)
71 71 $ hg debugindex --dir dir1 > before
72 72 $ hg ci -qm 'add dir2'
73 73 $ hg debugindex --dir dir1 > after
74 74 $ diff before after
75 75 $ rm before after
76 76
77 77 Removing directory does not create an revlog entry
78 78
79 79 $ hg rm dir1/dir1
80 80 removing dir1/dir1/a (glob)
81 81 removing dir1/dir1/b (glob)
82 82 $ hg debugindex --dir dir1/dir1 > before
83 83 $ hg ci -qm 'remove dir1/dir1'
84 84 $ hg debugindex --dir dir1/dir1 > after
85 85 $ diff before after
86 86 $ rm before after
87 87
88 88 Check that hg files (calls treemanifest.walk()) works
89 89 without loading all directory revlogs
90 90
91 91 $ hg co 'desc("add dir2")'
92 92 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
93 93 $ mv .hg/store/meta/dir2 .hg/store/meta/dir2-backup
94 94 $ hg files -r . dir1
95 95 dir1/a (glob)
96 96 dir1/b (glob)
97 97 dir1/dir1/a (glob)
98 98 dir1/dir1/b (glob)
99 99 dir1/dir2/a (glob)
100 100 dir1/dir2/b (glob)
101 101
102 102 Check that status between revisions works (calls treemanifest.matches())
103 103 without loading all directory revlogs
104 104
105 105 $ hg status --rev 'desc("add dir1")' --rev . dir1
106 106 A dir1/dir1/a
107 107 A dir1/dir1/b
108 108 A dir1/dir2/a
109 109 A dir1/dir2/b
110 110 $ mv .hg/store/meta/dir2-backup .hg/store/meta/dir2
111 111
112 112 Merge creates 2-parent revision of directory revlog
113 113
114 114 $ echo 5 > dir1/a
115 115 $ hg ci -Aqm 'modify dir1/a'
116 116 $ hg co '.^'
117 117 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
118 118 $ echo 6 > dir1/b
119 119 $ hg ci -Aqm 'modify dir1/b'
120 120 $ hg merge 'desc("modify dir1/a")'
121 121 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
122 122 (branch merge, don't forget to commit)
123 123 $ hg ci -m 'conflict-free merge involving dir1/'
124 124 $ cat dir1/a
125 125 5
126 126 $ cat dir1/b
127 127 6
128 128 $ hg debugindex --dir dir1
129 129 rev offset length delta linkrev nodeid p1 p2
130 130 0 0 54 -1 1 8b3ffd73f901 000000000000 000000000000
131 131 1 54 68 0 2 68e9d057c5a8 8b3ffd73f901 000000000000
132 132 2 122 12 1 4 4698198d2624 68e9d057c5a8 000000000000
133 133 3 134 55 1 5 44844058ccce 68e9d057c5a8 000000000000
134 134 4 189 55 1 6 bf3d9b744927 68e9d057c5a8 000000000000
135 135 5 244 55 4 7 dde7c0af2a03 bf3d9b744927 44844058ccce
136 136
137 137 Merge keeping directory from parent 1 does not create revlog entry. (Note that
138 138 dir1's manifest does change, but only because dir1/a's filelog changes.)
139 139
140 140 $ hg co 'desc("add dir2")'
141 141 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
142 142 $ echo 8 > dir2/a
143 143 $ hg ci -m 'modify dir2/a'
144 144 created new head
145 145
146 146 $ hg debugindex --dir dir2 > before
147 147 $ hg merge 'desc("modify dir1/a")'
148 148 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
149 149 (branch merge, don't forget to commit)
150 150 $ hg revert -r 'desc("modify dir2/a")' .
151 151 reverting dir1/a (glob)
152 152 $ hg ci -m 'merge, keeping parent 1'
153 153 $ hg debugindex --dir dir2 > after
154 154 $ diff before after
155 155 $ rm before after
156 156
157 157 Merge keeping directory from parent 2 does not create revlog entry. (Note that
158 158 dir2's manifest does change, but only because dir2/a's filelog changes.)
159 159
160 160 $ hg co 'desc("modify dir2/a")'
161 161 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
162 162 $ hg debugindex --dir dir1 > before
163 163 $ hg merge 'desc("modify dir1/a")'
164 164 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
165 165 (branch merge, don't forget to commit)
166 166 $ hg revert -r 'desc("modify dir1/a")' .
167 167 reverting dir2/a (glob)
168 168 $ hg ci -m 'merge, keeping parent 2'
169 169 created new head
170 170 $ hg debugindex --dir dir1 > after
171 171 $ diff before after
172 172 $ rm before after
173 173
174 174 Create flat source repo for tests with mixed flat/tree manifests
175 175
176 176 $ cd ..
177 177 $ hg init repo-flat
178 178 $ cd repo-flat
179 179
180 180 Create a few commits with flat manifest
181 181
182 182 $ echo 0 > a
183 183 $ echo 0 > b
184 184 $ echo 0 > e
185 185 $ for d in dir1 dir1/dir1 dir1/dir2 dir2
186 186 > do
187 187 > mkdir $d
188 188 > echo 0 > $d/a
189 189 > echo 0 > $d/b
190 190 > done
191 191 $ hg ci -Aqm initial
192 192
193 193 $ echo 1 > a
194 194 $ echo 1 > dir1/a
195 195 $ echo 1 > dir1/dir1/a
196 196 $ hg ci -Aqm 'modify on branch 1'
197 197
198 198 $ hg co 0
199 199 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
200 200 $ echo 2 > b
201 201 $ echo 2 > dir1/b
202 202 $ echo 2 > dir1/dir1/b
203 203 $ hg ci -Aqm 'modify on branch 2'
204 204
205 205 $ hg merge 1
206 206 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
207 207 (branch merge, don't forget to commit)
208 208 $ hg ci -m 'merge of flat manifests to new flat manifest'
209 209
210 210 $ hg serve -p $HGPORT -d --pid-file=hg.pid --errorlog=errors.log
211 211 $ cat hg.pid >> $DAEMON_PIDS
212 212
213 213 Create clone with tree manifests enabled
214 214
215 215 $ cd ..
216 216 $ hg clone --config experimental.treemanifest=1 \
217 217 > http://localhost:$HGPORT repo-mixed -r 1
218 218 adding changesets
219 219 adding manifests
220 220 adding file changes
221 221 added 2 changesets with 14 changes to 11 files
222 222 updating to branch default
223 223 11 files updated, 0 files merged, 0 files removed, 0 files unresolved
224 224 $ cd repo-mixed
225 225 $ test -d .hg/store/meta
226 226 [1]
227 227 $ grep treemanifest .hg/requires
228 228 treemanifest
229 229
230 230 Should be possible to push updates from flat to tree manifest repo
231 231
232 232 $ hg -R ../repo-flat push ssh://user@dummy/repo-mixed
233 233 pushing to ssh://user@dummy/repo-mixed
234 234 searching for changes
235 235 remote: adding changesets
236 236 remote: adding manifests
237 237 remote: adding file changes
238 238 remote: added 2 changesets with 3 changes to 3 files
239 239
240 240 Commit should store revlog per directory
241 241
242 242 $ hg co 1
243 243 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
244 244 $ echo 3 > a
245 245 $ echo 3 > dir1/a
246 246 $ echo 3 > dir1/dir1/a
247 247 $ hg ci -m 'first tree'
248 248 created new head
249 249 $ find .hg/store/meta | sort
250 250 .hg/store/meta
251 251 .hg/store/meta/dir1
252 252 .hg/store/meta/dir1/00manifest.i
253 253 .hg/store/meta/dir1/dir1
254 254 .hg/store/meta/dir1/dir1/00manifest.i
255 255 .hg/store/meta/dir1/dir2
256 256 .hg/store/meta/dir1/dir2/00manifest.i
257 257 .hg/store/meta/dir2
258 258 .hg/store/meta/dir2/00manifest.i
259 259
260 260 Merge of two trees
261 261
262 262 $ hg co 2
263 263 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
264 264 $ hg merge 1
265 265 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
266 266 (branch merge, don't forget to commit)
267 267 $ hg ci -m 'merge of flat manifests to new tree manifest'
268 268 created new head
269 269 $ hg diff -r 3
270 270
271 271 Parent of tree root manifest should be flat manifest, and two for merge
272 272
273 273 $ hg debugindex -m
274 274 rev offset length delta linkrev nodeid p1 p2
275 275 0 0 80 -1 0 40536115ed9e 000000000000 000000000000
276 276 1 80 83 0 1 f3376063c255 40536115ed9e 000000000000
277 277 2 163 89 0 2 5d9b9da231a2 40536115ed9e 000000000000
278 278 3 252 83 2 3 d17d663cbd8a 5d9b9da231a2 f3376063c255
279 279 4 335 124 1 4 51e32a8c60ee f3376063c255 000000000000
280 280 5 459 126 2 5 cc5baa78b230 5d9b9da231a2 f3376063c255
281 281
282 282
283 283 Status across flat/tree boundary should work
284 284
285 285 $ hg status --rev '.^' --rev .
286 286 M a
287 287 M dir1/a
288 288 M dir1/dir1/a
289 289
290 290
291 291 Turning off treemanifest config has no effect
292 292
293 293 $ hg debugindex --dir dir1
294 294 rev offset length delta linkrev nodeid p1 p2
295 295 0 0 127 -1 4 064927a0648a 000000000000 000000000000
296 296 1 127 111 0 5 25ecb8cb8618 000000000000 000000000000
297 297 $ echo 2 > dir1/a
298 298 $ hg --config experimental.treemanifest=False ci -qm 'modify dir1/a'
299 299 $ hg debugindex --dir dir1
300 300 rev offset length delta linkrev nodeid p1 p2
301 301 0 0 127 -1 4 064927a0648a 000000000000 000000000000
302 302 1 127 111 0 5 25ecb8cb8618 000000000000 000000000000
303 303 2 238 55 1 6 5b16163a30c6 25ecb8cb8618 000000000000
304 304
305 305 Stripping and recovering changes should work
306 306
307 307 $ hg st --change tip
308 308 M dir1/a
309 309 $ hg --config extensions.strip= strip tip
310 310 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
311 311 saved backup bundle to $TESTTMP/repo-mixed/.hg/strip-backup/51cfd7b1e13b-78a2f3ed-backup.hg (glob)
312 312 $ hg unbundle -q .hg/strip-backup/*
313 313 $ hg st --change tip
314 314 M dir1/a
315 315
316 316 Shelving and unshelving should work
317 317
318 318 $ echo foo >> dir1/a
319 319 $ hg --config extensions.shelve= shelve
320 320 shelved as default
321 321 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
322 322 $ hg --config extensions.shelve= unshelve
323 323 unshelving change 'default'
324 324 $ hg diff --nodates
325 325 diff -r 708a273da119 dir1/a
326 326 --- a/dir1/a
327 327 +++ b/dir1/a
328 328 @@ -1,1 +1,2 @@
329 329 1
330 330 +foo
331 331
332 332 Pushing from treemanifest repo to an empty repo makes that a treemanifest repo
333 333
334 334 $ cd ..
335 335 $ hg init empty-repo
336 336 $ cat << EOF >> empty-repo/.hg/hgrc
337 337 > [experimental]
338 338 > changegroup3=yes
339 339 > EOF
340 340 $ grep treemanifest empty-repo/.hg/requires
341 341 [1]
342 342 $ hg push -R repo -r 0 empty-repo
343 343 pushing to empty-repo
344 344 searching for changes
345 345 adding changesets
346 346 adding manifests
347 347 adding file changes
348 348 added 1 changesets with 2 changes to 2 files
349 349 $ grep treemanifest empty-repo/.hg/requires
350 350 treemanifest
351 351
352 352 Pushing to an empty repo works
353 353
354 354 $ hg --config experimental.treemanifest=1 init clone
355 355 $ grep treemanifest clone/.hg/requires
356 356 treemanifest
357 357 $ hg push -R repo clone
358 358 pushing to clone
359 359 searching for changes
360 360 adding changesets
361 361 adding manifests
362 362 adding file changes
363 363 added 11 changesets with 15 changes to 10 files (+3 heads)
364 364 $ grep treemanifest clone/.hg/requires
365 365 treemanifest
366 366
367 367 Create deeper repo with tree manifests.
368 368
369 369 $ hg --config experimental.treemanifest=True init deeprepo
370 370 $ cd deeprepo
371 371
372 372 $ mkdir .A
373 373 $ mkdir b
374 374 $ mkdir b/bar
375 375 $ mkdir b/bar/orange
376 376 $ mkdir b/bar/orange/fly
377 377 $ mkdir b/foo
378 378 $ mkdir b/foo/apple
379 379 $ mkdir b/foo/apple/bees
380 380
381 381 $ touch .A/one.txt
382 382 $ touch .A/two.txt
383 383 $ touch b/bar/fruits.txt
384 384 $ touch b/bar/orange/fly/gnat.py
385 385 $ touch b/bar/orange/fly/housefly.txt
386 386 $ touch b/foo/apple/bees/flower.py
387 387 $ touch c.txt
388 388 $ touch d.py
389 389
390 390 $ hg ci -Aqm 'initial'
391 391
392 392 We'll see that visitdir works by removing some treemanifest revlogs and running
393 393 the files command with various parameters.
394 394
395 395 Test files from the root.
396 396
397 397 $ hg files -r .
398 398 .A/one.txt (glob)
399 399 .A/two.txt (glob)
400 400 b/bar/fruits.txt (glob)
401 401 b/bar/orange/fly/gnat.py (glob)
402 402 b/bar/orange/fly/housefly.txt (glob)
403 403 b/foo/apple/bees/flower.py (glob)
404 404 c.txt
405 405 d.py
406 406
407 407 Excludes with a glob should not exclude everything from the glob's root
408 408
409 409 $ hg files -r . -X 'b/fo?' b
410 410 b/bar/fruits.txt (glob)
411 411 b/bar/orange/fly/gnat.py (glob)
412 412 b/bar/orange/fly/housefly.txt (glob)
413 413 $ cp -r .hg/store .hg/store-copy
414 414
415 415 Test files for a subdirectory.
416 416
417 417 $ rm -r .hg/store/meta/~2e_a
418 418 $ hg files -r . b
419 419 b/bar/fruits.txt (glob)
420 420 b/bar/orange/fly/gnat.py (glob)
421 421 b/bar/orange/fly/housefly.txt (glob)
422 422 b/foo/apple/bees/flower.py (glob)
423 423 $ cp -r .hg/store-copy/* .hg/store
424 424
425 425 Test files with just includes and excludes.
426 426
427 427 $ rm -r .hg/store/meta/~2e_a
428 428 $ rm -r .hg/store/meta/b/bar/orange/fly
429 429 $ rm -r .hg/store/meta/b/foo/apple/bees
430 430 $ hg files -r . -I path:b/bar -X path:b/bar/orange/fly -I path:b/foo -X path:b/foo/apple/bees
431 431 b/bar/fruits.txt (glob)
432 432 $ cp -r .hg/store-copy/* .hg/store
433 433
434 434 Test files for a subdirectory, excluding a directory within it.
435 435
436 436 $ rm -r .hg/store/meta/~2e_a
437 437 $ rm -r .hg/store/meta/b/foo
438 438 $ hg files -r . -X path:b/foo b
439 439 b/bar/fruits.txt (glob)
440 440 b/bar/orange/fly/gnat.py (glob)
441 441 b/bar/orange/fly/housefly.txt (glob)
442 442 $ cp -r .hg/store-copy/* .hg/store
443 443
444 444 Test files for a sub directory, including only a directory within it, and
445 445 including an unrelated directory.
446 446
447 447 $ rm -r .hg/store/meta/~2e_a
448 448 $ rm -r .hg/store/meta/b/foo
449 449 $ hg files -r . -I path:b/bar/orange -I path:a b
450 450 b/bar/orange/fly/gnat.py (glob)
451 451 b/bar/orange/fly/housefly.txt (glob)
452 452 $ cp -r .hg/store-copy/* .hg/store
453 453
454 454 Test files for a pattern, including a directory, and excluding a directory
455 455 within that.
456 456
457 457 $ rm -r .hg/store/meta/~2e_a
458 458 $ rm -r .hg/store/meta/b/foo
459 459 $ rm -r .hg/store/meta/b/bar/orange
460 460 $ hg files -r . glob:**.txt -I path:b/bar -X path:b/bar/orange
461 461 b/bar/fruits.txt (glob)
462 462 $ cp -r .hg/store-copy/* .hg/store
463 463
464 464 Add some more changes to the deep repo
465 465 $ echo narf >> b/bar/fruits.txt
466 466 $ hg ci -m narf
467 467 $ echo troz >> b/bar/orange/fly/gnat.py
468 468 $ hg ci -m troz
469 469
470 470 Verify works
471 471 $ hg verify
472 472 checking changesets
473 473 checking manifests
474 474 checking directory manifests
475 475 crosschecking files in changesets and manifests
476 476 checking files
477 477 8 files, 3 changesets, 10 total revisions
478 478
479 479 Dirlogs are included in fncache
480 480 $ grep meta/.A/00manifest.i .hg/store/fncache
481 481 meta/.A/00manifest.i
482 482
483 483 Rebuilt fncache includes dirlogs
484 484 $ rm .hg/store/fncache
485 485 $ hg debugrebuildfncache
486 486 adding data/.A/one.txt.i
487 487 adding data/.A/two.txt.i
488 488 adding data/b/bar/fruits.txt.i
489 489 adding data/b/bar/orange/fly/gnat.py.i
490 490 adding data/b/bar/orange/fly/housefly.txt.i
491 491 adding data/b/foo/apple/bees/flower.py.i
492 492 adding data/c.txt.i
493 493 adding data/d.py.i
494 494 adding meta/.A/00manifest.i
495 495 adding meta/b/00manifest.i
496 496 adding meta/b/bar/00manifest.i
497 497 adding meta/b/bar/orange/00manifest.i
498 498 adding meta/b/bar/orange/fly/00manifest.i
499 499 adding meta/b/foo/00manifest.i
500 500 adding meta/b/foo/apple/00manifest.i
501 501 adding meta/b/foo/apple/bees/00manifest.i
502 502 16 items added, 0 removed from fncache
503 503
504 504 Finish first server
505 505 $ killdaemons.py
506 506
507 507 Back up the recently added revlogs
508 508 $ cp -r .hg/store .hg/store-newcopy
509 509
510 510 Verify reports missing dirlog
511 511 $ rm .hg/store/meta/b/00manifest.*
512 512 $ hg verify
513 513 checking changesets
514 514 checking manifests
515 515 checking directory manifests
516 516 0: empty or missing b/
517 517 b/@0: parent-directory manifest refers to unknown revision 67688a370455
518 518 b/@1: parent-directory manifest refers to unknown revision f38e85d334c5
519 519 b/@2: parent-directory manifest refers to unknown revision 99c9792fd4b0
520 warning: orphan revlog 'meta/b/bar/00manifest.i'
521 warning: orphan revlog 'meta/b/bar/orange/00manifest.i'
522 warning: orphan revlog 'meta/b/bar/orange/fly/00manifest.i'
523 warning: orphan revlog 'meta/b/foo/00manifest.i'
524 warning: orphan revlog 'meta/b/foo/apple/00manifest.i'
525 warning: orphan revlog 'meta/b/foo/apple/bees/00manifest.i'
520 526 crosschecking files in changesets and manifests
521 527 b/bar/fruits.txt@0: in changeset but not in manifest
522 528 b/bar/orange/fly/gnat.py@0: in changeset but not in manifest
523 529 b/bar/orange/fly/housefly.txt@0: in changeset but not in manifest
524 530 b/foo/apple/bees/flower.py@0: in changeset but not in manifest
525 531 checking files
526 532 8 files, 3 changesets, 10 total revisions
533 6 warnings encountered!
527 534 8 integrity errors encountered!
528 535 (first damaged changeset appears to be 0)
529 536 [1]
530 537 $ cp -rT .hg/store-newcopy .hg/store
531 538
532 539 Verify reports missing dirlog entry
533 540 $ mv -f .hg/store-copy/meta/b/00manifest.* .hg/store/meta/b/
534 541 $ hg verify
535 542 checking changesets
536 543 checking manifests
537 544 checking directory manifests
538 545 b/@1: parent-directory manifest refers to unknown revision f38e85d334c5
539 546 b/@2: parent-directory manifest refers to unknown revision 99c9792fd4b0
540 547 b/bar/@?: rev 1 points to unexpected changeset 1
541 548 b/bar/@?: 5e03c4ee5e4a not in parent-directory manifest
542 549 b/bar/@?: rev 2 points to unexpected changeset 2
543 550 b/bar/@?: 1b16940d66d6 not in parent-directory manifest
544 551 b/bar/orange/@?: rev 1 points to unexpected changeset 2
545 552 (expected None)
546 553 b/bar/orange/fly/@?: rev 1 points to unexpected changeset 2
547 554 (expected None)
548 555 crosschecking files in changesets and manifests
549 556 checking files
550 557 8 files, 3 changesets, 10 total revisions
551 558 2 warnings encountered!
552 559 8 integrity errors encountered!
553 560 (first damaged changeset appears to be 1)
554 561 [1]
555 562 $ cp -rT .hg/store-newcopy .hg/store
556 563
557 564 Test cloning a treemanifest repo over http.
558 565 $ hg serve -p $HGPORT -d --pid-file=hg.pid --errorlog=errors.log
559 566 $ cat hg.pid >> $DAEMON_PIDS
560 567 $ cd ..
561 568 We can clone even with the knob turned off and we'll get a treemanifest repo.
562 569 $ hg clone --config experimental.treemanifest=False \
563 570 > --config experimental.changegroup3=True \
564 571 > http://localhost:$HGPORT deepclone
565 572 requesting all changes
566 573 adding changesets
567 574 adding manifests
568 575 adding file changes
569 576 added 3 changesets with 10 changes to 8 files
570 577 updating to branch default
571 578 8 files updated, 0 files merged, 0 files removed, 0 files unresolved
572 579 No server errors.
573 580 $ cat deeprepo/errors.log
574 581 requires got updated to include treemanifest
575 582 $ cat deepclone/.hg/requires | grep treemanifest
576 583 treemanifest
577 584 Tree manifest revlogs exist.
578 585 $ find deepclone/.hg/store/meta | sort
579 586 deepclone/.hg/store/meta
580 587 deepclone/.hg/store/meta/b
581 588 deepclone/.hg/store/meta/b/00manifest.i
582 589 deepclone/.hg/store/meta/b/bar
583 590 deepclone/.hg/store/meta/b/bar/00manifest.i
584 591 deepclone/.hg/store/meta/b/bar/orange
585 592 deepclone/.hg/store/meta/b/bar/orange/00manifest.i
586 593 deepclone/.hg/store/meta/b/bar/orange/fly
587 594 deepclone/.hg/store/meta/b/bar/orange/fly/00manifest.i
588 595 deepclone/.hg/store/meta/b/foo
589 596 deepclone/.hg/store/meta/b/foo/00manifest.i
590 597 deepclone/.hg/store/meta/b/foo/apple
591 598 deepclone/.hg/store/meta/b/foo/apple/00manifest.i
592 599 deepclone/.hg/store/meta/b/foo/apple/bees
593 600 deepclone/.hg/store/meta/b/foo/apple/bees/00manifest.i
594 601 deepclone/.hg/store/meta/~2e_a
595 602 deepclone/.hg/store/meta/~2e_a/00manifest.i
596 603 Verify passes.
597 604 $ cd deepclone
598 605 $ hg verify
599 606 checking changesets
600 607 checking manifests
601 608 checking directory manifests
602 609 crosschecking files in changesets and manifests
603 610 checking files
604 611 8 files, 3 changesets, 10 total revisions
605 612 $ cd ..
606 613
607 614 Create clones using old repo formats to use in later tests
608 615 $ hg clone --config format.usestore=False \
609 616 > --config experimental.changegroup3=True \
610 617 > http://localhost:$HGPORT deeprepo-basicstore
611 618 requesting all changes
612 619 adding changesets
613 620 adding manifests
614 621 adding file changes
615 622 added 3 changesets with 10 changes to 8 files
616 623 updating to branch default
617 624 8 files updated, 0 files merged, 0 files removed, 0 files unresolved
618 625 $ cd deeprepo-basicstore
619 626 $ grep store .hg/requires
620 627 [1]
621 628 $ hg serve -p $HGPORT1 -d --pid-file=hg.pid --errorlog=errors.log
622 629 $ cat hg.pid >> $DAEMON_PIDS
623 630 $ cd ..
624 631 $ hg clone --config format.usefncache=False \
625 632 > --config experimental.changegroup3=True \
626 633 > http://localhost:$HGPORT deeprepo-encodedstore
627 634 requesting all changes
628 635 adding changesets
629 636 adding manifests
630 637 adding file changes
631 638 added 3 changesets with 10 changes to 8 files
632 639 updating to branch default
633 640 8 files updated, 0 files merged, 0 files removed, 0 files unresolved
634 641 $ cd deeprepo-encodedstore
635 642 $ grep fncache .hg/requires
636 643 [1]
637 644 $ hg serve -p $HGPORT2 -d --pid-file=hg.pid --errorlog=errors.log
638 645 $ cat hg.pid >> $DAEMON_PIDS
639 646 $ cd ..
640 647
641 648 Local clone with basicstore
642 649 $ hg clone -U deeprepo-basicstore local-clone-basicstore
643 650 $ hg -R local-clone-basicstore verify
644 651 checking changesets
645 652 checking manifests
646 653 checking directory manifests
647 654 crosschecking files in changesets and manifests
648 655 checking files
649 656 8 files, 3 changesets, 10 total revisions
650 657
651 658 Local clone with encodedstore
652 659 $ hg clone -U deeprepo-encodedstore local-clone-encodedstore
653 660 $ hg -R local-clone-encodedstore verify
654 661 checking changesets
655 662 checking manifests
656 663 checking directory manifests
657 664 crosschecking files in changesets and manifests
658 665 checking files
659 666 8 files, 3 changesets, 10 total revisions
660 667
661 668 Local clone with fncachestore
662 669 $ hg clone -U deeprepo local-clone-fncachestore
663 670 $ hg -R local-clone-fncachestore verify
664 671 checking changesets
665 672 checking manifests
666 673 checking directory manifests
667 674 crosschecking files in changesets and manifests
668 675 checking files
669 676 8 files, 3 changesets, 10 total revisions
670 677
671 678 Stream clone with basicstore
672 679 $ hg clone --config experimental.changegroup3=True --uncompressed -U \
673 680 > http://localhost:$HGPORT1 stream-clone-basicstore
674 681 streaming all changes
675 682 18 files to transfer, * of data (glob)
676 683 transferred * in * seconds (*) (glob)
677 684 searching for changes
678 685 no changes found
679 686 $ hg -R stream-clone-basicstore verify
680 687 checking changesets
681 688 checking manifests
682 689 checking directory manifests
683 690 crosschecking files in changesets and manifests
684 691 checking files
685 692 8 files, 3 changesets, 10 total revisions
686 693
687 694 Stream clone with encodedstore
688 695 $ hg clone --config experimental.changegroup3=True --uncompressed -U \
689 696 > http://localhost:$HGPORT2 stream-clone-encodedstore
690 697 streaming all changes
691 698 18 files to transfer, * of data (glob)
692 699 transferred * in * seconds (*) (glob)
693 700 searching for changes
694 701 no changes found
695 702 $ hg -R stream-clone-encodedstore verify
696 703 checking changesets
697 704 checking manifests
698 705 checking directory manifests
699 706 crosschecking files in changesets and manifests
700 707 checking files
701 708 8 files, 3 changesets, 10 total revisions
702 709
703 710 Stream clone with fncachestore
704 711 $ hg clone --config experimental.changegroup3=True --uncompressed -U \
705 712 > http://localhost:$HGPORT stream-clone-fncachestore
706 713 streaming all changes
707 714 18 files to transfer, * of data (glob)
708 715 transferred * in * seconds (*) (glob)
709 716 searching for changes
710 717 no changes found
711 718 $ hg -R stream-clone-fncachestore verify
712 719 checking changesets
713 720 checking manifests
714 721 checking directory manifests
715 722 crosschecking files in changesets and manifests
716 723 checking files
717 724 8 files, 3 changesets, 10 total revisions
718 725
719 726 Packed bundle
720 727 $ hg -R deeprepo debugcreatestreamclonebundle repo-packed.hg
721 728 writing 3349 bytes for 18 files
722 729 bundle requirements: generaldelta, revlogv1, treemanifest
723 730 $ hg debugbundle --spec repo-packed.hg
724 731 none-packed1;requirements%3Dgeneraldelta%2Crevlogv1%2Ctreemanifest
General Comments 0
You need to be logged in to leave comments. Login now