##// END OF EJS Templates
repair: use 'rebuilding' progress topic in rebuildfncache()
av6 -
r28465:43eb31ea default
parent child Browse files
Show More
@@ -1,314 +1,314 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
13 13 from .i18n import _
14 14 from .node import short
15 15 from . import (
16 16 bundle2,
17 17 changegroup,
18 18 error,
19 19 exchange,
20 20 util,
21 21 )
22 22
23 23 def _bundle(repo, bases, heads, node, suffix, compress=True):
24 24 """create a bundle with the specified revisions as a backup"""
25 25 cgversion = changegroup.safeversion(repo)
26 26
27 27 cg = changegroup.changegroupsubset(repo, bases, heads, 'strip',
28 28 version=cgversion)
29 29 backupdir = "strip-backup"
30 30 vfs = repo.vfs
31 31 if not vfs.isdir(backupdir):
32 32 vfs.mkdir(backupdir)
33 33
34 34 # Include a hash of all the nodes in the filename for uniqueness
35 35 allcommits = repo.set('%ln::%ln', bases, heads)
36 36 allhashes = sorted(c.hex() for c in allcommits)
37 37 totalhash = util.sha1(''.join(allhashes)).hexdigest()
38 38 name = "%s/%s-%s-%s.hg" % (backupdir, short(node), totalhash[:8], suffix)
39 39
40 40 comp = None
41 41 if cgversion != '01':
42 42 bundletype = "HG20"
43 43 if compress:
44 44 comp = 'BZ'
45 45 elif compress:
46 46 bundletype = "HG10BZ"
47 47 else:
48 48 bundletype = "HG10UN"
49 49 return changegroup.writebundle(repo.ui, cg, name, bundletype, vfs,
50 50 compression=comp)
51 51
52 52 def _collectfiles(repo, striprev):
53 53 """find out the filelogs affected by the strip"""
54 54 files = set()
55 55
56 56 for x in xrange(striprev, len(repo)):
57 57 files.update(repo[x].files())
58 58
59 59 return sorted(files)
60 60
61 61 def _collectbrokencsets(repo, files, striprev):
62 62 """return the changesets which will be broken by the truncation"""
63 63 s = set()
64 64 def collectone(revlog):
65 65 _, brokenset = revlog.getstrippoint(striprev)
66 66 s.update([revlog.linkrev(r) for r in brokenset])
67 67
68 68 collectone(repo.manifest)
69 69 for fname in files:
70 70 collectone(repo.file(fname))
71 71
72 72 return s
73 73
74 74 def strip(ui, repo, nodelist, backup=True, topic='backup'):
75 75 # This function operates within a transaction of its own, but does
76 76 # not take any lock on the repo.
77 77 # Simple way to maintain backwards compatibility for this
78 78 # argument.
79 79 if backup in ['none', 'strip']:
80 80 backup = False
81 81
82 82 repo = repo.unfiltered()
83 83 repo.destroying()
84 84
85 85 cl = repo.changelog
86 86 # TODO handle undo of merge sets
87 87 if isinstance(nodelist, str):
88 88 nodelist = [nodelist]
89 89 striplist = [cl.rev(node) for node in nodelist]
90 90 striprev = min(striplist)
91 91
92 92 # Some revisions with rev > striprev may not be descendants of striprev.
93 93 # We have to find these revisions and put them in a bundle, so that
94 94 # we can restore them after the truncations.
95 95 # To create the bundle we use repo.changegroupsubset which requires
96 96 # the list of heads and bases of the set of interesting revisions.
97 97 # (head = revision in the set that has no descendant in the set;
98 98 # base = revision in the set that has no ancestor in the set)
99 99 tostrip = set(striplist)
100 100 for rev in striplist:
101 101 for desc in cl.descendants([rev]):
102 102 tostrip.add(desc)
103 103
104 104 files = _collectfiles(repo, striprev)
105 105 saverevs = _collectbrokencsets(repo, files, striprev)
106 106
107 107 # compute heads
108 108 saveheads = set(saverevs)
109 109 for r in xrange(striprev + 1, len(cl)):
110 110 if r not in tostrip:
111 111 saverevs.add(r)
112 112 saveheads.difference_update(cl.parentrevs(r))
113 113 saveheads.add(r)
114 114 saveheads = [cl.node(r) for r in saveheads]
115 115
116 116 # compute base nodes
117 117 if saverevs:
118 118 descendants = set(cl.descendants(saverevs))
119 119 saverevs.difference_update(descendants)
120 120 savebases = [cl.node(r) for r in saverevs]
121 121 stripbases = [cl.node(r) for r in tostrip]
122 122
123 123 # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)), but
124 124 # is much faster
125 125 newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip)
126 126 if newbmtarget:
127 127 newbmtarget = repo[newbmtarget.first()].node()
128 128 else:
129 129 newbmtarget = '.'
130 130
131 131 bm = repo._bookmarks
132 132 updatebm = []
133 133 for m in bm:
134 134 rev = repo[bm[m]].rev()
135 135 if rev in tostrip:
136 136 updatebm.append(m)
137 137
138 138 # create a changegroup for all the branches we need to keep
139 139 backupfile = None
140 140 vfs = repo.vfs
141 141 node = nodelist[-1]
142 142 if backup:
143 143 backupfile = _bundle(repo, stripbases, cl.heads(), node, topic)
144 144 repo.ui.status(_("saved backup bundle to %s\n") %
145 145 vfs.join(backupfile))
146 146 repo.ui.log("backupbundle", "saved backup bundle to %s\n",
147 147 vfs.join(backupfile))
148 148 if saveheads or savebases:
149 149 # do not compress partial bundle if we remove it from disk later
150 150 chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
151 151 compress=False)
152 152
153 153 mfst = repo.manifest
154 154
155 155 curtr = repo.currenttransaction()
156 156 if curtr is not None:
157 157 del curtr # avoid carrying reference to transaction for nothing
158 158 msg = _('programming error: cannot strip from inside a transaction')
159 159 raise error.Abort(msg, hint=_('contact your extension maintainer'))
160 160
161 161 try:
162 162 with repo.transaction("strip") as tr:
163 163 offset = len(tr.entries)
164 164
165 165 tr.startgroup()
166 166 cl.strip(striprev, tr)
167 167 mfst.strip(striprev, tr)
168 168 for fn in files:
169 169 repo.file(fn).strip(striprev, tr)
170 170 tr.endgroup()
171 171
172 172 for i in xrange(offset, len(tr.entries)):
173 173 file, troffset, ignore = tr.entries[i]
174 174 repo.svfs(file, 'a').truncate(troffset)
175 175 if troffset == 0:
176 176 repo.store.markremoved(file)
177 177
178 178 if saveheads or savebases:
179 179 ui.note(_("adding branch\n"))
180 180 f = vfs.open(chgrpfile, "rb")
181 181 gen = exchange.readbundle(ui, f, chgrpfile, vfs)
182 182 if not repo.ui.verbose:
183 183 # silence internal shuffling chatter
184 184 repo.ui.pushbuffer()
185 185 if isinstance(gen, bundle2.unbundle20):
186 186 with repo.transaction('strip') as tr:
187 187 tr.hookargs = {'source': 'strip',
188 188 'url': 'bundle:' + vfs.join(chgrpfile)}
189 189 bundle2.applybundle(repo, gen, tr, source='strip',
190 190 url='bundle:' + vfs.join(chgrpfile))
191 191 else:
192 192 gen.apply(repo, 'strip', 'bundle:' + vfs.join(chgrpfile), True)
193 193 if not repo.ui.verbose:
194 194 repo.ui.popbuffer()
195 195 f.close()
196 196
197 197 for m in updatebm:
198 198 bm[m] = repo[newbmtarget].node()
199 199 lock = tr = None
200 200 try:
201 201 lock = repo.lock()
202 202 tr = repo.transaction('repair')
203 203 bm.recordchange(tr)
204 204 tr.close()
205 205 finally:
206 206 tr.release()
207 207 lock.release()
208 208
209 209 # remove undo files
210 210 for undovfs, undofile in repo.undofiles():
211 211 try:
212 212 undovfs.unlink(undofile)
213 213 except OSError as e:
214 214 if e.errno != errno.ENOENT:
215 215 ui.warn(_('error removing %s: %s\n') %
216 216 (undovfs.join(undofile), str(e)))
217 217
218 218 except: # re-raises
219 219 if backupfile:
220 220 ui.warn(_("strip failed, full bundle stored in '%s'\n")
221 221 % vfs.join(backupfile))
222 222 elif saveheads:
223 223 ui.warn(_("strip failed, partial bundle stored in '%s'\n")
224 224 % vfs.join(chgrpfile))
225 225 raise
226 226 else:
227 227 if saveheads or savebases:
228 228 # Remove partial backup only if there were no exceptions
229 229 vfs.unlink(chgrpfile)
230 230
231 231 repo.destroyed()
232 232
233 233 def rebuildfncache(ui, repo):
234 234 """Rebuilds the fncache file from repo history.
235 235
236 236 Missing entries will be added. Extra entries will be removed.
237 237 """
238 238 repo = repo.unfiltered()
239 239
240 240 if 'fncache' not in repo.requirements:
241 241 ui.warn(_('(not rebuilding fncache because repository does not '
242 242 'support fncache)\n'))
243 243 return
244 244
245 245 with repo.lock():
246 246 fnc = repo.store.fncache
247 247 # Trigger load of fncache.
248 248 if 'irrelevant' in fnc:
249 249 pass
250 250
251 251 oldentries = set(fnc.entries)
252 252 newentries = set()
253 253 seenfiles = set()
254 254
255 255 repolen = len(repo)
256 256 for rev in repo:
257 ui.progress(_('changeset'), rev, total=repolen)
257 ui.progress(_('rebuilding'), rev, total=repolen)
258 258
259 259 ctx = repo[rev]
260 260 for f in ctx.files():
261 261 # This is to minimize I/O.
262 262 if f in seenfiles:
263 263 continue
264 264 seenfiles.add(f)
265 265
266 266 i = 'data/%s.i' % f
267 267 d = 'data/%s.d' % f
268 268
269 269 if repo.store._exists(i):
270 270 newentries.add(i)
271 271 if repo.store._exists(d):
272 272 newentries.add(d)
273 273
274 ui.progress(_('changeset'), None)
274 ui.progress(_('rebuilding'), None)
275 275
276 276 if 'treemanifest' in repo.requirements: # safe but unnecessary otherwise
277 277 for dir in util.dirs(seenfiles):
278 278 i = 'meta/%s/00manifest.i' % dir
279 279 d = 'meta/%s/00manifest.d' % dir
280 280
281 281 if repo.store._exists(i):
282 282 newentries.add(i)
283 283 if repo.store._exists(d):
284 284 newentries.add(d)
285 285
286 286 addcount = len(newentries - oldentries)
287 287 removecount = len(oldentries - newentries)
288 288 for p in sorted(oldentries - newentries):
289 289 ui.write(_('removing %s\n') % p)
290 290 for p in sorted(newentries - oldentries):
291 291 ui.write(_('adding %s\n') % p)
292 292
293 293 if addcount or removecount:
294 294 ui.write(_('%d items added, %d removed from fncache\n') %
295 295 (addcount, removecount))
296 296 fnc.entries = newentries
297 297 fnc._dirty = True
298 298
299 299 with repo.transaction('fncache') as tr:
300 300 fnc.write(tr)
301 301 else:
302 302 ui.write(_('fncache already up to date\n'))
303 303
304 304 def stripbmrevset(repo, mark):
305 305 """
306 306 The revset to strip when strip is called with -B mark
307 307
308 308 Needs to live here so extensions can use it and wrap it even when strip is
309 309 not enabled or not present on a box.
310 310 """
311 311 return repo.revs("ancestors(bookmark(%s)) - "
312 312 "ancestors(head() and not bookmark(%s)) - "
313 313 "ancestors(bookmark() and not bookmark(%s))",
314 314 mark, mark, mark)
General Comments 0
You need to be logged in to leave comments. Login now