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