##// END OF EJS Templates
verify: avoid spurious integrity warnings in verbose mode (issue6172)...
Matt Harbison -
r44572:e77b57e0 default
parent child Browse files
Show More
@@ -1,628 +1,628 b''
1 # verify.py - repository integrity checking for Mercurial
1 # verify.py - repository integrity checking for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import os
10 import os
11
11
12 from .i18n import _
12 from .i18n import _
13 from .node import (
13 from .node import (
14 nullid,
14 nullid,
15 short,
15 short,
16 )
16 )
17
17
18 from . import (
18 from . import (
19 error,
19 error,
20 pycompat,
20 pycompat,
21 revlog,
21 revlog,
22 util,
22 util,
23 )
23 )
24
24
25 VERIFY_DEFAULT = 0
25 VERIFY_DEFAULT = 0
26 VERIFY_FULL = 1
26 VERIFY_FULL = 1
27
27
28
28
29 def verify(repo, level=None):
29 def verify(repo, level=None):
30 with repo.lock():
30 with repo.lock():
31 v = verifier(repo, level)
31 v = verifier(repo, level)
32 return v.verify()
32 return v.verify()
33
33
34
34
35 def _normpath(f):
35 def _normpath(f):
36 # under hg < 2.4, convert didn't sanitize paths properly, so a
36 # under hg < 2.4, convert didn't sanitize paths properly, so a
37 # converted repo may contain repeated slashes
37 # converted repo may contain repeated slashes
38 while b'//' in f:
38 while b'//' in f:
39 f = f.replace(b'//', b'/')
39 f = f.replace(b'//', b'/')
40 return f
40 return f
41
41
42
42
43 class verifier(object):
43 class verifier(object):
44 def __init__(self, repo, level=None):
44 def __init__(self, repo, level=None):
45 self.repo = repo.unfiltered()
45 self.repo = repo.unfiltered()
46 self.ui = repo.ui
46 self.ui = repo.ui
47 self.match = repo.narrowmatch()
47 self.match = repo.narrowmatch()
48 if level is None:
48 if level is None:
49 level = VERIFY_DEFAULT
49 level = VERIFY_DEFAULT
50 self._level = level
50 self._level = level
51 self.badrevs = set()
51 self.badrevs = set()
52 self.errors = 0
52 self.errors = 0
53 self.warnings = 0
53 self.warnings = 0
54 self.havecl = len(repo.changelog) > 0
54 self.havecl = len(repo.changelog) > 0
55 self.havemf = len(repo.manifestlog.getstorage(b'')) > 0
55 self.havemf = len(repo.manifestlog.getstorage(b'')) > 0
56 self.revlogv1 = repo.changelog.version != revlog.REVLOGV0
56 self.revlogv1 = repo.changelog.version != revlog.REVLOGV0
57 self.lrugetctx = util.lrucachefunc(repo.__getitem__)
57 self.lrugetctx = util.lrucachefunc(repo.unfiltered().__getitem__)
58 self.refersmf = False
58 self.refersmf = False
59 self.fncachewarned = False
59 self.fncachewarned = False
60 # developer config: verify.skipflags
60 # developer config: verify.skipflags
61 self.skipflags = repo.ui.configint(b'verify', b'skipflags')
61 self.skipflags = repo.ui.configint(b'verify', b'skipflags')
62 self.warnorphanstorefiles = True
62 self.warnorphanstorefiles = True
63
63
64 def _warn(self, msg):
64 def _warn(self, msg):
65 """record a "warning" level issue"""
65 """record a "warning" level issue"""
66 self.ui.warn(msg + b"\n")
66 self.ui.warn(msg + b"\n")
67 self.warnings += 1
67 self.warnings += 1
68
68
69 def _err(self, linkrev, msg, filename=None):
69 def _err(self, linkrev, msg, filename=None):
70 """record a "error" level issue"""
70 """record a "error" level issue"""
71 if linkrev is not None:
71 if linkrev is not None:
72 self.badrevs.add(linkrev)
72 self.badrevs.add(linkrev)
73 linkrev = b"%d" % linkrev
73 linkrev = b"%d" % linkrev
74 else:
74 else:
75 linkrev = b'?'
75 linkrev = b'?'
76 msg = b"%s: %s" % (linkrev, msg)
76 msg = b"%s: %s" % (linkrev, msg)
77 if filename:
77 if filename:
78 msg = b"%s@%s" % (filename, msg)
78 msg = b"%s@%s" % (filename, msg)
79 self.ui.warn(b" " + msg + b"\n")
79 self.ui.warn(b" " + msg + b"\n")
80 self.errors += 1
80 self.errors += 1
81
81
82 def _exc(self, linkrev, msg, inst, filename=None):
82 def _exc(self, linkrev, msg, inst, filename=None):
83 """record exception raised during the verify process"""
83 """record exception raised during the verify process"""
84 fmsg = pycompat.bytestr(inst)
84 fmsg = pycompat.bytestr(inst)
85 if not fmsg:
85 if not fmsg:
86 fmsg = pycompat.byterepr(inst)
86 fmsg = pycompat.byterepr(inst)
87 self._err(linkrev, b"%s: %s" % (msg, fmsg), filename)
87 self._err(linkrev, b"%s: %s" % (msg, fmsg), filename)
88
88
89 def _checkrevlog(self, obj, name, linkrev):
89 def _checkrevlog(self, obj, name, linkrev):
90 """verify high level property of a revlog
90 """verify high level property of a revlog
91
91
92 - revlog is present,
92 - revlog is present,
93 - revlog is non-empty,
93 - revlog is non-empty,
94 - sizes (index and data) are correct,
94 - sizes (index and data) are correct,
95 - revlog's format version is correct.
95 - revlog's format version is correct.
96 """
96 """
97 if not len(obj) and (self.havecl or self.havemf):
97 if not len(obj) and (self.havecl or self.havemf):
98 self._err(linkrev, _(b"empty or missing %s") % name)
98 self._err(linkrev, _(b"empty or missing %s") % name)
99 return
99 return
100
100
101 d = obj.checksize()
101 d = obj.checksize()
102 if d[0]:
102 if d[0]:
103 self._err(None, _(b"data length off by %d bytes") % d[0], name)
103 self._err(None, _(b"data length off by %d bytes") % d[0], name)
104 if d[1]:
104 if d[1]:
105 self._err(None, _(b"index contains %d extra bytes") % d[1], name)
105 self._err(None, _(b"index contains %d extra bytes") % d[1], name)
106
106
107 if obj.version != revlog.REVLOGV0:
107 if obj.version != revlog.REVLOGV0:
108 if not self.revlogv1:
108 if not self.revlogv1:
109 self._warn(_(b"warning: `%s' uses revlog format 1") % name)
109 self._warn(_(b"warning: `%s' uses revlog format 1") % name)
110 elif self.revlogv1:
110 elif self.revlogv1:
111 self._warn(_(b"warning: `%s' uses revlog format 0") % name)
111 self._warn(_(b"warning: `%s' uses revlog format 0") % name)
112
112
113 def _checkentry(self, obj, i, node, seen, linkrevs, f):
113 def _checkentry(self, obj, i, node, seen, linkrevs, f):
114 """verify a single revlog entry
114 """verify a single revlog entry
115
115
116 arguments are:
116 arguments are:
117 - obj: the source revlog
117 - obj: the source revlog
118 - i: the revision number
118 - i: the revision number
119 - node: the revision node id
119 - node: the revision node id
120 - seen: nodes previously seen for this revlog
120 - seen: nodes previously seen for this revlog
121 - linkrevs: [changelog-revisions] introducing "node"
121 - linkrevs: [changelog-revisions] introducing "node"
122 - f: string label ("changelog", "manifest", or filename)
122 - f: string label ("changelog", "manifest", or filename)
123
123
124 Performs the following checks:
124 Performs the following checks:
125 - linkrev points to an existing changelog revision,
125 - linkrev points to an existing changelog revision,
126 - linkrev points to a changelog revision that introduces this revision,
126 - linkrev points to a changelog revision that introduces this revision,
127 - linkrev points to the lowest of these changesets,
127 - linkrev points to the lowest of these changesets,
128 - both parents exist in the revlog,
128 - both parents exist in the revlog,
129 - the revision is not duplicated.
129 - the revision is not duplicated.
130
130
131 Return the linkrev of the revision (or None for changelog's revisions).
131 Return the linkrev of the revision (or None for changelog's revisions).
132 """
132 """
133 lr = obj.linkrev(obj.rev(node))
133 lr = obj.linkrev(obj.rev(node))
134 if lr < 0 or (self.havecl and lr not in linkrevs):
134 if lr < 0 or (self.havecl and lr not in linkrevs):
135 if lr < 0 or lr >= len(self.repo.changelog):
135 if lr < 0 or lr >= len(self.repo.changelog):
136 msg = _(b"rev %d points to nonexistent changeset %d")
136 msg = _(b"rev %d points to nonexistent changeset %d")
137 else:
137 else:
138 msg = _(b"rev %d points to unexpected changeset %d")
138 msg = _(b"rev %d points to unexpected changeset %d")
139 self._err(None, msg % (i, lr), f)
139 self._err(None, msg % (i, lr), f)
140 if linkrevs:
140 if linkrevs:
141 if f and len(linkrevs) > 1:
141 if f and len(linkrevs) > 1:
142 try:
142 try:
143 # attempt to filter down to real linkrevs
143 # attempt to filter down to real linkrevs
144 linkrevs = [
144 linkrevs = [
145 l
145 l
146 for l in linkrevs
146 for l in linkrevs
147 if self.lrugetctx(l)[f].filenode() == node
147 if self.lrugetctx(l)[f].filenode() == node
148 ]
148 ]
149 except Exception:
149 except Exception:
150 pass
150 pass
151 self._warn(
151 self._warn(
152 _(b" (expected %s)")
152 _(b" (expected %s)")
153 % b" ".join(map(pycompat.bytestr, linkrevs))
153 % b" ".join(map(pycompat.bytestr, linkrevs))
154 )
154 )
155 lr = None # can't be trusted
155 lr = None # can't be trusted
156
156
157 try:
157 try:
158 p1, p2 = obj.parents(node)
158 p1, p2 = obj.parents(node)
159 if p1 not in seen and p1 != nullid:
159 if p1 not in seen and p1 != nullid:
160 self._err(
160 self._err(
161 lr,
161 lr,
162 _(b"unknown parent 1 %s of %s") % (short(p1), short(node)),
162 _(b"unknown parent 1 %s of %s") % (short(p1), short(node)),
163 f,
163 f,
164 )
164 )
165 if p2 not in seen and p2 != nullid:
165 if p2 not in seen and p2 != nullid:
166 self._err(
166 self._err(
167 lr,
167 lr,
168 _(b"unknown parent 2 %s of %s") % (short(p2), short(node)),
168 _(b"unknown parent 2 %s of %s") % (short(p2), short(node)),
169 f,
169 f,
170 )
170 )
171 except Exception as inst:
171 except Exception as inst:
172 self._exc(lr, _(b"checking parents of %s") % short(node), inst, f)
172 self._exc(lr, _(b"checking parents of %s") % short(node), inst, f)
173
173
174 if node in seen:
174 if node in seen:
175 self._err(lr, _(b"duplicate revision %d (%d)") % (i, seen[node]), f)
175 self._err(lr, _(b"duplicate revision %d (%d)") % (i, seen[node]), f)
176 seen[node] = i
176 seen[node] = i
177 return lr
177 return lr
178
178
179 def verify(self):
179 def verify(self):
180 """verify the content of the Mercurial repository
180 """verify the content of the Mercurial repository
181
181
182 This method run all verifications, displaying issues as they are found.
182 This method run all verifications, displaying issues as they are found.
183
183
184 return 1 if any error have been encountered, 0 otherwise."""
184 return 1 if any error have been encountered, 0 otherwise."""
185 # initial validation and generic report
185 # initial validation and generic report
186 repo = self.repo
186 repo = self.repo
187 ui = repo.ui
187 ui = repo.ui
188 if not repo.url().startswith(b'file:'):
188 if not repo.url().startswith(b'file:'):
189 raise error.Abort(_(b"cannot verify bundle or remote repos"))
189 raise error.Abort(_(b"cannot verify bundle or remote repos"))
190
190
191 if os.path.exists(repo.sjoin(b"journal")):
191 if os.path.exists(repo.sjoin(b"journal")):
192 ui.warn(_(b"abandoned transaction found - run hg recover\n"))
192 ui.warn(_(b"abandoned transaction found - run hg recover\n"))
193
193
194 if ui.verbose or not self.revlogv1:
194 if ui.verbose or not self.revlogv1:
195 ui.status(
195 ui.status(
196 _(b"repository uses revlog format %d\n")
196 _(b"repository uses revlog format %d\n")
197 % (self.revlogv1 and 1 or 0)
197 % (self.revlogv1 and 1 or 0)
198 )
198 )
199
199
200 # data verification
200 # data verification
201 mflinkrevs, filelinkrevs = self._verifychangelog()
201 mflinkrevs, filelinkrevs = self._verifychangelog()
202 filenodes = self._verifymanifest(mflinkrevs)
202 filenodes = self._verifymanifest(mflinkrevs)
203 del mflinkrevs
203 del mflinkrevs
204 self._crosscheckfiles(filelinkrevs, filenodes)
204 self._crosscheckfiles(filelinkrevs, filenodes)
205 totalfiles, filerevisions = self._verifyfiles(filenodes, filelinkrevs)
205 totalfiles, filerevisions = self._verifyfiles(filenodes, filelinkrevs)
206
206
207 # final report
207 # final report
208 ui.status(
208 ui.status(
209 _(b"checked %d changesets with %d changes to %d files\n")
209 _(b"checked %d changesets with %d changes to %d files\n")
210 % (len(repo.changelog), filerevisions, totalfiles)
210 % (len(repo.changelog), filerevisions, totalfiles)
211 )
211 )
212 if self.warnings:
212 if self.warnings:
213 ui.warn(_(b"%d warnings encountered!\n") % self.warnings)
213 ui.warn(_(b"%d warnings encountered!\n") % self.warnings)
214 if self.fncachewarned:
214 if self.fncachewarned:
215 ui.warn(
215 ui.warn(
216 _(
216 _(
217 b'hint: run "hg debugrebuildfncache" to recover from '
217 b'hint: run "hg debugrebuildfncache" to recover from '
218 b'corrupt fncache\n'
218 b'corrupt fncache\n'
219 )
219 )
220 )
220 )
221 if self.errors:
221 if self.errors:
222 ui.warn(_(b"%d integrity errors encountered!\n") % self.errors)
222 ui.warn(_(b"%d integrity errors encountered!\n") % self.errors)
223 if self.badrevs:
223 if self.badrevs:
224 ui.warn(
224 ui.warn(
225 _(b"(first damaged changeset appears to be %d)\n")
225 _(b"(first damaged changeset appears to be %d)\n")
226 % min(self.badrevs)
226 % min(self.badrevs)
227 )
227 )
228 return 1
228 return 1
229 return 0
229 return 0
230
230
231 def _verifychangelog(self):
231 def _verifychangelog(self):
232 """verify the changelog of a repository
232 """verify the changelog of a repository
233
233
234 The following checks are performed:
234 The following checks are performed:
235 - all of `_checkrevlog` checks,
235 - all of `_checkrevlog` checks,
236 - all of `_checkentry` checks (for each revisions),
236 - all of `_checkentry` checks (for each revisions),
237 - each revision can be read.
237 - each revision can be read.
238
238
239 The function returns some of the data observed in the changesets as a
239 The function returns some of the data observed in the changesets as a
240 (mflinkrevs, filelinkrevs) tuples:
240 (mflinkrevs, filelinkrevs) tuples:
241 - mflinkrevs: is a { manifest-node -> [changelog-rev] } mapping
241 - mflinkrevs: is a { manifest-node -> [changelog-rev] } mapping
242 - filelinkrevs: is a { file-path -> [changelog-rev] } mapping
242 - filelinkrevs: is a { file-path -> [changelog-rev] } mapping
243
243
244 If a matcher was specified, filelinkrevs will only contains matched
244 If a matcher was specified, filelinkrevs will only contains matched
245 files.
245 files.
246 """
246 """
247 ui = self.ui
247 ui = self.ui
248 repo = self.repo
248 repo = self.repo
249 match = self.match
249 match = self.match
250 cl = repo.changelog
250 cl = repo.changelog
251
251
252 ui.status(_(b"checking changesets\n"))
252 ui.status(_(b"checking changesets\n"))
253 mflinkrevs = {}
253 mflinkrevs = {}
254 filelinkrevs = {}
254 filelinkrevs = {}
255 seen = {}
255 seen = {}
256 self._checkrevlog(cl, b"changelog", 0)
256 self._checkrevlog(cl, b"changelog", 0)
257 progress = ui.makeprogress(
257 progress = ui.makeprogress(
258 _(b'checking'), unit=_(b'changesets'), total=len(repo)
258 _(b'checking'), unit=_(b'changesets'), total=len(repo)
259 )
259 )
260 for i in repo:
260 for i in repo:
261 progress.update(i)
261 progress.update(i)
262 n = cl.node(i)
262 n = cl.node(i)
263 self._checkentry(cl, i, n, seen, [i], b"changelog")
263 self._checkentry(cl, i, n, seen, [i], b"changelog")
264
264
265 try:
265 try:
266 changes = cl.read(n)
266 changes = cl.read(n)
267 if changes[0] != nullid:
267 if changes[0] != nullid:
268 mflinkrevs.setdefault(changes[0], []).append(i)
268 mflinkrevs.setdefault(changes[0], []).append(i)
269 self.refersmf = True
269 self.refersmf = True
270 for f in changes[3]:
270 for f in changes[3]:
271 if match(f):
271 if match(f):
272 filelinkrevs.setdefault(_normpath(f), []).append(i)
272 filelinkrevs.setdefault(_normpath(f), []).append(i)
273 except Exception as inst:
273 except Exception as inst:
274 self.refersmf = True
274 self.refersmf = True
275 self._exc(i, _(b"unpacking changeset %s") % short(n), inst)
275 self._exc(i, _(b"unpacking changeset %s") % short(n), inst)
276 progress.complete()
276 progress.complete()
277 return mflinkrevs, filelinkrevs
277 return mflinkrevs, filelinkrevs
278
278
279 def _verifymanifest(
279 def _verifymanifest(
280 self, mflinkrevs, dir=b"", storefiles=None, subdirprogress=None
280 self, mflinkrevs, dir=b"", storefiles=None, subdirprogress=None
281 ):
281 ):
282 """verify the manifestlog content
282 """verify the manifestlog content
283
283
284 Inputs:
284 Inputs:
285 - mflinkrevs: a {manifest-node -> [changelog-revisions]} mapping
285 - mflinkrevs: a {manifest-node -> [changelog-revisions]} mapping
286 - dir: a subdirectory to check (for tree manifest repo)
286 - dir: a subdirectory to check (for tree manifest repo)
287 - storefiles: set of currently "orphan" files.
287 - storefiles: set of currently "orphan" files.
288 - subdirprogress: a progress object
288 - subdirprogress: a progress object
289
289
290 This function checks:
290 This function checks:
291 * all of `_checkrevlog` checks (for all manifest related revlogs)
291 * all of `_checkrevlog` checks (for all manifest related revlogs)
292 * all of `_checkentry` checks (for all manifest related revisions)
292 * all of `_checkentry` checks (for all manifest related revisions)
293 * nodes for subdirectory exists in the sub-directory manifest
293 * nodes for subdirectory exists in the sub-directory manifest
294 * each manifest entries have a file path
294 * each manifest entries have a file path
295 * each manifest node refered in mflinkrevs exist in the manifest log
295 * each manifest node refered in mflinkrevs exist in the manifest log
296
296
297 If tree manifest is in use and a matchers is specified, only the
297 If tree manifest is in use and a matchers is specified, only the
298 sub-directories matching it will be verified.
298 sub-directories matching it will be verified.
299
299
300 return a two level mapping:
300 return a two level mapping:
301 {"path" -> { filenode -> changelog-revision}}
301 {"path" -> { filenode -> changelog-revision}}
302
302
303 This mapping primarily contains entries for every files in the
303 This mapping primarily contains entries for every files in the
304 repository. In addition, when tree-manifest is used, it also contains
304 repository. In addition, when tree-manifest is used, it also contains
305 sub-directory entries.
305 sub-directory entries.
306
306
307 If a matcher is provided, only matching paths will be included.
307 If a matcher is provided, only matching paths will be included.
308 """
308 """
309 repo = self.repo
309 repo = self.repo
310 ui = self.ui
310 ui = self.ui
311 match = self.match
311 match = self.match
312 mfl = self.repo.manifestlog
312 mfl = self.repo.manifestlog
313 mf = mfl.getstorage(dir)
313 mf = mfl.getstorage(dir)
314
314
315 if not dir:
315 if not dir:
316 self.ui.status(_(b"checking manifests\n"))
316 self.ui.status(_(b"checking manifests\n"))
317
317
318 filenodes = {}
318 filenodes = {}
319 subdirnodes = {}
319 subdirnodes = {}
320 seen = {}
320 seen = {}
321 label = b"manifest"
321 label = b"manifest"
322 if dir:
322 if dir:
323 label = dir
323 label = dir
324 revlogfiles = mf.files()
324 revlogfiles = mf.files()
325 storefiles.difference_update(revlogfiles)
325 storefiles.difference_update(revlogfiles)
326 if subdirprogress: # should be true since we're in a subdirectory
326 if subdirprogress: # should be true since we're in a subdirectory
327 subdirprogress.increment()
327 subdirprogress.increment()
328 if self.refersmf:
328 if self.refersmf:
329 # Do not check manifest if there are only changelog entries with
329 # Do not check manifest if there are only changelog entries with
330 # null manifests.
330 # null manifests.
331 self._checkrevlog(mf, label, 0)
331 self._checkrevlog(mf, label, 0)
332 progress = ui.makeprogress(
332 progress = ui.makeprogress(
333 _(b'checking'), unit=_(b'manifests'), total=len(mf)
333 _(b'checking'), unit=_(b'manifests'), total=len(mf)
334 )
334 )
335 for i in mf:
335 for i in mf:
336 if not dir:
336 if not dir:
337 progress.update(i)
337 progress.update(i)
338 n = mf.node(i)
338 n = mf.node(i)
339 lr = self._checkentry(mf, i, n, seen, mflinkrevs.get(n, []), label)
339 lr = self._checkentry(mf, i, n, seen, mflinkrevs.get(n, []), label)
340 if n in mflinkrevs:
340 if n in mflinkrevs:
341 del mflinkrevs[n]
341 del mflinkrevs[n]
342 elif dir:
342 elif dir:
343 self._err(
343 self._err(
344 lr,
344 lr,
345 _(b"%s not in parent-directory manifest") % short(n),
345 _(b"%s not in parent-directory manifest") % short(n),
346 label,
346 label,
347 )
347 )
348 else:
348 else:
349 self._err(lr, _(b"%s not in changesets") % short(n), label)
349 self._err(lr, _(b"%s not in changesets") % short(n), label)
350
350
351 try:
351 try:
352 mfdelta = mfl.get(dir, n).readdelta(shallow=True)
352 mfdelta = mfl.get(dir, n).readdelta(shallow=True)
353 for f, fn, fl in mfdelta.iterentries():
353 for f, fn, fl in mfdelta.iterentries():
354 if not f:
354 if not f:
355 self._err(lr, _(b"entry without name in manifest"))
355 self._err(lr, _(b"entry without name in manifest"))
356 elif f == b"/dev/null": # ignore this in very old repos
356 elif f == b"/dev/null": # ignore this in very old repos
357 continue
357 continue
358 fullpath = dir + _normpath(f)
358 fullpath = dir + _normpath(f)
359 if fl == b't':
359 if fl == b't':
360 if not match.visitdir(fullpath):
360 if not match.visitdir(fullpath):
361 continue
361 continue
362 subdirnodes.setdefault(fullpath + b'/', {}).setdefault(
362 subdirnodes.setdefault(fullpath + b'/', {}).setdefault(
363 fn, []
363 fn, []
364 ).append(lr)
364 ).append(lr)
365 else:
365 else:
366 if not match(fullpath):
366 if not match(fullpath):
367 continue
367 continue
368 filenodes.setdefault(fullpath, {}).setdefault(fn, lr)
368 filenodes.setdefault(fullpath, {}).setdefault(fn, lr)
369 except Exception as inst:
369 except Exception as inst:
370 self._exc(lr, _(b"reading delta %s") % short(n), inst, label)
370 self._exc(lr, _(b"reading delta %s") % short(n), inst, label)
371 if self._level >= VERIFY_FULL:
371 if self._level >= VERIFY_FULL:
372 try:
372 try:
373 # Various issues can affect manifest. So we read each full
373 # Various issues can affect manifest. So we read each full
374 # text from storage. This triggers the checks from the core
374 # text from storage. This triggers the checks from the core
375 # code (eg: hash verification, filename are ordered, etc.)
375 # code (eg: hash verification, filename are ordered, etc.)
376 mfdelta = mfl.get(dir, n).read()
376 mfdelta = mfl.get(dir, n).read()
377 except Exception as inst:
377 except Exception as inst:
378 self._exc(
378 self._exc(
379 lr,
379 lr,
380 _(b"reading full manifest %s") % short(n),
380 _(b"reading full manifest %s") % short(n),
381 inst,
381 inst,
382 label,
382 label,
383 )
383 )
384
384
385 if not dir:
385 if not dir:
386 progress.complete()
386 progress.complete()
387
387
388 if self.havemf:
388 if self.havemf:
389 # since we delete entry in `mflinkrevs` during iteration, any
389 # since we delete entry in `mflinkrevs` during iteration, any
390 # remaining entries are "missing". We need to issue errors for them.
390 # remaining entries are "missing". We need to issue errors for them.
391 changesetpairs = [(c, m) for m in mflinkrevs for c in mflinkrevs[m]]
391 changesetpairs = [(c, m) for m in mflinkrevs for c in mflinkrevs[m]]
392 for c, m in sorted(changesetpairs):
392 for c, m in sorted(changesetpairs):
393 if dir:
393 if dir:
394 self._err(
394 self._err(
395 c,
395 c,
396 _(
396 _(
397 b"parent-directory manifest refers to unknown"
397 b"parent-directory manifest refers to unknown"
398 b" revision %s"
398 b" revision %s"
399 )
399 )
400 % short(m),
400 % short(m),
401 label,
401 label,
402 )
402 )
403 else:
403 else:
404 self._err(
404 self._err(
405 c,
405 c,
406 _(b"changeset refers to unknown revision %s")
406 _(b"changeset refers to unknown revision %s")
407 % short(m),
407 % short(m),
408 label,
408 label,
409 )
409 )
410
410
411 if not dir and subdirnodes:
411 if not dir and subdirnodes:
412 self.ui.status(_(b"checking directory manifests\n"))
412 self.ui.status(_(b"checking directory manifests\n"))
413 storefiles = set()
413 storefiles = set()
414 subdirs = set()
414 subdirs = set()
415 revlogv1 = self.revlogv1
415 revlogv1 = self.revlogv1
416 for f, f2, size in repo.store.datafiles():
416 for f, f2, size in repo.store.datafiles():
417 if not f:
417 if not f:
418 self._err(None, _(b"cannot decode filename '%s'") % f2)
418 self._err(None, _(b"cannot decode filename '%s'") % f2)
419 elif (size > 0 or not revlogv1) and f.startswith(b'meta/'):
419 elif (size > 0 or not revlogv1) and f.startswith(b'meta/'):
420 storefiles.add(_normpath(f))
420 storefiles.add(_normpath(f))
421 subdirs.add(os.path.dirname(f))
421 subdirs.add(os.path.dirname(f))
422 subdirprogress = ui.makeprogress(
422 subdirprogress = ui.makeprogress(
423 _(b'checking'), unit=_(b'manifests'), total=len(subdirs)
423 _(b'checking'), unit=_(b'manifests'), total=len(subdirs)
424 )
424 )
425
425
426 for subdir, linkrevs in pycompat.iteritems(subdirnodes):
426 for subdir, linkrevs in pycompat.iteritems(subdirnodes):
427 subdirfilenodes = self._verifymanifest(
427 subdirfilenodes = self._verifymanifest(
428 linkrevs, subdir, storefiles, subdirprogress
428 linkrevs, subdir, storefiles, subdirprogress
429 )
429 )
430 for f, onefilenodes in pycompat.iteritems(subdirfilenodes):
430 for f, onefilenodes in pycompat.iteritems(subdirfilenodes):
431 filenodes.setdefault(f, {}).update(onefilenodes)
431 filenodes.setdefault(f, {}).update(onefilenodes)
432
432
433 if not dir and subdirnodes:
433 if not dir and subdirnodes:
434 subdirprogress.complete()
434 subdirprogress.complete()
435 if self.warnorphanstorefiles:
435 if self.warnorphanstorefiles:
436 for f in sorted(storefiles):
436 for f in sorted(storefiles):
437 self._warn(_(b"warning: orphan data file '%s'") % f)
437 self._warn(_(b"warning: orphan data file '%s'") % f)
438
438
439 return filenodes
439 return filenodes
440
440
441 def _crosscheckfiles(self, filelinkrevs, filenodes):
441 def _crosscheckfiles(self, filelinkrevs, filenodes):
442 repo = self.repo
442 repo = self.repo
443 ui = self.ui
443 ui = self.ui
444 ui.status(_(b"crosschecking files in changesets and manifests\n"))
444 ui.status(_(b"crosschecking files in changesets and manifests\n"))
445
445
446 total = len(filelinkrevs) + len(filenodes)
446 total = len(filelinkrevs) + len(filenodes)
447 progress = ui.makeprogress(
447 progress = ui.makeprogress(
448 _(b'crosschecking'), unit=_(b'files'), total=total
448 _(b'crosschecking'), unit=_(b'files'), total=total
449 )
449 )
450 if self.havemf:
450 if self.havemf:
451 for f in sorted(filelinkrevs):
451 for f in sorted(filelinkrevs):
452 progress.increment()
452 progress.increment()
453 if f not in filenodes:
453 if f not in filenodes:
454 lr = filelinkrevs[f][0]
454 lr = filelinkrevs[f][0]
455 self._err(lr, _(b"in changeset but not in manifest"), f)
455 self._err(lr, _(b"in changeset but not in manifest"), f)
456
456
457 if self.havecl:
457 if self.havecl:
458 for f in sorted(filenodes):
458 for f in sorted(filenodes):
459 progress.increment()
459 progress.increment()
460 if f not in filelinkrevs:
460 if f not in filelinkrevs:
461 try:
461 try:
462 fl = repo.file(f)
462 fl = repo.file(f)
463 lr = min([fl.linkrev(fl.rev(n)) for n in filenodes[f]])
463 lr = min([fl.linkrev(fl.rev(n)) for n in filenodes[f]])
464 except Exception:
464 except Exception:
465 lr = None
465 lr = None
466 self._err(lr, _(b"in manifest but not in changeset"), f)
466 self._err(lr, _(b"in manifest but not in changeset"), f)
467
467
468 progress.complete()
468 progress.complete()
469
469
470 def _verifyfiles(self, filenodes, filelinkrevs):
470 def _verifyfiles(self, filenodes, filelinkrevs):
471 repo = self.repo
471 repo = self.repo
472 ui = self.ui
472 ui = self.ui
473 lrugetctx = self.lrugetctx
473 lrugetctx = self.lrugetctx
474 revlogv1 = self.revlogv1
474 revlogv1 = self.revlogv1
475 havemf = self.havemf
475 havemf = self.havemf
476 ui.status(_(b"checking files\n"))
476 ui.status(_(b"checking files\n"))
477
477
478 storefiles = set()
478 storefiles = set()
479 for f, f2, size in repo.store.datafiles():
479 for f, f2, size in repo.store.datafiles():
480 if not f:
480 if not f:
481 self._err(None, _(b"cannot decode filename '%s'") % f2)
481 self._err(None, _(b"cannot decode filename '%s'") % f2)
482 elif (size > 0 or not revlogv1) and f.startswith(b'data/'):
482 elif (size > 0 or not revlogv1) and f.startswith(b'data/'):
483 storefiles.add(_normpath(f))
483 storefiles.add(_normpath(f))
484
484
485 state = {
485 state = {
486 # TODO this assumes revlog storage for changelog.
486 # TODO this assumes revlog storage for changelog.
487 b'expectedversion': self.repo.changelog.version & 0xFFFF,
487 b'expectedversion': self.repo.changelog.version & 0xFFFF,
488 b'skipflags': self.skipflags,
488 b'skipflags': self.skipflags,
489 # experimental config: censor.policy
489 # experimental config: censor.policy
490 b'erroroncensored': ui.config(b'censor', b'policy') == b'abort',
490 b'erroroncensored': ui.config(b'censor', b'policy') == b'abort',
491 }
491 }
492
492
493 files = sorted(set(filenodes) | set(filelinkrevs))
493 files = sorted(set(filenodes) | set(filelinkrevs))
494 revisions = 0
494 revisions = 0
495 progress = ui.makeprogress(
495 progress = ui.makeprogress(
496 _(b'checking'), unit=_(b'files'), total=len(files)
496 _(b'checking'), unit=_(b'files'), total=len(files)
497 )
497 )
498 for i, f in enumerate(files):
498 for i, f in enumerate(files):
499 progress.update(i, item=f)
499 progress.update(i, item=f)
500 try:
500 try:
501 linkrevs = filelinkrevs[f]
501 linkrevs = filelinkrevs[f]
502 except KeyError:
502 except KeyError:
503 # in manifest but not in changelog
503 # in manifest but not in changelog
504 linkrevs = []
504 linkrevs = []
505
505
506 if linkrevs:
506 if linkrevs:
507 lr = linkrevs[0]
507 lr = linkrevs[0]
508 else:
508 else:
509 lr = None
509 lr = None
510
510
511 try:
511 try:
512 fl = repo.file(f)
512 fl = repo.file(f)
513 except error.StorageError as e:
513 except error.StorageError as e:
514 self._err(lr, _(b"broken revlog! (%s)") % e, f)
514 self._err(lr, _(b"broken revlog! (%s)") % e, f)
515 continue
515 continue
516
516
517 for ff in fl.files():
517 for ff in fl.files():
518 try:
518 try:
519 storefiles.remove(ff)
519 storefiles.remove(ff)
520 except KeyError:
520 except KeyError:
521 if self.warnorphanstorefiles:
521 if self.warnorphanstorefiles:
522 self._warn(
522 self._warn(
523 _(b" warning: revlog '%s' not in fncache!") % ff
523 _(b" warning: revlog '%s' not in fncache!") % ff
524 )
524 )
525 self.fncachewarned = True
525 self.fncachewarned = True
526
526
527 if not len(fl) and (self.havecl or self.havemf):
527 if not len(fl) and (self.havecl or self.havemf):
528 self._err(lr, _(b"empty or missing %s") % f)
528 self._err(lr, _(b"empty or missing %s") % f)
529 else:
529 else:
530 # Guard against implementations not setting this.
530 # Guard against implementations not setting this.
531 state[b'skipread'] = set()
531 state[b'skipread'] = set()
532 state[b'safe_renamed'] = set()
532 state[b'safe_renamed'] = set()
533
533
534 for problem in fl.verifyintegrity(state):
534 for problem in fl.verifyintegrity(state):
535 if problem.node is not None:
535 if problem.node is not None:
536 linkrev = fl.linkrev(fl.rev(problem.node))
536 linkrev = fl.linkrev(fl.rev(problem.node))
537 else:
537 else:
538 linkrev = None
538 linkrev = None
539
539
540 if problem.warning:
540 if problem.warning:
541 self._warn(problem.warning)
541 self._warn(problem.warning)
542 elif problem.error:
542 elif problem.error:
543 self._err(
543 self._err(
544 linkrev if linkrev is not None else lr,
544 linkrev if linkrev is not None else lr,
545 problem.error,
545 problem.error,
546 f,
546 f,
547 )
547 )
548 else:
548 else:
549 raise error.ProgrammingError(
549 raise error.ProgrammingError(
550 b'problem instance does not set warning or error '
550 b'problem instance does not set warning or error '
551 b'attribute: %s' % problem.msg
551 b'attribute: %s' % problem.msg
552 )
552 )
553
553
554 seen = {}
554 seen = {}
555 for i in fl:
555 for i in fl:
556 revisions += 1
556 revisions += 1
557 n = fl.node(i)
557 n = fl.node(i)
558 lr = self._checkentry(fl, i, n, seen, linkrevs, f)
558 lr = self._checkentry(fl, i, n, seen, linkrevs, f)
559 if f in filenodes:
559 if f in filenodes:
560 if havemf and n not in filenodes[f]:
560 if havemf and n not in filenodes[f]:
561 self._err(lr, _(b"%s not in manifests") % (short(n)), f)
561 self._err(lr, _(b"%s not in manifests") % (short(n)), f)
562 else:
562 else:
563 del filenodes[f][n]
563 del filenodes[f][n]
564
564
565 if n in state[b'skipread'] and n not in state[b'safe_renamed']:
565 if n in state[b'skipread'] and n not in state[b'safe_renamed']:
566 continue
566 continue
567
567
568 # check renames
568 # check renames
569 try:
569 try:
570 # This requires resolving fulltext (at least on revlogs,
570 # This requires resolving fulltext (at least on revlogs,
571 # though not with LFS revisions). We may want
571 # though not with LFS revisions). We may want
572 # ``verifyintegrity()`` to pass a set of nodes with
572 # ``verifyintegrity()`` to pass a set of nodes with
573 # rename metadata as an optimization.
573 # rename metadata as an optimization.
574 rp = fl.renamed(n)
574 rp = fl.renamed(n)
575 if rp:
575 if rp:
576 if lr is not None and ui.verbose:
576 if lr is not None and ui.verbose:
577 ctx = lrugetctx(lr)
577 ctx = lrugetctx(lr)
578 if not any(rp[0] in pctx for pctx in ctx.parents()):
578 if not any(rp[0] in pctx for pctx in ctx.parents()):
579 self._warn(
579 self._warn(
580 _(
580 _(
581 b"warning: copy source of '%s' not"
581 b"warning: copy source of '%s' not"
582 b" in parents of %s"
582 b" in parents of %s"
583 )
583 )
584 % (f, ctx)
584 % (f, ctx)
585 )
585 )
586 fl2 = repo.file(rp[0])
586 fl2 = repo.file(rp[0])
587 if not len(fl2):
587 if not len(fl2):
588 self._err(
588 self._err(
589 lr,
589 lr,
590 _(
590 _(
591 b"empty or missing copy source revlog "
591 b"empty or missing copy source revlog "
592 b"%s:%s"
592 b"%s:%s"
593 )
593 )
594 % (rp[0], short(rp[1])),
594 % (rp[0], short(rp[1])),
595 f,
595 f,
596 )
596 )
597 elif rp[1] == nullid:
597 elif rp[1] == nullid:
598 ui.note(
598 ui.note(
599 _(
599 _(
600 b"warning: %s@%s: copy source"
600 b"warning: %s@%s: copy source"
601 b" revision is nullid %s:%s\n"
601 b" revision is nullid %s:%s\n"
602 )
602 )
603 % (f, lr, rp[0], short(rp[1]))
603 % (f, lr, rp[0], short(rp[1]))
604 )
604 )
605 else:
605 else:
606 fl2.rev(rp[1])
606 fl2.rev(rp[1])
607 except Exception as inst:
607 except Exception as inst:
608 self._exc(
608 self._exc(
609 lr, _(b"checking rename of %s") % short(n), inst, f
609 lr, _(b"checking rename of %s") % short(n), inst, f
610 )
610 )
611
611
612 # cross-check
612 # cross-check
613 if f in filenodes:
613 if f in filenodes:
614 fns = [(v, k) for k, v in pycompat.iteritems(filenodes[f])]
614 fns = [(v, k) for k, v in pycompat.iteritems(filenodes[f])]
615 for lr, node in sorted(fns):
615 for lr, node in sorted(fns):
616 self._err(
616 self._err(
617 lr,
617 lr,
618 _(b"manifest refers to unknown revision %s")
618 _(b"manifest refers to unknown revision %s")
619 % short(node),
619 % short(node),
620 f,
620 f,
621 )
621 )
622 progress.complete()
622 progress.complete()
623
623
624 if self.warnorphanstorefiles:
624 if self.warnorphanstorefiles:
625 for f in sorted(storefiles):
625 for f in sorted(storefiles):
626 self._warn(_(b"warning: orphan data file '%s'") % f)
626 self._warn(_(b"warning: orphan data file '%s'") % f)
627
627
628 return len(files), revisions
628 return len(files), revisions
@@ -1,411 +1,419 b''
1 Test for command `hg unamend` which lives in uncommit extension
1 Test for command `hg unamend` which lives in uncommit extension
2 ===============================================================
2 ===============================================================
3
3
4 $ cat >> $HGRCPATH << EOF
4 $ cat >> $HGRCPATH << EOF
5 > [alias]
5 > [alias]
6 > glog = log -G -T '{rev}:{node|short} {desc}'
6 > glog = log -G -T '{rev}:{node|short} {desc}'
7 > [experimental]
7 > [experimental]
8 > evolution = createmarkers, allowunstable
8 > evolution = createmarkers, allowunstable
9 > [extensions]
9 > [extensions]
10 > rebase =
10 > rebase =
11 > amend =
11 > amend =
12 > uncommit =
12 > uncommit =
13 > EOF
13 > EOF
14
14
15 Repo Setup
15 Repo Setup
16
16
17 $ hg init repo
17 $ hg init repo
18 $ cd repo
18 $ cd repo
19 $ for ch in a b c d e f g h; do touch $ch; echo "foo" >> $ch; hg ci -Aqm "Added "$ch; done
19 $ for ch in a b c d e f g h; do touch $ch; echo "foo" >> $ch; hg ci -Aqm "Added "$ch; done
20
20
21 $ hg glog
21 $ hg glog
22 @ 7:ec2426147f0e Added h
22 @ 7:ec2426147f0e Added h
23 |
23 |
24 o 6:87d6d6676308 Added g
24 o 6:87d6d6676308 Added g
25 |
25 |
26 o 5:825660c69f0c Added f
26 o 5:825660c69f0c Added f
27 |
27 |
28 o 4:aa98ab95a928 Added e
28 o 4:aa98ab95a928 Added e
29 |
29 |
30 o 3:62615734edd5 Added d
30 o 3:62615734edd5 Added d
31 |
31 |
32 o 2:28ad74487de9 Added c
32 o 2:28ad74487de9 Added c
33 |
33 |
34 o 1:29becc82797a Added b
34 o 1:29becc82797a Added b
35 |
35 |
36 o 0:18d04c59bb5d Added a
36 o 0:18d04c59bb5d Added a
37
37
38 Trying to unamend when there was no amend done
38 Trying to unamend when there was no amend done
39
39
40 $ hg unamend
40 $ hg unamend
41 abort: changeset must have one predecessor, found 0 predecessors
41 abort: changeset must have one predecessor, found 0 predecessors
42 [255]
42 [255]
43
43
44 Unamend on clean wdir and tip
44 Unamend on clean wdir and tip
45
45
46 $ echo "bar" >> h
46 $ echo "bar" >> h
47 $ hg amend
47 $ hg amend
48
48
49 $ hg exp
49 $ hg exp
50 # HG changeset patch
50 # HG changeset patch
51 # User test
51 # User test
52 # Date 0 0
52 # Date 0 0
53 # Thu Jan 01 00:00:00 1970 +0000
53 # Thu Jan 01 00:00:00 1970 +0000
54 # Node ID c9fa1a715c1b7661c0fafb362a9f30bd75878d7d
54 # Node ID c9fa1a715c1b7661c0fafb362a9f30bd75878d7d
55 # Parent 87d6d66763085b629e6d7ed56778c79827273022
55 # Parent 87d6d66763085b629e6d7ed56778c79827273022
56 Added h
56 Added h
57
57
58 diff -r 87d6d6676308 -r c9fa1a715c1b h
58 diff -r 87d6d6676308 -r c9fa1a715c1b h
59 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
59 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
60 +++ b/h Thu Jan 01 00:00:00 1970 +0000
60 +++ b/h Thu Jan 01 00:00:00 1970 +0000
61 @@ -0,0 +1,2 @@
61 @@ -0,0 +1,2 @@
62 +foo
62 +foo
63 +bar
63 +bar
64
64
65 $ hg glog --hidden
65 $ hg glog --hidden
66 @ 8:c9fa1a715c1b Added h
66 @ 8:c9fa1a715c1b Added h
67 |
67 |
68 | x 7:ec2426147f0e Added h
68 | x 7:ec2426147f0e Added h
69 |/
69 |/
70 o 6:87d6d6676308 Added g
70 o 6:87d6d6676308 Added g
71 |
71 |
72 o 5:825660c69f0c Added f
72 o 5:825660c69f0c Added f
73 |
73 |
74 o 4:aa98ab95a928 Added e
74 o 4:aa98ab95a928 Added e
75 |
75 |
76 o 3:62615734edd5 Added d
76 o 3:62615734edd5 Added d
77 |
77 |
78 o 2:28ad74487de9 Added c
78 o 2:28ad74487de9 Added c
79 |
79 |
80 o 1:29becc82797a Added b
80 o 1:29becc82797a Added b
81 |
81 |
82 o 0:18d04c59bb5d Added a
82 o 0:18d04c59bb5d Added a
83
83
84 $ hg unamend
84 $ hg unamend
85 $ hg glog --hidden
85 $ hg glog --hidden
86 @ 9:46d02d47eec6 Added h
86 @ 9:46d02d47eec6 Added h
87 |
87 |
88 | x 8:c9fa1a715c1b Added h
88 | x 8:c9fa1a715c1b Added h
89 |/
89 |/
90 | x 7:ec2426147f0e Added h
90 | x 7:ec2426147f0e Added h
91 |/
91 |/
92 o 6:87d6d6676308 Added g
92 o 6:87d6d6676308 Added g
93 |
93 |
94 o 5:825660c69f0c Added f
94 o 5:825660c69f0c Added f
95 |
95 |
96 o 4:aa98ab95a928 Added e
96 o 4:aa98ab95a928 Added e
97 |
97 |
98 o 3:62615734edd5 Added d
98 o 3:62615734edd5 Added d
99 |
99 |
100 o 2:28ad74487de9 Added c
100 o 2:28ad74487de9 Added c
101 |
101 |
102 o 1:29becc82797a Added b
102 o 1:29becc82797a Added b
103 |
103 |
104 o 0:18d04c59bb5d Added a
104 o 0:18d04c59bb5d Added a
105
105
106 $ hg diff
106 $ hg diff
107 diff -r 46d02d47eec6 h
107 diff -r 46d02d47eec6 h
108 --- a/h Thu Jan 01 00:00:00 1970 +0000
108 --- a/h Thu Jan 01 00:00:00 1970 +0000
109 +++ b/h Thu Jan 01 00:00:00 1970 +0000
109 +++ b/h Thu Jan 01 00:00:00 1970 +0000
110 @@ -1,1 +1,2 @@
110 @@ -1,1 +1,2 @@
111 foo
111 foo
112 +bar
112 +bar
113
113
114 $ hg exp
114 $ hg exp
115 # HG changeset patch
115 # HG changeset patch
116 # User test
116 # User test
117 # Date 0 0
117 # Date 0 0
118 # Thu Jan 01 00:00:00 1970 +0000
118 # Thu Jan 01 00:00:00 1970 +0000
119 # Node ID 46d02d47eec6ca096b8dcab3f8f5579c40c3dd9a
119 # Node ID 46d02d47eec6ca096b8dcab3f8f5579c40c3dd9a
120 # Parent 87d6d66763085b629e6d7ed56778c79827273022
120 # Parent 87d6d66763085b629e6d7ed56778c79827273022
121 Added h
121 Added h
122
122
123 diff -r 87d6d6676308 -r 46d02d47eec6 h
123 diff -r 87d6d6676308 -r 46d02d47eec6 h
124 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
124 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
125 +++ b/h Thu Jan 01 00:00:00 1970 +0000
125 +++ b/h Thu Jan 01 00:00:00 1970 +0000
126 @@ -0,0 +1,1 @@
126 @@ -0,0 +1,1 @@
127 +foo
127 +foo
128
128
129 $ hg status
129 $ hg status
130 M h
130 M h
131
131
132 $ hg log -r . -T '{extras % "{extra}\n"}' --config alias.log=log
132 $ hg log -r . -T '{extras % "{extra}\n"}' --config alias.log=log
133 branch=default
133 branch=default
134 unamend_source=c9fa1a715c1b7661c0fafb362a9f30bd75878d7d
134 unamend_source=c9fa1a715c1b7661c0fafb362a9f30bd75878d7d
135
135
136 Using unamend to undo an unamed (intentional)
136 Using unamend to undo an unamed (intentional)
137
137
138 $ hg unamend
138 $ hg unamend
139 $ hg exp
139 $ hg exp
140 # HG changeset patch
140 # HG changeset patch
141 # User test
141 # User test
142 # Date 0 0
142 # Date 0 0
143 # Thu Jan 01 00:00:00 1970 +0000
143 # Thu Jan 01 00:00:00 1970 +0000
144 # Node ID 850ddfc1bc662997ec6094ada958f01f0cc8070a
144 # Node ID 850ddfc1bc662997ec6094ada958f01f0cc8070a
145 # Parent 87d6d66763085b629e6d7ed56778c79827273022
145 # Parent 87d6d66763085b629e6d7ed56778c79827273022
146 Added h
146 Added h
147
147
148 diff -r 87d6d6676308 -r 850ddfc1bc66 h
148 diff -r 87d6d6676308 -r 850ddfc1bc66 h
149 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
149 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
150 +++ b/h Thu Jan 01 00:00:00 1970 +0000
150 +++ b/h Thu Jan 01 00:00:00 1970 +0000
151 @@ -0,0 +1,2 @@
151 @@ -0,0 +1,2 @@
152 +foo
152 +foo
153 +bar
153 +bar
154 $ hg diff
154 $ hg diff
155
155
156 Unamend on a dirty working directory
156 Unamend on a dirty working directory
157
157
158 $ echo "bar" >> a
158 $ echo "bar" >> a
159 $ hg amend
159 $ hg amend
160 $ echo "foobar" >> a
160 $ echo "foobar" >> a
161 $ echo "bar" >> b
161 $ echo "bar" >> b
162 $ hg status
162 $ hg status
163 M a
163 M a
164 M b
164 M b
165
165
166 $ hg unamend
166 $ hg unamend
167
167
168 $ hg status
168 $ hg status
169 M a
169 M a
170 M b
170 M b
171
171
172 $ hg diff
172 $ hg diff
173 diff -r ec338db45d51 a
173 diff -r ec338db45d51 a
174 --- a/a Thu Jan 01 00:00:00 1970 +0000
174 --- a/a Thu Jan 01 00:00:00 1970 +0000
175 +++ b/a Thu Jan 01 00:00:00 1970 +0000
175 +++ b/a Thu Jan 01 00:00:00 1970 +0000
176 @@ -1,1 +1,3 @@
176 @@ -1,1 +1,3 @@
177 foo
177 foo
178 +bar
178 +bar
179 +foobar
179 +foobar
180 diff -r ec338db45d51 b
180 diff -r ec338db45d51 b
181 --- a/b Thu Jan 01 00:00:00 1970 +0000
181 --- a/b Thu Jan 01 00:00:00 1970 +0000
182 +++ b/b Thu Jan 01 00:00:00 1970 +0000
182 +++ b/b Thu Jan 01 00:00:00 1970 +0000
183 @@ -1,1 +1,2 @@
183 @@ -1,1 +1,2 @@
184 foo
184 foo
185 +bar
185 +bar
186
186
187 Unamending an added file
187 Unamending an added file
188
188
189 $ hg ci -m "Added things to a and b"
189 $ hg ci -m "Added things to a and b"
190 $ echo foo > bar
190 $ echo foo > bar
191 $ hg add bar
191 $ hg add bar
192 $ hg amend
192 $ hg amend
193
193
194 $ hg unamend
194 $ hg unamend
195 $ hg status
195 $ hg status
196 A bar
196 A bar
197
197
198 $ hg revert --all
198 $ hg revert --all
199 forgetting bar
199 forgetting bar
200
200
201 Unamending a removed file
201 Unamending a removed file
202
202
203 $ hg remove a
203 $ hg remove a
204 $ hg amend
204 $ hg amend
205
205
206 $ hg unamend
206 $ hg unamend
207 $ hg status
207 $ hg status
208 R a
208 R a
209 ? bar
209 ? bar
210
210
211 $ hg revert --all
211 $ hg revert --all
212 undeleting a
212 undeleting a
213
213
214 Unamending an added file with dirty wdir status
214 Unamending an added file with dirty wdir status
215
215
216 $ hg add bar
216 $ hg add bar
217 $ hg amend
217 $ hg amend
218 $ echo bar >> bar
218 $ echo bar >> bar
219 $ hg status
219 $ hg status
220 M bar
220 M bar
221
221
222 $ hg unamend
222 $ hg unamend
223 $ hg status
223 $ hg status
224 A bar
224 A bar
225 $ hg diff
225 $ hg diff
226 diff -r 7f79409af972 bar
226 diff -r 7f79409af972 bar
227 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
227 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
228 +++ b/bar Thu Jan 01 00:00:00 1970 +0000
228 +++ b/bar Thu Jan 01 00:00:00 1970 +0000
229 @@ -0,0 +1,2 @@
229 @@ -0,0 +1,2 @@
230 +foo
230 +foo
231 +bar
231 +bar
232
232
233 $ hg revert --all
233 $ hg revert --all
234 forgetting bar
234 forgetting bar
235 $ rm bar
235 $ rm bar
236
236
237 Unamending in middle of a stack
237 Unamending in middle of a stack
238
238
239 $ hg glog
239 $ hg glog
240 @ 19:7f79409af972 Added things to a and b
240 @ 19:7f79409af972 Added things to a and b
241 |
241 |
242 o 12:ec338db45d51 Added h
242 o 12:ec338db45d51 Added h
243 |
243 |
244 o 6:87d6d6676308 Added g
244 o 6:87d6d6676308 Added g
245 |
245 |
246 o 5:825660c69f0c Added f
246 o 5:825660c69f0c Added f
247 |
247 |
248 o 4:aa98ab95a928 Added e
248 o 4:aa98ab95a928 Added e
249 |
249 |
250 o 3:62615734edd5 Added d
250 o 3:62615734edd5 Added d
251 |
251 |
252 o 2:28ad74487de9 Added c
252 o 2:28ad74487de9 Added c
253 |
253 |
254 o 1:29becc82797a Added b
254 o 1:29becc82797a Added b
255 |
255 |
256 o 0:18d04c59bb5d Added a
256 o 0:18d04c59bb5d Added a
257
257
258 $ hg up 5
258 $ hg up 5
259 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
259 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
260 $ echo bar >> f
260 $ echo bar >> f
261 $ hg amend
261 $ hg amend
262 3 new orphan changesets
262 3 new orphan changesets
263 $ hg rebase -s 6 -d . -q
263 $ hg rebase -s 6 -d . -q
264
264
265 $ hg glog
265 $ hg glog
266 o 23:03ddd6fc5af1 Added things to a and b
266 o 23:03ddd6fc5af1 Added things to a and b
267 |
267 |
268 o 22:3e7b64ee157b Added h
268 o 22:3e7b64ee157b Added h
269 |
269 |
270 o 21:49635b68477e Added g
270 o 21:49635b68477e Added g
271 |
271 |
272 @ 20:93f0e8ffab32 Added f
272 @ 20:93f0e8ffab32 Added f
273 |
273 |
274 o 4:aa98ab95a928 Added e
274 o 4:aa98ab95a928 Added e
275 |
275 |
276 o 3:62615734edd5 Added d
276 o 3:62615734edd5 Added d
277 |
277 |
278 o 2:28ad74487de9 Added c
278 o 2:28ad74487de9 Added c
279 |
279 |
280 o 1:29becc82797a Added b
280 o 1:29becc82797a Added b
281 |
281 |
282 o 0:18d04c59bb5d Added a
282 o 0:18d04c59bb5d Added a
283
283
284
284
285 $ hg --config experimental.evolution=createmarkers unamend
285 $ hg --config experimental.evolution=createmarkers unamend
286 abort: cannot unamend changeset with children
286 abort: cannot unamend changeset with children
287 [255]
287 [255]
288
288
289 $ hg unamend
289 $ hg unamend
290 3 new orphan changesets
290 3 new orphan changesets
291
291
292 Trying to unamend a public changeset
292 Trying to unamend a public changeset
293
293
294 $ hg up -C 23
294 $ hg up -C 23
295 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
295 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
296 $ hg phase -r . -p
296 $ hg phase -r . -p
297 1 new phase-divergent changesets
297 1 new phase-divergent changesets
298 $ hg unamend
298 $ hg unamend
299 abort: cannot unamend public changesets
299 abort: cannot unamend public changesets
300 (see 'hg help phases' for details)
300 (see 'hg help phases' for details)
301 [255]
301 [255]
302
302
303 Testing whether unamend retains copies or not
303 Testing whether unamend retains copies or not
304
304
305 $ hg status
305 $ hg status
306
306
307 $ hg mv a foo
307 $ hg mv a foo
308
308
309 $ hg ci -m "Moved a to foo"
309 $ hg ci -m "Moved a to foo"
310 $ hg exp --git
310 $ hg exp --git
311 # HG changeset patch
311 # HG changeset patch
312 # User test
312 # User test
313 # Date 0 0
313 # Date 0 0
314 # Thu Jan 01 00:00:00 1970 +0000
314 # Thu Jan 01 00:00:00 1970 +0000
315 # Node ID cfef290346fbee5126313d7e1aab51d877679b09
315 # Node ID cfef290346fbee5126313d7e1aab51d877679b09
316 # Parent 03ddd6fc5af19e028c44a2fd6d790dd22712f231
316 # Parent 03ddd6fc5af19e028c44a2fd6d790dd22712f231
317 Moved a to foo
317 Moved a to foo
318
318
319 diff --git a/a b/foo
319 diff --git a/a b/foo
320 rename from a
320 rename from a
321 rename to foo
321 rename to foo
322
322
323 $ hg mv b foobar
323 $ hg mv b foobar
324 $ hg diff --git
324 $ hg diff --git
325 diff --git a/b b/foobar
325 diff --git a/b b/foobar
326 rename from b
326 rename from b
327 rename to foobar
327 rename to foobar
328 $ hg amend
328 $ hg amend
329
329
330 $ hg exp --git
330 $ hg exp --git
331 # HG changeset patch
331 # HG changeset patch
332 # User test
332 # User test
333 # Date 0 0
333 # Date 0 0
334 # Thu Jan 01 00:00:00 1970 +0000
334 # Thu Jan 01 00:00:00 1970 +0000
335 # Node ID eca050985275bb271ce3092b54e56ea5c85d29a3
335 # Node ID eca050985275bb271ce3092b54e56ea5c85d29a3
336 # Parent 03ddd6fc5af19e028c44a2fd6d790dd22712f231
336 # Parent 03ddd6fc5af19e028c44a2fd6d790dd22712f231
337 Moved a to foo
337 Moved a to foo
338
338
339 diff --git a/a b/foo
339 diff --git a/a b/foo
340 rename from a
340 rename from a
341 rename to foo
341 rename to foo
342 diff --git a/b b/foobar
342 diff --git a/b b/foobar
343 rename from b
343 rename from b
344 rename to foobar
344 rename to foobar
345
345
346 $ hg mv c wat
346 $ hg mv c wat
347 $ hg unamend
347 $ hg unamend
348
348
349 $ hg verify -v
350 repository uses revlog format 1
351 checking changesets
352 checking manifests
353 crosschecking files in changesets and manifests
354 checking files
355 checked 28 changesets with 16 changes to 11 files
356
349 Retained copies in new prdecessor commit
357 Retained copies in new prdecessor commit
350
358
351 $ hg exp --git
359 $ hg exp --git
352 # HG changeset patch
360 # HG changeset patch
353 # User test
361 # User test
354 # Date 0 0
362 # Date 0 0
355 # Thu Jan 01 00:00:00 1970 +0000
363 # Thu Jan 01 00:00:00 1970 +0000
356 # Node ID 552e3af4f01f620f88ca27be1f898316235b736a
364 # Node ID 552e3af4f01f620f88ca27be1f898316235b736a
357 # Parent 03ddd6fc5af19e028c44a2fd6d790dd22712f231
365 # Parent 03ddd6fc5af19e028c44a2fd6d790dd22712f231
358 Moved a to foo
366 Moved a to foo
359
367
360 diff --git a/a b/foo
368 diff --git a/a b/foo
361 rename from a
369 rename from a
362 rename to foo
370 rename to foo
363
371
364 Retained copies in working directoy
372 Retained copies in working directoy
365
373
366 $ hg diff --git
374 $ hg diff --git
367 diff --git a/b b/foobar
375 diff --git a/b b/foobar
368 rename from b
376 rename from b
369 rename to foobar
377 rename to foobar
370 diff --git a/c b/wat
378 diff --git a/c b/wat
371 rename from c
379 rename from c
372 rename to wat
380 rename to wat
373 $ hg revert -qa
381 $ hg revert -qa
374 $ rm foobar wat
382 $ rm foobar wat
375
383
376 Rename a->b, then amend b->c. After unamend, should look like b->c.
384 Rename a->b, then amend b->c. After unamend, should look like b->c.
377
385
378 $ hg co -q 0
386 $ hg co -q 0
379 $ hg mv a b
387 $ hg mv a b
380 $ hg ci -qm 'move to a b'
388 $ hg ci -qm 'move to a b'
381 $ hg mv b c
389 $ hg mv b c
382 $ hg amend
390 $ hg amend
383 $ hg unamend
391 $ hg unamend
384 $ hg st --copies --change .
392 $ hg st --copies --change .
385 A b
393 A b
386 a
394 a
387 R a
395 R a
388 $ hg st --copies
396 $ hg st --copies
389 A c
397 A c
390 b
398 b
391 R b
399 R b
392 $ hg revert -qa
400 $ hg revert -qa
393 $ rm c
401 $ rm c
394
402
395 Rename a->b, then amend b->c, and working copy change c->d. After unamend, should look like b->d
403 Rename a->b, then amend b->c, and working copy change c->d. After unamend, should look like b->d
396
404
397 $ hg co -q 0
405 $ hg co -q 0
398 $ hg mv a b
406 $ hg mv a b
399 $ hg ci -qm 'move to a b'
407 $ hg ci -qm 'move to a b'
400 $ hg mv b c
408 $ hg mv b c
401 $ hg amend
409 $ hg amend
402 $ hg mv c d
410 $ hg mv c d
403 $ hg unamend
411 $ hg unamend
404 $ hg st --copies --change .
412 $ hg st --copies --change .
405 A b
413 A b
406 a
414 a
407 R a
415 R a
408 $ hg st --copies
416 $ hg st --copies
409 A d
417 A d
410 b
418 b
411 R b
419 R b
General Comments 0
You need to be logged in to leave comments. Login now