##// END OF EJS Templates
scmutil: use util.shellquote instead of %r...
Augie Fackler -
r33795:2cd5aba5 default
parent child Browse files
Show More
@@ -1,1105 +1,1105 b''
1 # scmutil.py - Mercurial core utility functions
1 # scmutil.py - Mercurial core utility functions
2 #
2 #
3 # Copyright Matt Mackall <mpm@selenic.com>
3 # Copyright 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 errno
10 import errno
11 import glob
11 import glob
12 import hashlib
12 import hashlib
13 import os
13 import os
14 import re
14 import re
15 import socket
15 import socket
16 import weakref
16 import weakref
17
17
18 from .i18n import _
18 from .i18n import _
19 from .node import (
19 from .node import (
20 hex,
20 hex,
21 nullid,
21 nullid,
22 wdirid,
22 wdirid,
23 wdirrev,
23 wdirrev,
24 )
24 )
25
25
26 from . import (
26 from . import (
27 encoding,
27 encoding,
28 error,
28 error,
29 match as matchmod,
29 match as matchmod,
30 obsolete,
30 obsolete,
31 obsutil,
31 obsutil,
32 pathutil,
32 pathutil,
33 phases,
33 phases,
34 pycompat,
34 pycompat,
35 revsetlang,
35 revsetlang,
36 similar,
36 similar,
37 util,
37 util,
38 )
38 )
39
39
40 if pycompat.osname == 'nt':
40 if pycompat.osname == 'nt':
41 from . import scmwindows as scmplatform
41 from . import scmwindows as scmplatform
42 else:
42 else:
43 from . import scmposix as scmplatform
43 from . import scmposix as scmplatform
44
44
45 termsize = scmplatform.termsize
45 termsize = scmplatform.termsize
46
46
47 class status(tuple):
47 class status(tuple):
48 '''Named tuple with a list of files per status. The 'deleted', 'unknown'
48 '''Named tuple with a list of files per status. The 'deleted', 'unknown'
49 and 'ignored' properties are only relevant to the working copy.
49 and 'ignored' properties are only relevant to the working copy.
50 '''
50 '''
51
51
52 __slots__ = ()
52 __slots__ = ()
53
53
54 def __new__(cls, modified, added, removed, deleted, unknown, ignored,
54 def __new__(cls, modified, added, removed, deleted, unknown, ignored,
55 clean):
55 clean):
56 return tuple.__new__(cls, (modified, added, removed, deleted, unknown,
56 return tuple.__new__(cls, (modified, added, removed, deleted, unknown,
57 ignored, clean))
57 ignored, clean))
58
58
59 @property
59 @property
60 def modified(self):
60 def modified(self):
61 '''files that have been modified'''
61 '''files that have been modified'''
62 return self[0]
62 return self[0]
63
63
64 @property
64 @property
65 def added(self):
65 def added(self):
66 '''files that have been added'''
66 '''files that have been added'''
67 return self[1]
67 return self[1]
68
68
69 @property
69 @property
70 def removed(self):
70 def removed(self):
71 '''files that have been removed'''
71 '''files that have been removed'''
72 return self[2]
72 return self[2]
73
73
74 @property
74 @property
75 def deleted(self):
75 def deleted(self):
76 '''files that are in the dirstate, but have been deleted from the
76 '''files that are in the dirstate, but have been deleted from the
77 working copy (aka "missing")
77 working copy (aka "missing")
78 '''
78 '''
79 return self[3]
79 return self[3]
80
80
81 @property
81 @property
82 def unknown(self):
82 def unknown(self):
83 '''files not in the dirstate that are not ignored'''
83 '''files not in the dirstate that are not ignored'''
84 return self[4]
84 return self[4]
85
85
86 @property
86 @property
87 def ignored(self):
87 def ignored(self):
88 '''files not in the dirstate that are ignored (by _dirignore())'''
88 '''files not in the dirstate that are ignored (by _dirignore())'''
89 return self[5]
89 return self[5]
90
90
91 @property
91 @property
92 def clean(self):
92 def clean(self):
93 '''files that have not been modified'''
93 '''files that have not been modified'''
94 return self[6]
94 return self[6]
95
95
96 def __repr__(self, *args, **kwargs):
96 def __repr__(self, *args, **kwargs):
97 return (('<status modified=%r, added=%r, removed=%r, deleted=%r, '
97 return (('<status modified=%r, added=%r, removed=%r, deleted=%r, '
98 'unknown=%r, ignored=%r, clean=%r>') % self)
98 'unknown=%r, ignored=%r, clean=%r>') % self)
99
99
100 def itersubrepos(ctx1, ctx2):
100 def itersubrepos(ctx1, ctx2):
101 """find subrepos in ctx1 or ctx2"""
101 """find subrepos in ctx1 or ctx2"""
102 # Create a (subpath, ctx) mapping where we prefer subpaths from
102 # Create a (subpath, ctx) mapping where we prefer subpaths from
103 # ctx1. The subpaths from ctx2 are important when the .hgsub file
103 # ctx1. The subpaths from ctx2 are important when the .hgsub file
104 # has been modified (in ctx2) but not yet committed (in ctx1).
104 # has been modified (in ctx2) but not yet committed (in ctx1).
105 subpaths = dict.fromkeys(ctx2.substate, ctx2)
105 subpaths = dict.fromkeys(ctx2.substate, ctx2)
106 subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
106 subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
107
107
108 missing = set()
108 missing = set()
109
109
110 for subpath in ctx2.substate:
110 for subpath in ctx2.substate:
111 if subpath not in ctx1.substate:
111 if subpath not in ctx1.substate:
112 del subpaths[subpath]
112 del subpaths[subpath]
113 missing.add(subpath)
113 missing.add(subpath)
114
114
115 for subpath, ctx in sorted(subpaths.iteritems()):
115 for subpath, ctx in sorted(subpaths.iteritems()):
116 yield subpath, ctx.sub(subpath)
116 yield subpath, ctx.sub(subpath)
117
117
118 # Yield an empty subrepo based on ctx1 for anything only in ctx2. That way,
118 # Yield an empty subrepo based on ctx1 for anything only in ctx2. That way,
119 # status and diff will have an accurate result when it does
119 # status and diff will have an accurate result when it does
120 # 'sub.{status|diff}(rev2)'. Otherwise, the ctx2 subrepo is compared
120 # 'sub.{status|diff}(rev2)'. Otherwise, the ctx2 subrepo is compared
121 # against itself.
121 # against itself.
122 for subpath in missing:
122 for subpath in missing:
123 yield subpath, ctx2.nullsub(subpath, ctx1)
123 yield subpath, ctx2.nullsub(subpath, ctx1)
124
124
125 def nochangesfound(ui, repo, excluded=None):
125 def nochangesfound(ui, repo, excluded=None):
126 '''Report no changes for push/pull, excluded is None or a list of
126 '''Report no changes for push/pull, excluded is None or a list of
127 nodes excluded from the push/pull.
127 nodes excluded from the push/pull.
128 '''
128 '''
129 secretlist = []
129 secretlist = []
130 if excluded:
130 if excluded:
131 for n in excluded:
131 for n in excluded:
132 ctx = repo[n]
132 ctx = repo[n]
133 if ctx.phase() >= phases.secret and not ctx.extinct():
133 if ctx.phase() >= phases.secret and not ctx.extinct():
134 secretlist.append(n)
134 secretlist.append(n)
135
135
136 if secretlist:
136 if secretlist:
137 ui.status(_("no changes found (ignored %d secret changesets)\n")
137 ui.status(_("no changes found (ignored %d secret changesets)\n")
138 % len(secretlist))
138 % len(secretlist))
139 else:
139 else:
140 ui.status(_("no changes found\n"))
140 ui.status(_("no changes found\n"))
141
141
142 def callcatch(ui, func):
142 def callcatch(ui, func):
143 """call func() with global exception handling
143 """call func() with global exception handling
144
144
145 return func() if no exception happens. otherwise do some error handling
145 return func() if no exception happens. otherwise do some error handling
146 and return an exit code accordingly. does not handle all exceptions.
146 and return an exit code accordingly. does not handle all exceptions.
147 """
147 """
148 try:
148 try:
149 try:
149 try:
150 return func()
150 return func()
151 except: # re-raises
151 except: # re-raises
152 ui.traceback()
152 ui.traceback()
153 raise
153 raise
154 # Global exception handling, alphabetically
154 # Global exception handling, alphabetically
155 # Mercurial-specific first, followed by built-in and library exceptions
155 # Mercurial-specific first, followed by built-in and library exceptions
156 except error.LockHeld as inst:
156 except error.LockHeld as inst:
157 if inst.errno == errno.ETIMEDOUT:
157 if inst.errno == errno.ETIMEDOUT:
158 reason = _('timed out waiting for lock held by %r') % inst.locker
158 reason = _('timed out waiting for lock held by %r') % inst.locker
159 else:
159 else:
160 reason = _('lock held by %r') % inst.locker
160 reason = _('lock held by %r') % inst.locker
161 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
161 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
162 if not inst.locker:
162 if not inst.locker:
163 ui.warn(_("(lock might be very busy)\n"))
163 ui.warn(_("(lock might be very busy)\n"))
164 except error.LockUnavailable as inst:
164 except error.LockUnavailable as inst:
165 ui.warn(_("abort: could not lock %s: %s\n") %
165 ui.warn(_("abort: could not lock %s: %s\n") %
166 (inst.desc or inst.filename, inst.strerror))
166 (inst.desc or inst.filename, inst.strerror))
167 except error.OutOfBandError as inst:
167 except error.OutOfBandError as inst:
168 if inst.args:
168 if inst.args:
169 msg = _("abort: remote error:\n")
169 msg = _("abort: remote error:\n")
170 else:
170 else:
171 msg = _("abort: remote error\n")
171 msg = _("abort: remote error\n")
172 ui.warn(msg)
172 ui.warn(msg)
173 if inst.args:
173 if inst.args:
174 ui.warn(''.join(inst.args))
174 ui.warn(''.join(inst.args))
175 if inst.hint:
175 if inst.hint:
176 ui.warn('(%s)\n' % inst.hint)
176 ui.warn('(%s)\n' % inst.hint)
177 except error.RepoError as inst:
177 except error.RepoError as inst:
178 ui.warn(_("abort: %s!\n") % inst)
178 ui.warn(_("abort: %s!\n") % inst)
179 if inst.hint:
179 if inst.hint:
180 ui.warn(_("(%s)\n") % inst.hint)
180 ui.warn(_("(%s)\n") % inst.hint)
181 except error.ResponseError as inst:
181 except error.ResponseError as inst:
182 ui.warn(_("abort: %s") % inst.args[0])
182 ui.warn(_("abort: %s") % inst.args[0])
183 if not isinstance(inst.args[1], basestring):
183 if not isinstance(inst.args[1], basestring):
184 ui.warn(" %r\n" % (inst.args[1],))
184 ui.warn(" %r\n" % (inst.args[1],))
185 elif not inst.args[1]:
185 elif not inst.args[1]:
186 ui.warn(_(" empty string\n"))
186 ui.warn(_(" empty string\n"))
187 else:
187 else:
188 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
188 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
189 except error.CensoredNodeError as inst:
189 except error.CensoredNodeError as inst:
190 ui.warn(_("abort: file censored %s!\n") % inst)
190 ui.warn(_("abort: file censored %s!\n") % inst)
191 except error.RevlogError as inst:
191 except error.RevlogError as inst:
192 ui.warn(_("abort: %s!\n") % inst)
192 ui.warn(_("abort: %s!\n") % inst)
193 except error.InterventionRequired as inst:
193 except error.InterventionRequired as inst:
194 ui.warn("%s\n" % inst)
194 ui.warn("%s\n" % inst)
195 if inst.hint:
195 if inst.hint:
196 ui.warn(_("(%s)\n") % inst.hint)
196 ui.warn(_("(%s)\n") % inst.hint)
197 return 1
197 return 1
198 except error.WdirUnsupported:
198 except error.WdirUnsupported:
199 ui.warn(_("abort: working directory revision cannot be specified\n"))
199 ui.warn(_("abort: working directory revision cannot be specified\n"))
200 except error.Abort as inst:
200 except error.Abort as inst:
201 ui.warn(_("abort: %s\n") % inst)
201 ui.warn(_("abort: %s\n") % inst)
202 if inst.hint:
202 if inst.hint:
203 ui.warn(_("(%s)\n") % inst.hint)
203 ui.warn(_("(%s)\n") % inst.hint)
204 except ImportError as inst:
204 except ImportError as inst:
205 ui.warn(_("abort: %s!\n") % inst)
205 ui.warn(_("abort: %s!\n") % inst)
206 m = str(inst).split()[-1]
206 m = str(inst).split()[-1]
207 if m in "mpatch bdiff".split():
207 if m in "mpatch bdiff".split():
208 ui.warn(_("(did you forget to compile extensions?)\n"))
208 ui.warn(_("(did you forget to compile extensions?)\n"))
209 elif m in "zlib".split():
209 elif m in "zlib".split():
210 ui.warn(_("(is your Python install correct?)\n"))
210 ui.warn(_("(is your Python install correct?)\n"))
211 except IOError as inst:
211 except IOError as inst:
212 if util.safehasattr(inst, "code"):
212 if util.safehasattr(inst, "code"):
213 ui.warn(_("abort: %s\n") % inst)
213 ui.warn(_("abort: %s\n") % inst)
214 elif util.safehasattr(inst, "reason"):
214 elif util.safehasattr(inst, "reason"):
215 try: # usually it is in the form (errno, strerror)
215 try: # usually it is in the form (errno, strerror)
216 reason = inst.reason.args[1]
216 reason = inst.reason.args[1]
217 except (AttributeError, IndexError):
217 except (AttributeError, IndexError):
218 # it might be anything, for example a string
218 # it might be anything, for example a string
219 reason = inst.reason
219 reason = inst.reason
220 if isinstance(reason, unicode):
220 if isinstance(reason, unicode):
221 # SSLError of Python 2.7.9 contains a unicode
221 # SSLError of Python 2.7.9 contains a unicode
222 reason = encoding.unitolocal(reason)
222 reason = encoding.unitolocal(reason)
223 ui.warn(_("abort: error: %s\n") % reason)
223 ui.warn(_("abort: error: %s\n") % reason)
224 elif (util.safehasattr(inst, "args")
224 elif (util.safehasattr(inst, "args")
225 and inst.args and inst.args[0] == errno.EPIPE):
225 and inst.args and inst.args[0] == errno.EPIPE):
226 pass
226 pass
227 elif getattr(inst, "strerror", None):
227 elif getattr(inst, "strerror", None):
228 if getattr(inst, "filename", None):
228 if getattr(inst, "filename", None):
229 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
229 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
230 else:
230 else:
231 ui.warn(_("abort: %s\n") % inst.strerror)
231 ui.warn(_("abort: %s\n") % inst.strerror)
232 else:
232 else:
233 raise
233 raise
234 except OSError as inst:
234 except OSError as inst:
235 if getattr(inst, "filename", None) is not None:
235 if getattr(inst, "filename", None) is not None:
236 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
236 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
237 else:
237 else:
238 ui.warn(_("abort: %s\n") % inst.strerror)
238 ui.warn(_("abort: %s\n") % inst.strerror)
239 except MemoryError:
239 except MemoryError:
240 ui.warn(_("abort: out of memory\n"))
240 ui.warn(_("abort: out of memory\n"))
241 except SystemExit as inst:
241 except SystemExit as inst:
242 # Commands shouldn't sys.exit directly, but give a return code.
242 # Commands shouldn't sys.exit directly, but give a return code.
243 # Just in case catch this and and pass exit code to caller.
243 # Just in case catch this and and pass exit code to caller.
244 return inst.code
244 return inst.code
245 except socket.error as inst:
245 except socket.error as inst:
246 ui.warn(_("abort: %s\n") % inst.args[-1])
246 ui.warn(_("abort: %s\n") % inst.args[-1])
247
247
248 return -1
248 return -1
249
249
250 def checknewlabel(repo, lbl, kind):
250 def checknewlabel(repo, lbl, kind):
251 # Do not use the "kind" parameter in ui output.
251 # Do not use the "kind" parameter in ui output.
252 # It makes strings difficult to translate.
252 # It makes strings difficult to translate.
253 if lbl in ['tip', '.', 'null']:
253 if lbl in ['tip', '.', 'null']:
254 raise error.Abort(_("the name '%s' is reserved") % lbl)
254 raise error.Abort(_("the name '%s' is reserved") % lbl)
255 for c in (':', '\0', '\n', '\r'):
255 for c in (':', '\0', '\n', '\r'):
256 if c in lbl:
256 if c in lbl:
257 raise error.Abort(_("%r cannot be used in a name") % c)
257 raise error.Abort(_("%r cannot be used in a name") % c)
258 try:
258 try:
259 int(lbl)
259 int(lbl)
260 raise error.Abort(_("cannot use an integer as a name"))
260 raise error.Abort(_("cannot use an integer as a name"))
261 except ValueError:
261 except ValueError:
262 pass
262 pass
263
263
264 def checkfilename(f):
264 def checkfilename(f):
265 '''Check that the filename f is an acceptable filename for a tracked file'''
265 '''Check that the filename f is an acceptable filename for a tracked file'''
266 if '\r' in f or '\n' in f:
266 if '\r' in f or '\n' in f:
267 raise error.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
267 raise error.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
268
268
269 def checkportable(ui, f):
269 def checkportable(ui, f):
270 '''Check if filename f is portable and warn or abort depending on config'''
270 '''Check if filename f is portable and warn or abort depending on config'''
271 checkfilename(f)
271 checkfilename(f)
272 abort, warn = checkportabilityalert(ui)
272 abort, warn = checkportabilityalert(ui)
273 if abort or warn:
273 if abort or warn:
274 msg = util.checkwinfilename(f)
274 msg = util.checkwinfilename(f)
275 if msg:
275 if msg:
276 msg = "%s: %r" % (msg, f)
276 msg = "%s: %s" % (msg, util.shellquote(f))
277 if abort:
277 if abort:
278 raise error.Abort(msg)
278 raise error.Abort(msg)
279 ui.warn(_("warning: %s\n") % msg)
279 ui.warn(_("warning: %s\n") % msg)
280
280
281 def checkportabilityalert(ui):
281 def checkportabilityalert(ui):
282 '''check if the user's config requests nothing, a warning, or abort for
282 '''check if the user's config requests nothing, a warning, or abort for
283 non-portable filenames'''
283 non-portable filenames'''
284 val = ui.config('ui', 'portablefilenames')
284 val = ui.config('ui', 'portablefilenames')
285 lval = val.lower()
285 lval = val.lower()
286 bval = util.parsebool(val)
286 bval = util.parsebool(val)
287 abort = pycompat.osname == 'nt' or lval == 'abort'
287 abort = pycompat.osname == 'nt' or lval == 'abort'
288 warn = bval or lval == 'warn'
288 warn = bval or lval == 'warn'
289 if bval is None and not (warn or abort or lval == 'ignore'):
289 if bval is None and not (warn or abort or lval == 'ignore'):
290 raise error.ConfigError(
290 raise error.ConfigError(
291 _("ui.portablefilenames value is invalid ('%s')") % val)
291 _("ui.portablefilenames value is invalid ('%s')") % val)
292 return abort, warn
292 return abort, warn
293
293
294 class casecollisionauditor(object):
294 class casecollisionauditor(object):
295 def __init__(self, ui, abort, dirstate):
295 def __init__(self, ui, abort, dirstate):
296 self._ui = ui
296 self._ui = ui
297 self._abort = abort
297 self._abort = abort
298 allfiles = '\0'.join(dirstate._map)
298 allfiles = '\0'.join(dirstate._map)
299 self._loweredfiles = set(encoding.lower(allfiles).split('\0'))
299 self._loweredfiles = set(encoding.lower(allfiles).split('\0'))
300 self._dirstate = dirstate
300 self._dirstate = dirstate
301 # The purpose of _newfiles is so that we don't complain about
301 # The purpose of _newfiles is so that we don't complain about
302 # case collisions if someone were to call this object with the
302 # case collisions if someone were to call this object with the
303 # same filename twice.
303 # same filename twice.
304 self._newfiles = set()
304 self._newfiles = set()
305
305
306 def __call__(self, f):
306 def __call__(self, f):
307 if f in self._newfiles:
307 if f in self._newfiles:
308 return
308 return
309 fl = encoding.lower(f)
309 fl = encoding.lower(f)
310 if fl in self._loweredfiles and f not in self._dirstate:
310 if fl in self._loweredfiles and f not in self._dirstate:
311 msg = _('possible case-folding collision for %s') % f
311 msg = _('possible case-folding collision for %s') % f
312 if self._abort:
312 if self._abort:
313 raise error.Abort(msg)
313 raise error.Abort(msg)
314 self._ui.warn(_("warning: %s\n") % msg)
314 self._ui.warn(_("warning: %s\n") % msg)
315 self._loweredfiles.add(fl)
315 self._loweredfiles.add(fl)
316 self._newfiles.add(f)
316 self._newfiles.add(f)
317
317
318 def filteredhash(repo, maxrev):
318 def filteredhash(repo, maxrev):
319 """build hash of filtered revisions in the current repoview.
319 """build hash of filtered revisions in the current repoview.
320
320
321 Multiple caches perform up-to-date validation by checking that the
321 Multiple caches perform up-to-date validation by checking that the
322 tiprev and tipnode stored in the cache file match the current repository.
322 tiprev and tipnode stored in the cache file match the current repository.
323 However, this is not sufficient for validating repoviews because the set
323 However, this is not sufficient for validating repoviews because the set
324 of revisions in the view may change without the repository tiprev and
324 of revisions in the view may change without the repository tiprev and
325 tipnode changing.
325 tipnode changing.
326
326
327 This function hashes all the revs filtered from the view and returns
327 This function hashes all the revs filtered from the view and returns
328 that SHA-1 digest.
328 that SHA-1 digest.
329 """
329 """
330 cl = repo.changelog
330 cl = repo.changelog
331 if not cl.filteredrevs:
331 if not cl.filteredrevs:
332 return None
332 return None
333 key = None
333 key = None
334 revs = sorted(r for r in cl.filteredrevs if r <= maxrev)
334 revs = sorted(r for r in cl.filteredrevs if r <= maxrev)
335 if revs:
335 if revs:
336 s = hashlib.sha1()
336 s = hashlib.sha1()
337 for rev in revs:
337 for rev in revs:
338 s.update('%d;' % rev)
338 s.update('%d;' % rev)
339 key = s.digest()
339 key = s.digest()
340 return key
340 return key
341
341
342 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
342 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
343 '''yield every hg repository under path, always recursively.
343 '''yield every hg repository under path, always recursively.
344 The recurse flag will only control recursion into repo working dirs'''
344 The recurse flag will only control recursion into repo working dirs'''
345 def errhandler(err):
345 def errhandler(err):
346 if err.filename == path:
346 if err.filename == path:
347 raise err
347 raise err
348 samestat = getattr(os.path, 'samestat', None)
348 samestat = getattr(os.path, 'samestat', None)
349 if followsym and samestat is not None:
349 if followsym and samestat is not None:
350 def adddir(dirlst, dirname):
350 def adddir(dirlst, dirname):
351 match = False
351 match = False
352 dirstat = os.stat(dirname)
352 dirstat = os.stat(dirname)
353 for lstdirstat in dirlst:
353 for lstdirstat in dirlst:
354 if samestat(dirstat, lstdirstat):
354 if samestat(dirstat, lstdirstat):
355 match = True
355 match = True
356 break
356 break
357 if not match:
357 if not match:
358 dirlst.append(dirstat)
358 dirlst.append(dirstat)
359 return not match
359 return not match
360 else:
360 else:
361 followsym = False
361 followsym = False
362
362
363 if (seen_dirs is None) and followsym:
363 if (seen_dirs is None) and followsym:
364 seen_dirs = []
364 seen_dirs = []
365 adddir(seen_dirs, path)
365 adddir(seen_dirs, path)
366 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
366 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
367 dirs.sort()
367 dirs.sort()
368 if '.hg' in dirs:
368 if '.hg' in dirs:
369 yield root # found a repository
369 yield root # found a repository
370 qroot = os.path.join(root, '.hg', 'patches')
370 qroot = os.path.join(root, '.hg', 'patches')
371 if os.path.isdir(os.path.join(qroot, '.hg')):
371 if os.path.isdir(os.path.join(qroot, '.hg')):
372 yield qroot # we have a patch queue repo here
372 yield qroot # we have a patch queue repo here
373 if recurse:
373 if recurse:
374 # avoid recursing inside the .hg directory
374 # avoid recursing inside the .hg directory
375 dirs.remove('.hg')
375 dirs.remove('.hg')
376 else:
376 else:
377 dirs[:] = [] # don't descend further
377 dirs[:] = [] # don't descend further
378 elif followsym:
378 elif followsym:
379 newdirs = []
379 newdirs = []
380 for d in dirs:
380 for d in dirs:
381 fname = os.path.join(root, d)
381 fname = os.path.join(root, d)
382 if adddir(seen_dirs, fname):
382 if adddir(seen_dirs, fname):
383 if os.path.islink(fname):
383 if os.path.islink(fname):
384 for hgname in walkrepos(fname, True, seen_dirs):
384 for hgname in walkrepos(fname, True, seen_dirs):
385 yield hgname
385 yield hgname
386 else:
386 else:
387 newdirs.append(d)
387 newdirs.append(d)
388 dirs[:] = newdirs
388 dirs[:] = newdirs
389
389
390 def binnode(ctx):
390 def binnode(ctx):
391 """Return binary node id for a given basectx"""
391 """Return binary node id for a given basectx"""
392 node = ctx.node()
392 node = ctx.node()
393 if node is None:
393 if node is None:
394 return wdirid
394 return wdirid
395 return node
395 return node
396
396
397 def intrev(ctx):
397 def intrev(ctx):
398 """Return integer for a given basectx that can be used in comparison or
398 """Return integer for a given basectx that can be used in comparison or
399 arithmetic operation"""
399 arithmetic operation"""
400 rev = ctx.rev()
400 rev = ctx.rev()
401 if rev is None:
401 if rev is None:
402 return wdirrev
402 return wdirrev
403 return rev
403 return rev
404
404
405 def revsingle(repo, revspec, default='.'):
405 def revsingle(repo, revspec, default='.'):
406 if not revspec and revspec != 0:
406 if not revspec and revspec != 0:
407 return repo[default]
407 return repo[default]
408
408
409 l = revrange(repo, [revspec])
409 l = revrange(repo, [revspec])
410 if not l:
410 if not l:
411 raise error.Abort(_('empty revision set'))
411 raise error.Abort(_('empty revision set'))
412 return repo[l.last()]
412 return repo[l.last()]
413
413
414 def _pairspec(revspec):
414 def _pairspec(revspec):
415 tree = revsetlang.parse(revspec)
415 tree = revsetlang.parse(revspec)
416 return tree and tree[0] in ('range', 'rangepre', 'rangepost', 'rangeall')
416 return tree and tree[0] in ('range', 'rangepre', 'rangepost', 'rangeall')
417
417
418 def revpair(repo, revs):
418 def revpair(repo, revs):
419 if not revs:
419 if not revs:
420 return repo.dirstate.p1(), None
420 return repo.dirstate.p1(), None
421
421
422 l = revrange(repo, revs)
422 l = revrange(repo, revs)
423
423
424 if not l:
424 if not l:
425 first = second = None
425 first = second = None
426 elif l.isascending():
426 elif l.isascending():
427 first = l.min()
427 first = l.min()
428 second = l.max()
428 second = l.max()
429 elif l.isdescending():
429 elif l.isdescending():
430 first = l.max()
430 first = l.max()
431 second = l.min()
431 second = l.min()
432 else:
432 else:
433 first = l.first()
433 first = l.first()
434 second = l.last()
434 second = l.last()
435
435
436 if first is None:
436 if first is None:
437 raise error.Abort(_('empty revision range'))
437 raise error.Abort(_('empty revision range'))
438 if (first == second and len(revs) >= 2
438 if (first == second and len(revs) >= 2
439 and not all(revrange(repo, [r]) for r in revs)):
439 and not all(revrange(repo, [r]) for r in revs)):
440 raise error.Abort(_('empty revision on one side of range'))
440 raise error.Abort(_('empty revision on one side of range'))
441
441
442 # if top-level is range expression, the result must always be a pair
442 # if top-level is range expression, the result must always be a pair
443 if first == second and len(revs) == 1 and not _pairspec(revs[0]):
443 if first == second and len(revs) == 1 and not _pairspec(revs[0]):
444 return repo.lookup(first), None
444 return repo.lookup(first), None
445
445
446 return repo.lookup(first), repo.lookup(second)
446 return repo.lookup(first), repo.lookup(second)
447
447
448 def revrange(repo, specs):
448 def revrange(repo, specs):
449 """Execute 1 to many revsets and return the union.
449 """Execute 1 to many revsets and return the union.
450
450
451 This is the preferred mechanism for executing revsets using user-specified
451 This is the preferred mechanism for executing revsets using user-specified
452 config options, such as revset aliases.
452 config options, such as revset aliases.
453
453
454 The revsets specified by ``specs`` will be executed via a chained ``OR``
454 The revsets specified by ``specs`` will be executed via a chained ``OR``
455 expression. If ``specs`` is empty, an empty result is returned.
455 expression. If ``specs`` is empty, an empty result is returned.
456
456
457 ``specs`` can contain integers, in which case they are assumed to be
457 ``specs`` can contain integers, in which case they are assumed to be
458 revision numbers.
458 revision numbers.
459
459
460 It is assumed the revsets are already formatted. If you have arguments
460 It is assumed the revsets are already formatted. If you have arguments
461 that need to be expanded in the revset, call ``revsetlang.formatspec()``
461 that need to be expanded in the revset, call ``revsetlang.formatspec()``
462 and pass the result as an element of ``specs``.
462 and pass the result as an element of ``specs``.
463
463
464 Specifying a single revset is allowed.
464 Specifying a single revset is allowed.
465
465
466 Returns a ``revset.abstractsmartset`` which is a list-like interface over
466 Returns a ``revset.abstractsmartset`` which is a list-like interface over
467 integer revisions.
467 integer revisions.
468 """
468 """
469 allspecs = []
469 allspecs = []
470 for spec in specs:
470 for spec in specs:
471 if isinstance(spec, int):
471 if isinstance(spec, int):
472 spec = revsetlang.formatspec('rev(%d)', spec)
472 spec = revsetlang.formatspec('rev(%d)', spec)
473 allspecs.append(spec)
473 allspecs.append(spec)
474 return repo.anyrevs(allspecs, user=True)
474 return repo.anyrevs(allspecs, user=True)
475
475
476 def meaningfulparents(repo, ctx):
476 def meaningfulparents(repo, ctx):
477 """Return list of meaningful (or all if debug) parentrevs for rev.
477 """Return list of meaningful (or all if debug) parentrevs for rev.
478
478
479 For merges (two non-nullrev revisions) both parents are meaningful.
479 For merges (two non-nullrev revisions) both parents are meaningful.
480 Otherwise the first parent revision is considered meaningful if it
480 Otherwise the first parent revision is considered meaningful if it
481 is not the preceding revision.
481 is not the preceding revision.
482 """
482 """
483 parents = ctx.parents()
483 parents = ctx.parents()
484 if len(parents) > 1:
484 if len(parents) > 1:
485 return parents
485 return parents
486 if repo.ui.debugflag:
486 if repo.ui.debugflag:
487 return [parents[0], repo['null']]
487 return [parents[0], repo['null']]
488 if parents[0].rev() >= intrev(ctx) - 1:
488 if parents[0].rev() >= intrev(ctx) - 1:
489 return []
489 return []
490 return parents
490 return parents
491
491
492 def expandpats(pats):
492 def expandpats(pats):
493 '''Expand bare globs when running on windows.
493 '''Expand bare globs when running on windows.
494 On posix we assume it already has already been done by sh.'''
494 On posix we assume it already has already been done by sh.'''
495 if not util.expandglobs:
495 if not util.expandglobs:
496 return list(pats)
496 return list(pats)
497 ret = []
497 ret = []
498 for kindpat in pats:
498 for kindpat in pats:
499 kind, pat = matchmod._patsplit(kindpat, None)
499 kind, pat = matchmod._patsplit(kindpat, None)
500 if kind is None:
500 if kind is None:
501 try:
501 try:
502 globbed = glob.glob(pat)
502 globbed = glob.glob(pat)
503 except re.error:
503 except re.error:
504 globbed = [pat]
504 globbed = [pat]
505 if globbed:
505 if globbed:
506 ret.extend(globbed)
506 ret.extend(globbed)
507 continue
507 continue
508 ret.append(kindpat)
508 ret.append(kindpat)
509 return ret
509 return ret
510
510
511 def matchandpats(ctx, pats=(), opts=None, globbed=False, default='relpath',
511 def matchandpats(ctx, pats=(), opts=None, globbed=False, default='relpath',
512 badfn=None):
512 badfn=None):
513 '''Return a matcher and the patterns that were used.
513 '''Return a matcher and the patterns that were used.
514 The matcher will warn about bad matches, unless an alternate badfn callback
514 The matcher will warn about bad matches, unless an alternate badfn callback
515 is provided.'''
515 is provided.'''
516 if pats == ("",):
516 if pats == ("",):
517 pats = []
517 pats = []
518 if opts is None:
518 if opts is None:
519 opts = {}
519 opts = {}
520 if not globbed and default == 'relpath':
520 if not globbed and default == 'relpath':
521 pats = expandpats(pats or [])
521 pats = expandpats(pats or [])
522
522
523 def bad(f, msg):
523 def bad(f, msg):
524 ctx.repo().ui.warn("%s: %s\n" % (m.rel(f), msg))
524 ctx.repo().ui.warn("%s: %s\n" % (m.rel(f), msg))
525
525
526 if badfn is None:
526 if badfn is None:
527 badfn = bad
527 badfn = bad
528
528
529 m = ctx.match(pats, opts.get('include'), opts.get('exclude'),
529 m = ctx.match(pats, opts.get('include'), opts.get('exclude'),
530 default, listsubrepos=opts.get('subrepos'), badfn=badfn)
530 default, listsubrepos=opts.get('subrepos'), badfn=badfn)
531
531
532 if m.always():
532 if m.always():
533 pats = []
533 pats = []
534 return m, pats
534 return m, pats
535
535
536 def match(ctx, pats=(), opts=None, globbed=False, default='relpath',
536 def match(ctx, pats=(), opts=None, globbed=False, default='relpath',
537 badfn=None):
537 badfn=None):
538 '''Return a matcher that will warn about bad matches.'''
538 '''Return a matcher that will warn about bad matches.'''
539 return matchandpats(ctx, pats, opts, globbed, default, badfn=badfn)[0]
539 return matchandpats(ctx, pats, opts, globbed, default, badfn=badfn)[0]
540
540
541 def matchall(repo):
541 def matchall(repo):
542 '''Return a matcher that will efficiently match everything.'''
542 '''Return a matcher that will efficiently match everything.'''
543 return matchmod.always(repo.root, repo.getcwd())
543 return matchmod.always(repo.root, repo.getcwd())
544
544
545 def matchfiles(repo, files, badfn=None):
545 def matchfiles(repo, files, badfn=None):
546 '''Return a matcher that will efficiently match exactly these files.'''
546 '''Return a matcher that will efficiently match exactly these files.'''
547 return matchmod.exact(repo.root, repo.getcwd(), files, badfn=badfn)
547 return matchmod.exact(repo.root, repo.getcwd(), files, badfn=badfn)
548
548
549 def origpath(ui, repo, filepath):
549 def origpath(ui, repo, filepath):
550 '''customize where .orig files are created
550 '''customize where .orig files are created
551
551
552 Fetch user defined path from config file: [ui] origbackuppath = <path>
552 Fetch user defined path from config file: [ui] origbackuppath = <path>
553 Fall back to default (filepath) if not specified
553 Fall back to default (filepath) if not specified
554 '''
554 '''
555 origbackuppath = ui.config('ui', 'origbackuppath')
555 origbackuppath = ui.config('ui', 'origbackuppath')
556 if origbackuppath is None:
556 if origbackuppath is None:
557 return filepath + ".orig"
557 return filepath + ".orig"
558
558
559 filepathfromroot = os.path.relpath(filepath, start=repo.root)
559 filepathfromroot = os.path.relpath(filepath, start=repo.root)
560 fullorigpath = repo.wjoin(origbackuppath, filepathfromroot)
560 fullorigpath = repo.wjoin(origbackuppath, filepathfromroot)
561
561
562 origbackupdir = repo.vfs.dirname(fullorigpath)
562 origbackupdir = repo.vfs.dirname(fullorigpath)
563 if not repo.vfs.exists(origbackupdir):
563 if not repo.vfs.exists(origbackupdir):
564 ui.note(_('creating directory: %s\n') % origbackupdir)
564 ui.note(_('creating directory: %s\n') % origbackupdir)
565 util.makedirs(origbackupdir)
565 util.makedirs(origbackupdir)
566
566
567 return fullorigpath + ".orig"
567 return fullorigpath + ".orig"
568
568
569 class _containsnode(object):
569 class _containsnode(object):
570 """proxy __contains__(node) to container.__contains__ which accepts revs"""
570 """proxy __contains__(node) to container.__contains__ which accepts revs"""
571
571
572 def __init__(self, repo, revcontainer):
572 def __init__(self, repo, revcontainer):
573 self._torev = repo.changelog.rev
573 self._torev = repo.changelog.rev
574 self._revcontains = revcontainer.__contains__
574 self._revcontains = revcontainer.__contains__
575
575
576 def __contains__(self, node):
576 def __contains__(self, node):
577 return self._revcontains(self._torev(node))
577 return self._revcontains(self._torev(node))
578
578
579 def cleanupnodes(repo, mapping, operation):
579 def cleanupnodes(repo, mapping, operation):
580 """do common cleanups when old nodes are replaced by new nodes
580 """do common cleanups when old nodes are replaced by new nodes
581
581
582 That includes writing obsmarkers or stripping nodes, and moving bookmarks.
582 That includes writing obsmarkers or stripping nodes, and moving bookmarks.
583 (we might also want to move working directory parent in the future)
583 (we might also want to move working directory parent in the future)
584
584
585 mapping is {oldnode: [newnode]} or a iterable of nodes if they do not have
585 mapping is {oldnode: [newnode]} or a iterable of nodes if they do not have
586 replacements. operation is a string, like "rebase".
586 replacements. operation is a string, like "rebase".
587 """
587 """
588 if not util.safehasattr(mapping, 'items'):
588 if not util.safehasattr(mapping, 'items'):
589 mapping = {n: () for n in mapping}
589 mapping = {n: () for n in mapping}
590
590
591 with repo.transaction('cleanup') as tr:
591 with repo.transaction('cleanup') as tr:
592 # Move bookmarks
592 # Move bookmarks
593 bmarks = repo._bookmarks
593 bmarks = repo._bookmarks
594 bmarkchanges = []
594 bmarkchanges = []
595 allnewnodes = [n for ns in mapping.values() for n in ns]
595 allnewnodes = [n for ns in mapping.values() for n in ns]
596 for oldnode, newnodes in mapping.items():
596 for oldnode, newnodes in mapping.items():
597 oldbmarks = repo.nodebookmarks(oldnode)
597 oldbmarks = repo.nodebookmarks(oldnode)
598 if not oldbmarks:
598 if not oldbmarks:
599 continue
599 continue
600 from . import bookmarks # avoid import cycle
600 from . import bookmarks # avoid import cycle
601 if len(newnodes) > 1:
601 if len(newnodes) > 1:
602 # usually a split, take the one with biggest rev number
602 # usually a split, take the one with biggest rev number
603 newnode = next(repo.set('max(%ln)', newnodes)).node()
603 newnode = next(repo.set('max(%ln)', newnodes)).node()
604 elif len(newnodes) == 0:
604 elif len(newnodes) == 0:
605 # move bookmark backwards
605 # move bookmark backwards
606 roots = list(repo.set('max((::%n) - %ln)', oldnode,
606 roots = list(repo.set('max((::%n) - %ln)', oldnode,
607 list(mapping)))
607 list(mapping)))
608 if roots:
608 if roots:
609 newnode = roots[0].node()
609 newnode = roots[0].node()
610 else:
610 else:
611 newnode = nullid
611 newnode = nullid
612 else:
612 else:
613 newnode = newnodes[0]
613 newnode = newnodes[0]
614 repo.ui.debug('moving bookmarks %r from %s to %s\n' %
614 repo.ui.debug('moving bookmarks %r from %s to %s\n' %
615 (oldbmarks, hex(oldnode), hex(newnode)))
615 (oldbmarks, hex(oldnode), hex(newnode)))
616 # Delete divergent bookmarks being parents of related newnodes
616 # Delete divergent bookmarks being parents of related newnodes
617 deleterevs = repo.revs('parents(roots(%ln & (::%n))) - parents(%n)',
617 deleterevs = repo.revs('parents(roots(%ln & (::%n))) - parents(%n)',
618 allnewnodes, newnode, oldnode)
618 allnewnodes, newnode, oldnode)
619 deletenodes = _containsnode(repo, deleterevs)
619 deletenodes = _containsnode(repo, deleterevs)
620 for name in oldbmarks:
620 for name in oldbmarks:
621 bmarkchanges.append((name, newnode))
621 bmarkchanges.append((name, newnode))
622 for b in bookmarks.divergent2delete(repo, deletenodes, name):
622 for b in bookmarks.divergent2delete(repo, deletenodes, name):
623 bmarkchanges.append((b, None))
623 bmarkchanges.append((b, None))
624
624
625 if bmarkchanges:
625 if bmarkchanges:
626 bmarks.applychanges(repo, tr, bmarkchanges)
626 bmarks.applychanges(repo, tr, bmarkchanges)
627
627
628 # Obsolete or strip nodes
628 # Obsolete or strip nodes
629 if obsolete.isenabled(repo, obsolete.createmarkersopt):
629 if obsolete.isenabled(repo, obsolete.createmarkersopt):
630 # If a node is already obsoleted, and we want to obsolete it
630 # If a node is already obsoleted, and we want to obsolete it
631 # without a successor, skip that obssolete request since it's
631 # without a successor, skip that obssolete request since it's
632 # unnecessary. That's the "if s or not isobs(n)" check below.
632 # unnecessary. That's the "if s or not isobs(n)" check below.
633 # Also sort the node in topology order, that might be useful for
633 # Also sort the node in topology order, that might be useful for
634 # some obsstore logic.
634 # some obsstore logic.
635 # NOTE: the filtering and sorting might belong to createmarkers.
635 # NOTE: the filtering and sorting might belong to createmarkers.
636 # Unfiltered repo is needed since nodes in mapping might be hidden.
636 # Unfiltered repo is needed since nodes in mapping might be hidden.
637 unfi = repo.unfiltered()
637 unfi = repo.unfiltered()
638 isobs = unfi.obsstore.successors.__contains__
638 isobs = unfi.obsstore.successors.__contains__
639 torev = unfi.changelog.rev
639 torev = unfi.changelog.rev
640 sortfunc = lambda ns: torev(ns[0])
640 sortfunc = lambda ns: torev(ns[0])
641 rels = [(unfi[n], tuple(unfi[m] for m in s))
641 rels = [(unfi[n], tuple(unfi[m] for m in s))
642 for n, s in sorted(mapping.items(), key=sortfunc)
642 for n, s in sorted(mapping.items(), key=sortfunc)
643 if s or not isobs(n)]
643 if s or not isobs(n)]
644 obsolete.createmarkers(repo, rels, operation=operation)
644 obsolete.createmarkers(repo, rels, operation=operation)
645 else:
645 else:
646 from . import repair # avoid import cycle
646 from . import repair # avoid import cycle
647 repair.delayedstrip(repo.ui, repo, list(mapping), operation)
647 repair.delayedstrip(repo.ui, repo, list(mapping), operation)
648
648
649 def addremove(repo, matcher, prefix, opts=None, dry_run=None, similarity=None):
649 def addremove(repo, matcher, prefix, opts=None, dry_run=None, similarity=None):
650 if opts is None:
650 if opts is None:
651 opts = {}
651 opts = {}
652 m = matcher
652 m = matcher
653 if dry_run is None:
653 if dry_run is None:
654 dry_run = opts.get('dry_run')
654 dry_run = opts.get('dry_run')
655 if similarity is None:
655 if similarity is None:
656 similarity = float(opts.get('similarity') or 0)
656 similarity = float(opts.get('similarity') or 0)
657
657
658 ret = 0
658 ret = 0
659 join = lambda f: os.path.join(prefix, f)
659 join = lambda f: os.path.join(prefix, f)
660
660
661 wctx = repo[None]
661 wctx = repo[None]
662 for subpath in sorted(wctx.substate):
662 for subpath in sorted(wctx.substate):
663 submatch = matchmod.subdirmatcher(subpath, m)
663 submatch = matchmod.subdirmatcher(subpath, m)
664 if opts.get('subrepos') or m.exact(subpath) or any(submatch.files()):
664 if opts.get('subrepos') or m.exact(subpath) or any(submatch.files()):
665 sub = wctx.sub(subpath)
665 sub = wctx.sub(subpath)
666 try:
666 try:
667 if sub.addremove(submatch, prefix, opts, dry_run, similarity):
667 if sub.addremove(submatch, prefix, opts, dry_run, similarity):
668 ret = 1
668 ret = 1
669 except error.LookupError:
669 except error.LookupError:
670 repo.ui.status(_("skipping missing subrepository: %s\n")
670 repo.ui.status(_("skipping missing subrepository: %s\n")
671 % join(subpath))
671 % join(subpath))
672
672
673 rejected = []
673 rejected = []
674 def badfn(f, msg):
674 def badfn(f, msg):
675 if f in m.files():
675 if f in m.files():
676 m.bad(f, msg)
676 m.bad(f, msg)
677 rejected.append(f)
677 rejected.append(f)
678
678
679 badmatch = matchmod.badmatch(m, badfn)
679 badmatch = matchmod.badmatch(m, badfn)
680 added, unknown, deleted, removed, forgotten = _interestingfiles(repo,
680 added, unknown, deleted, removed, forgotten = _interestingfiles(repo,
681 badmatch)
681 badmatch)
682
682
683 unknownset = set(unknown + forgotten)
683 unknownset = set(unknown + forgotten)
684 toprint = unknownset.copy()
684 toprint = unknownset.copy()
685 toprint.update(deleted)
685 toprint.update(deleted)
686 for abs in sorted(toprint):
686 for abs in sorted(toprint):
687 if repo.ui.verbose or not m.exact(abs):
687 if repo.ui.verbose or not m.exact(abs):
688 if abs in unknownset:
688 if abs in unknownset:
689 status = _('adding %s\n') % m.uipath(abs)
689 status = _('adding %s\n') % m.uipath(abs)
690 else:
690 else:
691 status = _('removing %s\n') % m.uipath(abs)
691 status = _('removing %s\n') % m.uipath(abs)
692 repo.ui.status(status)
692 repo.ui.status(status)
693
693
694 renames = _findrenames(repo, m, added + unknown, removed + deleted,
694 renames = _findrenames(repo, m, added + unknown, removed + deleted,
695 similarity)
695 similarity)
696
696
697 if not dry_run:
697 if not dry_run:
698 _markchanges(repo, unknown + forgotten, deleted, renames)
698 _markchanges(repo, unknown + forgotten, deleted, renames)
699
699
700 for f in rejected:
700 for f in rejected:
701 if f in m.files():
701 if f in m.files():
702 return 1
702 return 1
703 return ret
703 return ret
704
704
705 def marktouched(repo, files, similarity=0.0):
705 def marktouched(repo, files, similarity=0.0):
706 '''Assert that files have somehow been operated upon. files are relative to
706 '''Assert that files have somehow been operated upon. files are relative to
707 the repo root.'''
707 the repo root.'''
708 m = matchfiles(repo, files, badfn=lambda x, y: rejected.append(x))
708 m = matchfiles(repo, files, badfn=lambda x, y: rejected.append(x))
709 rejected = []
709 rejected = []
710
710
711 added, unknown, deleted, removed, forgotten = _interestingfiles(repo, m)
711 added, unknown, deleted, removed, forgotten = _interestingfiles(repo, m)
712
712
713 if repo.ui.verbose:
713 if repo.ui.verbose:
714 unknownset = set(unknown + forgotten)
714 unknownset = set(unknown + forgotten)
715 toprint = unknownset.copy()
715 toprint = unknownset.copy()
716 toprint.update(deleted)
716 toprint.update(deleted)
717 for abs in sorted(toprint):
717 for abs in sorted(toprint):
718 if abs in unknownset:
718 if abs in unknownset:
719 status = _('adding %s\n') % abs
719 status = _('adding %s\n') % abs
720 else:
720 else:
721 status = _('removing %s\n') % abs
721 status = _('removing %s\n') % abs
722 repo.ui.status(status)
722 repo.ui.status(status)
723
723
724 renames = _findrenames(repo, m, added + unknown, removed + deleted,
724 renames = _findrenames(repo, m, added + unknown, removed + deleted,
725 similarity)
725 similarity)
726
726
727 _markchanges(repo, unknown + forgotten, deleted, renames)
727 _markchanges(repo, unknown + forgotten, deleted, renames)
728
728
729 for f in rejected:
729 for f in rejected:
730 if f in m.files():
730 if f in m.files():
731 return 1
731 return 1
732 return 0
732 return 0
733
733
734 def _interestingfiles(repo, matcher):
734 def _interestingfiles(repo, matcher):
735 '''Walk dirstate with matcher, looking for files that addremove would care
735 '''Walk dirstate with matcher, looking for files that addremove would care
736 about.
736 about.
737
737
738 This is different from dirstate.status because it doesn't care about
738 This is different from dirstate.status because it doesn't care about
739 whether files are modified or clean.'''
739 whether files are modified or clean.'''
740 added, unknown, deleted, removed, forgotten = [], [], [], [], []
740 added, unknown, deleted, removed, forgotten = [], [], [], [], []
741 audit_path = pathutil.pathauditor(repo.root, cached=True)
741 audit_path = pathutil.pathauditor(repo.root, cached=True)
742
742
743 ctx = repo[None]
743 ctx = repo[None]
744 dirstate = repo.dirstate
744 dirstate = repo.dirstate
745 walkresults = dirstate.walk(matcher, sorted(ctx.substate), True, False,
745 walkresults = dirstate.walk(matcher, sorted(ctx.substate), True, False,
746 full=False)
746 full=False)
747 for abs, st in walkresults.iteritems():
747 for abs, st in walkresults.iteritems():
748 dstate = dirstate[abs]
748 dstate = dirstate[abs]
749 if dstate == '?' and audit_path.check(abs):
749 if dstate == '?' and audit_path.check(abs):
750 unknown.append(abs)
750 unknown.append(abs)
751 elif dstate != 'r' and not st:
751 elif dstate != 'r' and not st:
752 deleted.append(abs)
752 deleted.append(abs)
753 elif dstate == 'r' and st:
753 elif dstate == 'r' and st:
754 forgotten.append(abs)
754 forgotten.append(abs)
755 # for finding renames
755 # for finding renames
756 elif dstate == 'r' and not st:
756 elif dstate == 'r' and not st:
757 removed.append(abs)
757 removed.append(abs)
758 elif dstate == 'a':
758 elif dstate == 'a':
759 added.append(abs)
759 added.append(abs)
760
760
761 return added, unknown, deleted, removed, forgotten
761 return added, unknown, deleted, removed, forgotten
762
762
763 def _findrenames(repo, matcher, added, removed, similarity):
763 def _findrenames(repo, matcher, added, removed, similarity):
764 '''Find renames from removed files to added ones.'''
764 '''Find renames from removed files to added ones.'''
765 renames = {}
765 renames = {}
766 if similarity > 0:
766 if similarity > 0:
767 for old, new, score in similar.findrenames(repo, added, removed,
767 for old, new, score in similar.findrenames(repo, added, removed,
768 similarity):
768 similarity):
769 if (repo.ui.verbose or not matcher.exact(old)
769 if (repo.ui.verbose or not matcher.exact(old)
770 or not matcher.exact(new)):
770 or not matcher.exact(new)):
771 repo.ui.status(_('recording removal of %s as rename to %s '
771 repo.ui.status(_('recording removal of %s as rename to %s '
772 '(%d%% similar)\n') %
772 '(%d%% similar)\n') %
773 (matcher.rel(old), matcher.rel(new),
773 (matcher.rel(old), matcher.rel(new),
774 score * 100))
774 score * 100))
775 renames[new] = old
775 renames[new] = old
776 return renames
776 return renames
777
777
778 def _markchanges(repo, unknown, deleted, renames):
778 def _markchanges(repo, unknown, deleted, renames):
779 '''Marks the files in unknown as added, the files in deleted as removed,
779 '''Marks the files in unknown as added, the files in deleted as removed,
780 and the files in renames as copied.'''
780 and the files in renames as copied.'''
781 wctx = repo[None]
781 wctx = repo[None]
782 with repo.wlock():
782 with repo.wlock():
783 wctx.forget(deleted)
783 wctx.forget(deleted)
784 wctx.add(unknown)
784 wctx.add(unknown)
785 for new, old in renames.iteritems():
785 for new, old in renames.iteritems():
786 wctx.copy(old, new)
786 wctx.copy(old, new)
787
787
788 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
788 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
789 """Update the dirstate to reflect the intent of copying src to dst. For
789 """Update the dirstate to reflect the intent of copying src to dst. For
790 different reasons it might not end with dst being marked as copied from src.
790 different reasons it might not end with dst being marked as copied from src.
791 """
791 """
792 origsrc = repo.dirstate.copied(src) or src
792 origsrc = repo.dirstate.copied(src) or src
793 if dst == origsrc: # copying back a copy?
793 if dst == origsrc: # copying back a copy?
794 if repo.dirstate[dst] not in 'mn' and not dryrun:
794 if repo.dirstate[dst] not in 'mn' and not dryrun:
795 repo.dirstate.normallookup(dst)
795 repo.dirstate.normallookup(dst)
796 else:
796 else:
797 if repo.dirstate[origsrc] == 'a' and origsrc == src:
797 if repo.dirstate[origsrc] == 'a' and origsrc == src:
798 if not ui.quiet:
798 if not ui.quiet:
799 ui.warn(_("%s has not been committed yet, so no copy "
799 ui.warn(_("%s has not been committed yet, so no copy "
800 "data will be stored for %s.\n")
800 "data will be stored for %s.\n")
801 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
801 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
802 if repo.dirstate[dst] in '?r' and not dryrun:
802 if repo.dirstate[dst] in '?r' and not dryrun:
803 wctx.add([dst])
803 wctx.add([dst])
804 elif not dryrun:
804 elif not dryrun:
805 wctx.copy(origsrc, dst)
805 wctx.copy(origsrc, dst)
806
806
807 def readrequires(opener, supported):
807 def readrequires(opener, supported):
808 '''Reads and parses .hg/requires and checks if all entries found
808 '''Reads and parses .hg/requires and checks if all entries found
809 are in the list of supported features.'''
809 are in the list of supported features.'''
810 requirements = set(opener.read("requires").splitlines())
810 requirements = set(opener.read("requires").splitlines())
811 missings = []
811 missings = []
812 for r in requirements:
812 for r in requirements:
813 if r not in supported:
813 if r not in supported:
814 if not r or not r[0].isalnum():
814 if not r or not r[0].isalnum():
815 raise error.RequirementError(_(".hg/requires file is corrupt"))
815 raise error.RequirementError(_(".hg/requires file is corrupt"))
816 missings.append(r)
816 missings.append(r)
817 missings.sort()
817 missings.sort()
818 if missings:
818 if missings:
819 raise error.RequirementError(
819 raise error.RequirementError(
820 _("repository requires features unknown to this Mercurial: %s")
820 _("repository requires features unknown to this Mercurial: %s")
821 % " ".join(missings),
821 % " ".join(missings),
822 hint=_("see https://mercurial-scm.org/wiki/MissingRequirement"
822 hint=_("see https://mercurial-scm.org/wiki/MissingRequirement"
823 " for more information"))
823 " for more information"))
824 return requirements
824 return requirements
825
825
826 def writerequires(opener, requirements):
826 def writerequires(opener, requirements):
827 with opener('requires', 'w') as fp:
827 with opener('requires', 'w') as fp:
828 for r in sorted(requirements):
828 for r in sorted(requirements):
829 fp.write("%s\n" % r)
829 fp.write("%s\n" % r)
830
830
831 class filecachesubentry(object):
831 class filecachesubentry(object):
832 def __init__(self, path, stat):
832 def __init__(self, path, stat):
833 self.path = path
833 self.path = path
834 self.cachestat = None
834 self.cachestat = None
835 self._cacheable = None
835 self._cacheable = None
836
836
837 if stat:
837 if stat:
838 self.cachestat = filecachesubentry.stat(self.path)
838 self.cachestat = filecachesubentry.stat(self.path)
839
839
840 if self.cachestat:
840 if self.cachestat:
841 self._cacheable = self.cachestat.cacheable()
841 self._cacheable = self.cachestat.cacheable()
842 else:
842 else:
843 # None means we don't know yet
843 # None means we don't know yet
844 self._cacheable = None
844 self._cacheable = None
845
845
846 def refresh(self):
846 def refresh(self):
847 if self.cacheable():
847 if self.cacheable():
848 self.cachestat = filecachesubentry.stat(self.path)
848 self.cachestat = filecachesubentry.stat(self.path)
849
849
850 def cacheable(self):
850 def cacheable(self):
851 if self._cacheable is not None:
851 if self._cacheable is not None:
852 return self._cacheable
852 return self._cacheable
853
853
854 # we don't know yet, assume it is for now
854 # we don't know yet, assume it is for now
855 return True
855 return True
856
856
857 def changed(self):
857 def changed(self):
858 # no point in going further if we can't cache it
858 # no point in going further if we can't cache it
859 if not self.cacheable():
859 if not self.cacheable():
860 return True
860 return True
861
861
862 newstat = filecachesubentry.stat(self.path)
862 newstat = filecachesubentry.stat(self.path)
863
863
864 # we may not know if it's cacheable yet, check again now
864 # we may not know if it's cacheable yet, check again now
865 if newstat and self._cacheable is None:
865 if newstat and self._cacheable is None:
866 self._cacheable = newstat.cacheable()
866 self._cacheable = newstat.cacheable()
867
867
868 # check again
868 # check again
869 if not self._cacheable:
869 if not self._cacheable:
870 return True
870 return True
871
871
872 if self.cachestat != newstat:
872 if self.cachestat != newstat:
873 self.cachestat = newstat
873 self.cachestat = newstat
874 return True
874 return True
875 else:
875 else:
876 return False
876 return False
877
877
878 @staticmethod
878 @staticmethod
879 def stat(path):
879 def stat(path):
880 try:
880 try:
881 return util.cachestat(path)
881 return util.cachestat(path)
882 except OSError as e:
882 except OSError as e:
883 if e.errno != errno.ENOENT:
883 if e.errno != errno.ENOENT:
884 raise
884 raise
885
885
886 class filecacheentry(object):
886 class filecacheentry(object):
887 def __init__(self, paths, stat=True):
887 def __init__(self, paths, stat=True):
888 self._entries = []
888 self._entries = []
889 for path in paths:
889 for path in paths:
890 self._entries.append(filecachesubentry(path, stat))
890 self._entries.append(filecachesubentry(path, stat))
891
891
892 def changed(self):
892 def changed(self):
893 '''true if any entry has changed'''
893 '''true if any entry has changed'''
894 for entry in self._entries:
894 for entry in self._entries:
895 if entry.changed():
895 if entry.changed():
896 return True
896 return True
897 return False
897 return False
898
898
899 def refresh(self):
899 def refresh(self):
900 for entry in self._entries:
900 for entry in self._entries:
901 entry.refresh()
901 entry.refresh()
902
902
903 class filecache(object):
903 class filecache(object):
904 '''A property like decorator that tracks files under .hg/ for updates.
904 '''A property like decorator that tracks files under .hg/ for updates.
905
905
906 Records stat info when called in _filecache.
906 Records stat info when called in _filecache.
907
907
908 On subsequent calls, compares old stat info with new info, and recreates the
908 On subsequent calls, compares old stat info with new info, and recreates the
909 object when any of the files changes, updating the new stat info in
909 object when any of the files changes, updating the new stat info in
910 _filecache.
910 _filecache.
911
911
912 Mercurial either atomic renames or appends for files under .hg,
912 Mercurial either atomic renames or appends for files under .hg,
913 so to ensure the cache is reliable we need the filesystem to be able
913 so to ensure the cache is reliable we need the filesystem to be able
914 to tell us if a file has been replaced. If it can't, we fallback to
914 to tell us if a file has been replaced. If it can't, we fallback to
915 recreating the object on every call (essentially the same behavior as
915 recreating the object on every call (essentially the same behavior as
916 propertycache).
916 propertycache).
917
917
918 '''
918 '''
919 def __init__(self, *paths):
919 def __init__(self, *paths):
920 self.paths = paths
920 self.paths = paths
921
921
922 def join(self, obj, fname):
922 def join(self, obj, fname):
923 """Used to compute the runtime path of a cached file.
923 """Used to compute the runtime path of a cached file.
924
924
925 Users should subclass filecache and provide their own version of this
925 Users should subclass filecache and provide their own version of this
926 function to call the appropriate join function on 'obj' (an instance
926 function to call the appropriate join function on 'obj' (an instance
927 of the class that its member function was decorated).
927 of the class that its member function was decorated).
928 """
928 """
929 raise NotImplementedError
929 raise NotImplementedError
930
930
931 def __call__(self, func):
931 def __call__(self, func):
932 self.func = func
932 self.func = func
933 self.name = func.__name__.encode('ascii')
933 self.name = func.__name__.encode('ascii')
934 return self
934 return self
935
935
936 def __get__(self, obj, type=None):
936 def __get__(self, obj, type=None):
937 # if accessed on the class, return the descriptor itself.
937 # if accessed on the class, return the descriptor itself.
938 if obj is None:
938 if obj is None:
939 return self
939 return self
940 # do we need to check if the file changed?
940 # do we need to check if the file changed?
941 if self.name in obj.__dict__:
941 if self.name in obj.__dict__:
942 assert self.name in obj._filecache, self.name
942 assert self.name in obj._filecache, self.name
943 return obj.__dict__[self.name]
943 return obj.__dict__[self.name]
944
944
945 entry = obj._filecache.get(self.name)
945 entry = obj._filecache.get(self.name)
946
946
947 if entry:
947 if entry:
948 if entry.changed():
948 if entry.changed():
949 entry.obj = self.func(obj)
949 entry.obj = self.func(obj)
950 else:
950 else:
951 paths = [self.join(obj, path) for path in self.paths]
951 paths = [self.join(obj, path) for path in self.paths]
952
952
953 # We stat -before- creating the object so our cache doesn't lie if
953 # We stat -before- creating the object so our cache doesn't lie if
954 # a writer modified between the time we read and stat
954 # a writer modified between the time we read and stat
955 entry = filecacheentry(paths, True)
955 entry = filecacheentry(paths, True)
956 entry.obj = self.func(obj)
956 entry.obj = self.func(obj)
957
957
958 obj._filecache[self.name] = entry
958 obj._filecache[self.name] = entry
959
959
960 obj.__dict__[self.name] = entry.obj
960 obj.__dict__[self.name] = entry.obj
961 return entry.obj
961 return entry.obj
962
962
963 def __set__(self, obj, value):
963 def __set__(self, obj, value):
964 if self.name not in obj._filecache:
964 if self.name not in obj._filecache:
965 # we add an entry for the missing value because X in __dict__
965 # we add an entry for the missing value because X in __dict__
966 # implies X in _filecache
966 # implies X in _filecache
967 paths = [self.join(obj, path) for path in self.paths]
967 paths = [self.join(obj, path) for path in self.paths]
968 ce = filecacheentry(paths, False)
968 ce = filecacheentry(paths, False)
969 obj._filecache[self.name] = ce
969 obj._filecache[self.name] = ce
970 else:
970 else:
971 ce = obj._filecache[self.name]
971 ce = obj._filecache[self.name]
972
972
973 ce.obj = value # update cached copy
973 ce.obj = value # update cached copy
974 obj.__dict__[self.name] = value # update copy returned by obj.x
974 obj.__dict__[self.name] = value # update copy returned by obj.x
975
975
976 def __delete__(self, obj):
976 def __delete__(self, obj):
977 try:
977 try:
978 del obj.__dict__[self.name]
978 del obj.__dict__[self.name]
979 except KeyError:
979 except KeyError:
980 raise AttributeError(self.name)
980 raise AttributeError(self.name)
981
981
982 def _locksub(repo, lock, envvar, cmd, environ=None, *args, **kwargs):
982 def _locksub(repo, lock, envvar, cmd, environ=None, *args, **kwargs):
983 if lock is None:
983 if lock is None:
984 raise error.LockInheritanceContractViolation(
984 raise error.LockInheritanceContractViolation(
985 'lock can only be inherited while held')
985 'lock can only be inherited while held')
986 if environ is None:
986 if environ is None:
987 environ = {}
987 environ = {}
988 with lock.inherit() as locker:
988 with lock.inherit() as locker:
989 environ[envvar] = locker
989 environ[envvar] = locker
990 return repo.ui.system(cmd, environ=environ, *args, **kwargs)
990 return repo.ui.system(cmd, environ=environ, *args, **kwargs)
991
991
992 def wlocksub(repo, cmd, *args, **kwargs):
992 def wlocksub(repo, cmd, *args, **kwargs):
993 """run cmd as a subprocess that allows inheriting repo's wlock
993 """run cmd as a subprocess that allows inheriting repo's wlock
994
994
995 This can only be called while the wlock is held. This takes all the
995 This can only be called while the wlock is held. This takes all the
996 arguments that ui.system does, and returns the exit code of the
996 arguments that ui.system does, and returns the exit code of the
997 subprocess."""
997 subprocess."""
998 return _locksub(repo, repo.currentwlock(), 'HG_WLOCK_LOCKER', cmd, *args,
998 return _locksub(repo, repo.currentwlock(), 'HG_WLOCK_LOCKER', cmd, *args,
999 **kwargs)
999 **kwargs)
1000
1000
1001 def gdinitconfig(ui):
1001 def gdinitconfig(ui):
1002 """helper function to know if a repo should be created as general delta
1002 """helper function to know if a repo should be created as general delta
1003 """
1003 """
1004 # experimental config: format.generaldelta
1004 # experimental config: format.generaldelta
1005 return (ui.configbool('format', 'generaldelta')
1005 return (ui.configbool('format', 'generaldelta')
1006 or ui.configbool('format', 'usegeneraldelta'))
1006 or ui.configbool('format', 'usegeneraldelta'))
1007
1007
1008 def gddeltaconfig(ui):
1008 def gddeltaconfig(ui):
1009 """helper function to know if incoming delta should be optimised
1009 """helper function to know if incoming delta should be optimised
1010 """
1010 """
1011 # experimental config: format.generaldelta
1011 # experimental config: format.generaldelta
1012 return ui.configbool('format', 'generaldelta')
1012 return ui.configbool('format', 'generaldelta')
1013
1013
1014 class simplekeyvaluefile(object):
1014 class simplekeyvaluefile(object):
1015 """A simple file with key=value lines
1015 """A simple file with key=value lines
1016
1016
1017 Keys must be alphanumerics and start with a letter, values must not
1017 Keys must be alphanumerics and start with a letter, values must not
1018 contain '\n' characters"""
1018 contain '\n' characters"""
1019 firstlinekey = '__firstline'
1019 firstlinekey = '__firstline'
1020
1020
1021 def __init__(self, vfs, path, keys=None):
1021 def __init__(self, vfs, path, keys=None):
1022 self.vfs = vfs
1022 self.vfs = vfs
1023 self.path = path
1023 self.path = path
1024
1024
1025 def read(self, firstlinenonkeyval=False):
1025 def read(self, firstlinenonkeyval=False):
1026 """Read the contents of a simple key-value file
1026 """Read the contents of a simple key-value file
1027
1027
1028 'firstlinenonkeyval' indicates whether the first line of file should
1028 'firstlinenonkeyval' indicates whether the first line of file should
1029 be treated as a key-value pair or reuturned fully under the
1029 be treated as a key-value pair or reuturned fully under the
1030 __firstline key."""
1030 __firstline key."""
1031 lines = self.vfs.readlines(self.path)
1031 lines = self.vfs.readlines(self.path)
1032 d = {}
1032 d = {}
1033 if firstlinenonkeyval:
1033 if firstlinenonkeyval:
1034 if not lines:
1034 if not lines:
1035 e = _("empty simplekeyvalue file")
1035 e = _("empty simplekeyvalue file")
1036 raise error.CorruptedState(e)
1036 raise error.CorruptedState(e)
1037 # we don't want to include '\n' in the __firstline
1037 # we don't want to include '\n' in the __firstline
1038 d[self.firstlinekey] = lines[0][:-1]
1038 d[self.firstlinekey] = lines[0][:-1]
1039 del lines[0]
1039 del lines[0]
1040
1040
1041 try:
1041 try:
1042 # the 'if line.strip()' part prevents us from failing on empty
1042 # the 'if line.strip()' part prevents us from failing on empty
1043 # lines which only contain '\n' therefore are not skipped
1043 # lines which only contain '\n' therefore are not skipped
1044 # by 'if line'
1044 # by 'if line'
1045 updatedict = dict(line[:-1].split('=', 1) for line in lines
1045 updatedict = dict(line[:-1].split('=', 1) for line in lines
1046 if line.strip())
1046 if line.strip())
1047 if self.firstlinekey in updatedict:
1047 if self.firstlinekey in updatedict:
1048 e = _("%r can't be used as a key")
1048 e = _("%r can't be used as a key")
1049 raise error.CorruptedState(e % self.firstlinekey)
1049 raise error.CorruptedState(e % self.firstlinekey)
1050 d.update(updatedict)
1050 d.update(updatedict)
1051 except ValueError as e:
1051 except ValueError as e:
1052 raise error.CorruptedState(str(e))
1052 raise error.CorruptedState(str(e))
1053 return d
1053 return d
1054
1054
1055 def write(self, data, firstline=None):
1055 def write(self, data, firstline=None):
1056 """Write key=>value mapping to a file
1056 """Write key=>value mapping to a file
1057 data is a dict. Keys must be alphanumerical and start with a letter.
1057 data is a dict. Keys must be alphanumerical and start with a letter.
1058 Values must not contain newline characters.
1058 Values must not contain newline characters.
1059
1059
1060 If 'firstline' is not None, it is written to file before
1060 If 'firstline' is not None, it is written to file before
1061 everything else, as it is, not in a key=value form"""
1061 everything else, as it is, not in a key=value form"""
1062 lines = []
1062 lines = []
1063 if firstline is not None:
1063 if firstline is not None:
1064 lines.append('%s\n' % firstline)
1064 lines.append('%s\n' % firstline)
1065
1065
1066 for k, v in data.items():
1066 for k, v in data.items():
1067 if k == self.firstlinekey:
1067 if k == self.firstlinekey:
1068 e = "key name '%s' is reserved" % self.firstlinekey
1068 e = "key name '%s' is reserved" % self.firstlinekey
1069 raise error.ProgrammingError(e)
1069 raise error.ProgrammingError(e)
1070 if not k[0].isalpha():
1070 if not k[0].isalpha():
1071 e = "keys must start with a letter in a key-value file"
1071 e = "keys must start with a letter in a key-value file"
1072 raise error.ProgrammingError(e)
1072 raise error.ProgrammingError(e)
1073 if not k.isalnum():
1073 if not k.isalnum():
1074 e = "invalid key name in a simple key-value file"
1074 e = "invalid key name in a simple key-value file"
1075 raise error.ProgrammingError(e)
1075 raise error.ProgrammingError(e)
1076 if '\n' in v:
1076 if '\n' in v:
1077 e = "invalid value in a simple key-value file"
1077 e = "invalid value in a simple key-value file"
1078 raise error.ProgrammingError(e)
1078 raise error.ProgrammingError(e)
1079 lines.append("%s=%s\n" % (k, v))
1079 lines.append("%s=%s\n" % (k, v))
1080 with self.vfs(self.path, mode='wb', atomictemp=True) as fp:
1080 with self.vfs(self.path, mode='wb', atomictemp=True) as fp:
1081 fp.write(''.join(lines))
1081 fp.write(''.join(lines))
1082
1082
1083 _reportobsoletedsource = [
1083 _reportobsoletedsource = [
1084 'debugobsolete',
1084 'debugobsolete',
1085 'pull',
1085 'pull',
1086 'push',
1086 'push',
1087 'serve',
1087 'serve',
1088 'unbundle',
1088 'unbundle',
1089 ]
1089 ]
1090
1090
1091 def registersummarycallback(repo, otr, txnname=''):
1091 def registersummarycallback(repo, otr, txnname=''):
1092 """register a callback to issue a summary after the transaction is closed
1092 """register a callback to issue a summary after the transaction is closed
1093 """
1093 """
1094 for source in _reportobsoletedsource:
1094 for source in _reportobsoletedsource:
1095 if txnname.startswith(source):
1095 if txnname.startswith(source):
1096 reporef = weakref.ref(repo)
1096 reporef = weakref.ref(repo)
1097 def reportsummary(tr):
1097 def reportsummary(tr):
1098 """the actual callback reporting the summary"""
1098 """the actual callback reporting the summary"""
1099 repo = reporef()
1099 repo = reporef()
1100 obsoleted = obsutil.getobsoleted(repo, tr)
1100 obsoleted = obsutil.getobsoleted(repo, tr)
1101 if obsoleted:
1101 if obsoleted:
1102 repo.ui.status(_('obsoleted %i changesets\n')
1102 repo.ui.status(_('obsoleted %i changesets\n')
1103 % len(obsoleted))
1103 % len(obsoleted))
1104 otr.addpostclose('00-txnreport', reportsummary)
1104 otr.addpostclose('00-txnreport', reportsummary)
1105 break
1105 break
@@ -1,251 +1,251 b''
1 $ hg init a
1 $ hg init a
2 $ cd a
2 $ cd a
3 $ echo a > a
3 $ echo a > a
4 $ hg add -n
4 $ hg add -n
5 adding a
5 adding a
6 $ hg st
6 $ hg st
7 ? a
7 ? a
8 $ hg add
8 $ hg add
9 adding a
9 adding a
10 $ hg st
10 $ hg st
11 A a
11 A a
12 $ hg forget a
12 $ hg forget a
13 $ hg add
13 $ hg add
14 adding a
14 adding a
15 $ hg st
15 $ hg st
16 A a
16 A a
17 $ mkdir dir
17 $ mkdir dir
18 $ cd dir
18 $ cd dir
19 $ hg add ../a
19 $ hg add ../a
20 ../a already tracked!
20 ../a already tracked!
21 $ cd ..
21 $ cd ..
22
22
23 $ echo b > b
23 $ echo b > b
24 $ hg add -n b
24 $ hg add -n b
25 $ hg st
25 $ hg st
26 A a
26 A a
27 ? b
27 ? b
28 $ hg add b
28 $ hg add b
29 $ hg st
29 $ hg st
30 A a
30 A a
31 A b
31 A b
32
32
33 should fail
33 should fail
34
34
35 $ hg add b
35 $ hg add b
36 b already tracked!
36 b already tracked!
37 $ hg st
37 $ hg st
38 A a
38 A a
39 A b
39 A b
40
40
41 #if no-windows
41 #if no-windows
42 $ echo foo > con.xml
42 $ echo foo > con.xml
43 $ hg --config ui.portablefilenames=jump add con.xml
43 $ hg --config ui.portablefilenames=jump add con.xml
44 abort: ui.portablefilenames value is invalid ('jump')
44 abort: ui.portablefilenames value is invalid ('jump')
45 [255]
45 [255]
46 $ hg --config ui.portablefilenames=abort add con.xml
46 $ hg --config ui.portablefilenames=abort add con.xml
47 abort: filename contains 'con', which is reserved on Windows: 'con.xml'
47 abort: filename contains 'con', which is reserved on Windows: con.xml
48 [255]
48 [255]
49 $ hg st
49 $ hg st
50 A a
50 A a
51 A b
51 A b
52 ? con.xml
52 ? con.xml
53 $ hg add con.xml
53 $ hg add con.xml
54 warning: filename contains 'con', which is reserved on Windows: 'con.xml'
54 warning: filename contains 'con', which is reserved on Windows: con.xml
55 $ hg st
55 $ hg st
56 A a
56 A a
57 A b
57 A b
58 A con.xml
58 A con.xml
59 $ hg forget con.xml
59 $ hg forget con.xml
60 $ rm con.xml
60 $ rm con.xml
61 #endif
61 #endif
62
62
63 #if eol-in-paths
63 #if eol-in-paths
64 $ echo bla > 'hello:world'
64 $ echo bla > 'hello:world'
65 $ hg --config ui.portablefilenames=abort add
65 $ hg --config ui.portablefilenames=abort add
66 adding hello:world
66 adding hello:world
67 abort: filename contains ':', which is reserved on Windows: 'hello:world'
67 abort: filename contains ':', which is reserved on Windows: 'hello:world'
68 [255]
68 [255]
69 $ hg st
69 $ hg st
70 A a
70 A a
71 A b
71 A b
72 ? hello:world
72 ? hello:world
73 $ hg --config ui.portablefilenames=ignore add
73 $ hg --config ui.portablefilenames=ignore add
74 adding hello:world
74 adding hello:world
75 $ hg st
75 $ hg st
76 A a
76 A a
77 A b
77 A b
78 A hello:world
78 A hello:world
79 #endif
79 #endif
80
80
81 $ hg ci -m 0 --traceback
81 $ hg ci -m 0 --traceback
82
82
83 $ hg log -r "heads(. or wdir() & file('**'))"
83 $ hg log -r "heads(. or wdir() & file('**'))"
84 changeset: 0:* (glob)
84 changeset: 0:* (glob)
85 tag: tip
85 tag: tip
86 user: test
86 user: test
87 date: Thu Jan 01 00:00:00 1970 +0000
87 date: Thu Jan 01 00:00:00 1970 +0000
88 summary: 0
88 summary: 0
89
89
90 should fail
90 should fail
91
91
92 $ hg add a
92 $ hg add a
93 a already tracked!
93 a already tracked!
94
94
95 $ echo aa > a
95 $ echo aa > a
96 $ hg ci -m 1
96 $ hg ci -m 1
97 $ hg up 0
97 $ hg up 0
98 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
98 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
99 $ echo aaa > a
99 $ echo aaa > a
100 $ hg ci -m 2
100 $ hg ci -m 2
101 created new head
101 created new head
102
102
103 $ hg merge
103 $ hg merge
104 merging a
104 merging a
105 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
105 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
106 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
106 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
107 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
107 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
108 [1]
108 [1]
109 $ hg st
109 $ hg st
110 M a
110 M a
111 ? a.orig
111 ? a.orig
112
112
113 wdir doesn't cause a crash, and can be dynamically selected if dirty
113 wdir doesn't cause a crash, and can be dynamically selected if dirty
114
114
115 $ hg log -r "heads(. or wdir() & file('**'))"
115 $ hg log -r "heads(. or wdir() & file('**'))"
116 changeset: 2147483647:ffffffffffff
116 changeset: 2147483647:ffffffffffff
117 parent: 2:* (glob)
117 parent: 2:* (glob)
118 parent: 1:* (glob)
118 parent: 1:* (glob)
119 user: test
119 user: test
120 date: * (glob)
120 date: * (glob)
121
121
122 should fail
122 should fail
123
123
124 $ hg add a
124 $ hg add a
125 a already tracked!
125 a already tracked!
126 $ hg st
126 $ hg st
127 M a
127 M a
128 ? a.orig
128 ? a.orig
129 $ hg resolve -m a
129 $ hg resolve -m a
130 (no more unresolved files)
130 (no more unresolved files)
131 $ hg ci -m merge
131 $ hg ci -m merge
132
132
133 Issue683: peculiarity with hg revert of an removed then added file
133 Issue683: peculiarity with hg revert of an removed then added file
134
134
135 $ hg forget a
135 $ hg forget a
136 $ hg add a
136 $ hg add a
137 $ hg st
137 $ hg st
138 ? a.orig
138 ? a.orig
139 $ hg rm a
139 $ hg rm a
140 $ hg st
140 $ hg st
141 R a
141 R a
142 ? a.orig
142 ? a.orig
143 $ echo a > a
143 $ echo a > a
144 $ hg add a
144 $ hg add a
145 $ hg st
145 $ hg st
146 M a
146 M a
147 ? a.orig
147 ? a.orig
148
148
149 Forgotten file can be added back (as either clean or modified)
149 Forgotten file can be added back (as either clean or modified)
150
150
151 $ hg forget b
151 $ hg forget b
152 $ hg add b
152 $ hg add b
153 $ hg st -A b
153 $ hg st -A b
154 C b
154 C b
155 $ hg forget b
155 $ hg forget b
156 $ echo modified > b
156 $ echo modified > b
157 $ hg add b
157 $ hg add b
158 $ hg st -A b
158 $ hg st -A b
159 M b
159 M b
160 $ hg revert -qC b
160 $ hg revert -qC b
161
161
162 $ hg add c && echo "unexpected addition of missing file"
162 $ hg add c && echo "unexpected addition of missing file"
163 c: * (glob)
163 c: * (glob)
164 [1]
164 [1]
165 $ echo c > c
165 $ echo c > c
166 $ hg add d c && echo "unexpected addition of missing file"
166 $ hg add d c && echo "unexpected addition of missing file"
167 d: * (glob)
167 d: * (glob)
168 [1]
168 [1]
169 $ hg st
169 $ hg st
170 M a
170 M a
171 A c
171 A c
172 ? a.orig
172 ? a.orig
173 $ hg up -C
173 $ hg up -C
174 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
174 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
175
175
176 forget and get should have the right order: added but missing dir should be
176 forget and get should have the right order: added but missing dir should be
177 forgotten before file with same name is added
177 forgotten before file with same name is added
178
178
179 $ echo file d > d
179 $ echo file d > d
180 $ hg add d
180 $ hg add d
181 $ hg ci -md
181 $ hg ci -md
182 $ hg rm d
182 $ hg rm d
183 $ mkdir d
183 $ mkdir d
184 $ echo a > d/a
184 $ echo a > d/a
185 $ hg add d/a
185 $ hg add d/a
186 $ rm -r d
186 $ rm -r d
187 $ hg up -C
187 $ hg up -C
188 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
188 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
189 $ cat d
189 $ cat d
190 file d
190 file d
191
191
192 Test that adding a directory doesn't require case matching (issue4578)
192 Test that adding a directory doesn't require case matching (issue4578)
193 #if icasefs
193 #if icasefs
194 $ mkdir -p CapsDir1/CapsDir
194 $ mkdir -p CapsDir1/CapsDir
195 $ echo abc > CapsDir1/CapsDir/AbC.txt
195 $ echo abc > CapsDir1/CapsDir/AbC.txt
196 $ mkdir CapsDir1/CapsDir/SubDir
196 $ mkdir CapsDir1/CapsDir/SubDir
197 $ echo def > CapsDir1/CapsDir/SubDir/Def.txt
197 $ echo def > CapsDir1/CapsDir/SubDir/Def.txt
198
198
199 $ hg add capsdir1/capsdir
199 $ hg add capsdir1/capsdir
200 adding CapsDir1/CapsDir/AbC.txt (glob)
200 adding CapsDir1/CapsDir/AbC.txt (glob)
201 adding CapsDir1/CapsDir/SubDir/Def.txt (glob)
201 adding CapsDir1/CapsDir/SubDir/Def.txt (glob)
202
202
203 $ hg forget capsdir1/capsdir/abc.txt
203 $ hg forget capsdir1/capsdir/abc.txt
204
204
205 $ hg forget capsdir1/capsdir
205 $ hg forget capsdir1/capsdir
206 removing CapsDir1/CapsDir/SubDir/Def.txt (glob)
206 removing CapsDir1/CapsDir/SubDir/Def.txt (glob)
207
207
208 $ hg add capsdir1
208 $ hg add capsdir1
209 adding CapsDir1/CapsDir/AbC.txt (glob)
209 adding CapsDir1/CapsDir/AbC.txt (glob)
210 adding CapsDir1/CapsDir/SubDir/Def.txt (glob)
210 adding CapsDir1/CapsDir/SubDir/Def.txt (glob)
211
211
212 $ hg ci -m "AbCDef" capsdir1/capsdir
212 $ hg ci -m "AbCDef" capsdir1/capsdir
213
213
214 $ hg status -A capsdir1/capsdir
214 $ hg status -A capsdir1/capsdir
215 C CapsDir1/CapsDir/AbC.txt
215 C CapsDir1/CapsDir/AbC.txt
216 C CapsDir1/CapsDir/SubDir/Def.txt
216 C CapsDir1/CapsDir/SubDir/Def.txt
217
217
218 $ hg files capsdir1/capsdir
218 $ hg files capsdir1/capsdir
219 CapsDir1/CapsDir/AbC.txt (glob)
219 CapsDir1/CapsDir/AbC.txt (glob)
220 CapsDir1/CapsDir/SubDir/Def.txt (glob)
220 CapsDir1/CapsDir/SubDir/Def.txt (glob)
221
221
222 $ echo xyz > CapsDir1/CapsDir/SubDir/Def.txt
222 $ echo xyz > CapsDir1/CapsDir/SubDir/Def.txt
223 $ hg ci -m xyz capsdir1/capsdir/subdir/def.txt
223 $ hg ci -m xyz capsdir1/capsdir/subdir/def.txt
224
224
225 $ hg revert -r '.^' capsdir1/capsdir
225 $ hg revert -r '.^' capsdir1/capsdir
226 reverting CapsDir1/CapsDir/SubDir/Def.txt (glob)
226 reverting CapsDir1/CapsDir/SubDir/Def.txt (glob)
227
227
228 The conditional tests above mean the hash on the diff line differs on Windows
228 The conditional tests above mean the hash on the diff line differs on Windows
229 and OS X
229 and OS X
230 $ hg diff capsdir1/capsdir
230 $ hg diff capsdir1/capsdir
231 diff -r * CapsDir1/CapsDir/SubDir/Def.txt (glob)
231 diff -r * CapsDir1/CapsDir/SubDir/Def.txt (glob)
232 --- a/CapsDir1/CapsDir/SubDir/Def.txt Thu Jan 01 00:00:00 1970 +0000
232 --- a/CapsDir1/CapsDir/SubDir/Def.txt Thu Jan 01 00:00:00 1970 +0000
233 +++ b/CapsDir1/CapsDir/SubDir/Def.txt * (glob)
233 +++ b/CapsDir1/CapsDir/SubDir/Def.txt * (glob)
234 @@ -1,1 +1,1 @@
234 @@ -1,1 +1,1 @@
235 -xyz
235 -xyz
236 +def
236 +def
237
237
238 $ hg mv CapsDir1/CapsDir/abc.txt CapsDir1/CapsDir/ABC.txt
238 $ hg mv CapsDir1/CapsDir/abc.txt CapsDir1/CapsDir/ABC.txt
239 $ hg ci -m "case changing rename" CapsDir1/CapsDir/AbC.txt CapsDir1/CapsDir/ABC.txt
239 $ hg ci -m "case changing rename" CapsDir1/CapsDir/AbC.txt CapsDir1/CapsDir/ABC.txt
240
240
241 $ hg status -A capsdir1/capsdir
241 $ hg status -A capsdir1/capsdir
242 M CapsDir1/CapsDir/SubDir/Def.txt
242 M CapsDir1/CapsDir/SubDir/Def.txt
243 C CapsDir1/CapsDir/ABC.txt
243 C CapsDir1/CapsDir/ABC.txt
244
244
245 $ hg remove -f 'glob:**.txt' -X capsdir1/capsdir
245 $ hg remove -f 'glob:**.txt' -X capsdir1/capsdir
246 $ hg remove -f 'glob:**.txt' -I capsdir1/capsdir
246 $ hg remove -f 'glob:**.txt' -I capsdir1/capsdir
247 removing CapsDir1/CapsDir/ABC.txt (glob)
247 removing CapsDir1/CapsDir/ABC.txt (glob)
248 removing CapsDir1/CapsDir/SubDir/Def.txt (glob)
248 removing CapsDir1/CapsDir/SubDir/Def.txt (glob)
249 #endif
249 #endif
250
250
251 $ cd ..
251 $ cd ..
@@ -1,250 +1,250 b''
1 # enable bundle2 in advance
1 # enable bundle2 in advance
2
2
3 $ cat << EOF >> $HGRCPATH
3 $ cat << EOF >> $HGRCPATH
4 > [format]
4 > [format]
5 > usegeneraldelta=yes
5 > usegeneraldelta=yes
6 > EOF
6 > EOF
7
7
8 $ mkdir part1
8 $ mkdir part1
9 $ cd part1
9 $ cd part1
10
10
11 $ hg init
11 $ hg init
12 $ echo a > a
12 $ echo a > a
13 $ hg add a
13 $ hg add a
14 $ hg commit -m "1"
14 $ hg commit -m "1"
15 $ hg status
15 $ hg status
16 $ hg copy a b
16 $ hg copy a b
17 $ hg --config ui.portablefilenames=abort copy a con.xml
17 $ hg --config ui.portablefilenames=abort copy a con.xml
18 abort: filename contains 'con', which is reserved on Windows: 'con.xml'
18 abort: filename contains 'con', which is reserved on Windows: con.xml
19 [255]
19 [255]
20 $ hg status
20 $ hg status
21 A b
21 A b
22 $ hg sum
22 $ hg sum
23 parent: 0:c19d34741b0a tip
23 parent: 0:c19d34741b0a tip
24 1
24 1
25 branch: default
25 branch: default
26 commit: 1 copied
26 commit: 1 copied
27 update: (current)
27 update: (current)
28 phases: 1 draft
28 phases: 1 draft
29 $ hg --debug commit -m "2"
29 $ hg --debug commit -m "2"
30 committing files:
30 committing files:
31 b
31 b
32 b: copy a:b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
32 b: copy a:b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
33 committing manifest
33 committing manifest
34 committing changelog
34 committing changelog
35 updating the branch cache
35 updating the branch cache
36 committed changeset 1:93580a2c28a50a56f63526fb305067e6fbf739c4
36 committed changeset 1:93580a2c28a50a56f63526fb305067e6fbf739c4
37
37
38 we should see two history entries
38 we should see two history entries
39
39
40 $ hg history -v
40 $ hg history -v
41 changeset: 1:93580a2c28a5
41 changeset: 1:93580a2c28a5
42 tag: tip
42 tag: tip
43 user: test
43 user: test
44 date: Thu Jan 01 00:00:00 1970 +0000
44 date: Thu Jan 01 00:00:00 1970 +0000
45 files: b
45 files: b
46 description:
46 description:
47 2
47 2
48
48
49
49
50 changeset: 0:c19d34741b0a
50 changeset: 0:c19d34741b0a
51 user: test
51 user: test
52 date: Thu Jan 01 00:00:00 1970 +0000
52 date: Thu Jan 01 00:00:00 1970 +0000
53 files: a
53 files: a
54 description:
54 description:
55 1
55 1
56
56
57
57
58
58
59 we should see one log entry for a
59 we should see one log entry for a
60
60
61 $ hg log a
61 $ hg log a
62 changeset: 0:c19d34741b0a
62 changeset: 0:c19d34741b0a
63 user: test
63 user: test
64 date: Thu Jan 01 00:00:00 1970 +0000
64 date: Thu Jan 01 00:00:00 1970 +0000
65 summary: 1
65 summary: 1
66
66
67
67
68 this should show a revision linked to changeset 0
68 this should show a revision linked to changeset 0
69
69
70 $ hg debugindex a
70 $ hg debugindex a
71 rev offset length ..... linkrev nodeid p1 p2 (re)
71 rev offset length ..... linkrev nodeid p1 p2 (re)
72 0 0 3 ..... 0 b789fdd96dc2 000000000000 000000000000 (re)
72 0 0 3 ..... 0 b789fdd96dc2 000000000000 000000000000 (re)
73
73
74 we should see one log entry for b
74 we should see one log entry for b
75
75
76 $ hg log b
76 $ hg log b
77 changeset: 1:93580a2c28a5
77 changeset: 1:93580a2c28a5
78 tag: tip
78 tag: tip
79 user: test
79 user: test
80 date: Thu Jan 01 00:00:00 1970 +0000
80 date: Thu Jan 01 00:00:00 1970 +0000
81 summary: 2
81 summary: 2
82
82
83
83
84 this should show a revision linked to changeset 1
84 this should show a revision linked to changeset 1
85
85
86 $ hg debugindex b
86 $ hg debugindex b
87 rev offset length ..... linkrev nodeid p1 p2 (re)
87 rev offset length ..... linkrev nodeid p1 p2 (re)
88 0 0 65 ..... 1 37d9b5d994ea 000000000000 000000000000 (re)
88 0 0 65 ..... 1 37d9b5d994ea 000000000000 000000000000 (re)
89
89
90 this should show the rename information in the metadata
90 this should show the rename information in the metadata
91
91
92 $ hg debugdata b 0 | head -3 | tail -2
92 $ hg debugdata b 0 | head -3 | tail -2
93 copy: a
93 copy: a
94 copyrev: b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
94 copyrev: b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
95
95
96 $ md5sum.py .hg/store/data/b.i
96 $ md5sum.py .hg/store/data/b.i
97 44913824c8f5890ae218f9829535922e .hg/store/data/b.i
97 44913824c8f5890ae218f9829535922e .hg/store/data/b.i
98 $ hg cat b > bsum
98 $ hg cat b > bsum
99 $ md5sum.py bsum
99 $ md5sum.py bsum
100 60b725f10c9c85c70d97880dfe8191b3 bsum
100 60b725f10c9c85c70d97880dfe8191b3 bsum
101 $ hg cat a > asum
101 $ hg cat a > asum
102 $ md5sum.py asum
102 $ md5sum.py asum
103 60b725f10c9c85c70d97880dfe8191b3 asum
103 60b725f10c9c85c70d97880dfe8191b3 asum
104 $ hg verify
104 $ hg verify
105 checking changesets
105 checking changesets
106 checking manifests
106 checking manifests
107 crosschecking files in changesets and manifests
107 crosschecking files in changesets and manifests
108 checking files
108 checking files
109 2 files, 2 changesets, 2 total revisions
109 2 files, 2 changesets, 2 total revisions
110
110
111 $ cd ..
111 $ cd ..
112
112
113
113
114 $ mkdir part2
114 $ mkdir part2
115 $ cd part2
115 $ cd part2
116
116
117 $ hg init
117 $ hg init
118 $ echo foo > foo
118 $ echo foo > foo
119 should fail - foo is not managed
119 should fail - foo is not managed
120 $ hg mv foo bar
120 $ hg mv foo bar
121 foo: not copying - file is not managed
121 foo: not copying - file is not managed
122 abort: no files to copy
122 abort: no files to copy
123 [255]
123 [255]
124 $ hg st -A
124 $ hg st -A
125 ? foo
125 ? foo
126 $ hg add foo
126 $ hg add foo
127 dry-run; print a warning that this is not a real copy; foo is added
127 dry-run; print a warning that this is not a real copy; foo is added
128 $ hg mv --dry-run foo bar
128 $ hg mv --dry-run foo bar
129 foo has not been committed yet, so no copy data will be stored for bar.
129 foo has not been committed yet, so no copy data will be stored for bar.
130 $ hg st -A
130 $ hg st -A
131 A foo
131 A foo
132 should print a warning that this is not a real copy; bar is added
132 should print a warning that this is not a real copy; bar is added
133 $ hg mv foo bar
133 $ hg mv foo bar
134 foo has not been committed yet, so no copy data will be stored for bar.
134 foo has not been committed yet, so no copy data will be stored for bar.
135 $ hg st -A
135 $ hg st -A
136 A bar
136 A bar
137 should print a warning that this is not a real copy; foo is added
137 should print a warning that this is not a real copy; foo is added
138 $ hg cp bar foo
138 $ hg cp bar foo
139 bar has not been committed yet, so no copy data will be stored for foo.
139 bar has not been committed yet, so no copy data will be stored for foo.
140 $ hg rm -f bar
140 $ hg rm -f bar
141 $ rm bar
141 $ rm bar
142 $ hg st -A
142 $ hg st -A
143 A foo
143 A foo
144 $ hg commit -m1
144 $ hg commit -m1
145
145
146 moving a missing file
146 moving a missing file
147 $ rm foo
147 $ rm foo
148 $ hg mv foo foo3
148 $ hg mv foo foo3
149 foo: deleted in working directory
149 foo: deleted in working directory
150 foo3 does not exist!
150 foo3 does not exist!
151 $ hg up -qC .
151 $ hg up -qC .
152
152
153 copy --after to a nonexistent target filename
153 copy --after to a nonexistent target filename
154 $ hg cp -A foo dummy
154 $ hg cp -A foo dummy
155 foo: not recording copy - dummy does not exist
155 foo: not recording copy - dummy does not exist
156
156
157 dry-run; should show that foo is clean
157 dry-run; should show that foo is clean
158 $ hg copy --dry-run foo bar
158 $ hg copy --dry-run foo bar
159 $ hg st -A
159 $ hg st -A
160 C foo
160 C foo
161 should show copy
161 should show copy
162 $ hg copy foo bar
162 $ hg copy foo bar
163 $ hg st -C
163 $ hg st -C
164 A bar
164 A bar
165 foo
165 foo
166
166
167 shouldn't show copy
167 shouldn't show copy
168 $ hg commit -m2
168 $ hg commit -m2
169 $ hg st -C
169 $ hg st -C
170
170
171 should match
171 should match
172 $ hg debugindex foo
172 $ hg debugindex foo
173 rev offset length ..... linkrev nodeid p1 p2 (re)
173 rev offset length ..... linkrev nodeid p1 p2 (re)
174 0 0 5 ..... 0 2ed2a3912a0b 000000000000 000000000000 (re)
174 0 0 5 ..... 0 2ed2a3912a0b 000000000000 000000000000 (re)
175 $ hg debugrename bar
175 $ hg debugrename bar
176 bar renamed from foo:2ed2a3912a0b24502043eae84ee4b279c18b90dd
176 bar renamed from foo:2ed2a3912a0b24502043eae84ee4b279c18b90dd
177
177
178 $ echo bleah > foo
178 $ echo bleah > foo
179 $ echo quux > bar
179 $ echo quux > bar
180 $ hg commit -m3
180 $ hg commit -m3
181
181
182 should not be renamed
182 should not be renamed
183 $ hg debugrename bar
183 $ hg debugrename bar
184 bar not renamed
184 bar not renamed
185
185
186 $ hg copy -f foo bar
186 $ hg copy -f foo bar
187 should show copy
187 should show copy
188 $ hg st -C
188 $ hg st -C
189 M bar
189 M bar
190 foo
190 foo
191
191
192 XXX: filtering lfilesrepo.status() in 3.3-rc causes the copy source to not be
192 XXX: filtering lfilesrepo.status() in 3.3-rc causes the copy source to not be
193 displayed.
193 displayed.
194 $ hg st -C --config extensions.largefiles=
194 $ hg st -C --config extensions.largefiles=
195 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
195 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
196 M bar
196 M bar
197 foo
197 foo
198
198
199 $ hg commit -m3
199 $ hg commit -m3
200
200
201 should show no parents for tip
201 should show no parents for tip
202 $ hg debugindex bar
202 $ hg debugindex bar
203 rev offset length ..... linkrev nodeid p1 p2 (re)
203 rev offset length ..... linkrev nodeid p1 p2 (re)
204 0 0 69 ..... 1 7711d36246cc 000000000000 000000000000 (re)
204 0 0 69 ..... 1 7711d36246cc 000000000000 000000000000 (re)
205 1 69 6 ..... 2 bdf70a2b8d03 7711d36246cc 000000000000 (re)
205 1 69 6 ..... 2 bdf70a2b8d03 7711d36246cc 000000000000 (re)
206 2 75 71 ..... 3 b2558327ea8d 000000000000 000000000000 (re)
206 2 75 71 ..... 3 b2558327ea8d 000000000000 000000000000 (re)
207 should match
207 should match
208 $ hg debugindex foo
208 $ hg debugindex foo
209 rev offset length ..... linkrev nodeid p1 p2 (re)
209 rev offset length ..... linkrev nodeid p1 p2 (re)
210 0 0 5 ..... 0 2ed2a3912a0b 000000000000 000000000000 (re)
210 0 0 5 ..... 0 2ed2a3912a0b 000000000000 000000000000 (re)
211 1 5 7 ..... 2 dd12c926cf16 2ed2a3912a0b 000000000000 (re)
211 1 5 7 ..... 2 dd12c926cf16 2ed2a3912a0b 000000000000 (re)
212 $ hg debugrename bar
212 $ hg debugrename bar
213 bar renamed from foo:dd12c926cf165e3eb4cf87b084955cb617221c17
213 bar renamed from foo:dd12c926cf165e3eb4cf87b084955cb617221c17
214
214
215 should show no copies
215 should show no copies
216 $ hg st -C
216 $ hg st -C
217
217
218 copy --after on an added file
218 copy --after on an added file
219 $ cp bar baz
219 $ cp bar baz
220 $ hg add baz
220 $ hg add baz
221 $ hg cp -A bar baz
221 $ hg cp -A bar baz
222 $ hg st -C
222 $ hg st -C
223 A baz
223 A baz
224 bar
224 bar
225
225
226 foo was clean:
226 foo was clean:
227 $ hg st -AC foo
227 $ hg st -AC foo
228 C foo
228 C foo
229 Trying to copy on top of an existing file fails,
229 Trying to copy on top of an existing file fails,
230 $ hg copy -A bar foo
230 $ hg copy -A bar foo
231 foo: not overwriting - file already committed
231 foo: not overwriting - file already committed
232 (hg copy --after --force to replace the file by recording a copy)
232 (hg copy --after --force to replace the file by recording a copy)
233 same error without the --after, so the user doesn't have to go through
233 same error without the --after, so the user doesn't have to go through
234 two hints:
234 two hints:
235 $ hg copy bar foo
235 $ hg copy bar foo
236 foo: not overwriting - file already committed
236 foo: not overwriting - file already committed
237 (hg copy --force to replace the file by recording a copy)
237 (hg copy --force to replace the file by recording a copy)
238 but it's considered modified after a copy --after --force
238 but it's considered modified after a copy --after --force
239 $ hg copy -Af bar foo
239 $ hg copy -Af bar foo
240 $ hg st -AC foo
240 $ hg st -AC foo
241 M foo
241 M foo
242 bar
242 bar
243 The hint for a file that exists but is not in file history doesn't
243 The hint for a file that exists but is not in file history doesn't
244 mention --force:
244 mention --force:
245 $ touch xyzzy
245 $ touch xyzzy
246 $ hg cp bar xyzzy
246 $ hg cp bar xyzzy
247 xyzzy: not overwriting - file exists
247 xyzzy: not overwriting - file exists
248 (hg copy --after to record the copy)
248 (hg copy --after to record the copy)
249
249
250 $ cd ..
250 $ cd ..
@@ -1,659 +1,659 b''
1 $ hg init
1 $ hg init
2 $ mkdir d1 d1/d11 d2
2 $ mkdir d1 d1/d11 d2
3 $ echo d1/a > d1/a
3 $ echo d1/a > d1/a
4 $ echo d1/ba > d1/ba
4 $ echo d1/ba > d1/ba
5 $ echo d1/a1 > d1/d11/a1
5 $ echo d1/a1 > d1/d11/a1
6 $ echo d1/b > d1/b
6 $ echo d1/b > d1/b
7 $ echo d2/b > d2/b
7 $ echo d2/b > d2/b
8 $ hg add d1/a d1/b d1/ba d1/d11/a1 d2/b
8 $ hg add d1/a d1/b d1/ba d1/d11/a1 d2/b
9 $ hg commit -m "1"
9 $ hg commit -m "1"
10
10
11 rename a single file
11 rename a single file
12
12
13 $ hg rename d1/d11/a1 d2/c
13 $ hg rename d1/d11/a1 d2/c
14 $ hg --config ui.portablefilenames=abort rename d1/a d1/con.xml
14 $ hg --config ui.portablefilenames=abort rename d1/a d1/con.xml
15 abort: filename contains 'con', which is reserved on Windows: 'd1/con.xml'
15 abort: filename contains 'con', which is reserved on Windows: d1/con.xml
16 [255]
16 [255]
17 $ hg sum
17 $ hg sum
18 parent: 0:9b4b6e7b2c26 tip
18 parent: 0:9b4b6e7b2c26 tip
19 1
19 1
20 branch: default
20 branch: default
21 commit: 1 renamed
21 commit: 1 renamed
22 update: (current)
22 update: (current)
23 phases: 1 draft
23 phases: 1 draft
24 $ hg status -C
24 $ hg status -C
25 A d2/c
25 A d2/c
26 d1/d11/a1
26 d1/d11/a1
27 R d1/d11/a1
27 R d1/d11/a1
28 $ hg update -C
28 $ hg update -C
29 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
29 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
30 $ rm d2/c
30 $ rm d2/c
31
31
32 rename a single file using absolute paths
32 rename a single file using absolute paths
33
33
34 $ hg rename `pwd`/d1/d11/a1 `pwd`/d2/c
34 $ hg rename `pwd`/d1/d11/a1 `pwd`/d2/c
35 $ hg status -C
35 $ hg status -C
36 A d2/c
36 A d2/c
37 d1/d11/a1
37 d1/d11/a1
38 R d1/d11/a1
38 R d1/d11/a1
39 $ hg update -C
39 $ hg update -C
40 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
40 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
41 $ rm d2/c
41 $ rm d2/c
42
42
43 rename --after a single file
43 rename --after a single file
44
44
45 $ mv d1/d11/a1 d2/c
45 $ mv d1/d11/a1 d2/c
46 $ hg rename --after d1/d11/a1 d2/c
46 $ hg rename --after d1/d11/a1 d2/c
47 $ hg status -C
47 $ hg status -C
48 A d2/c
48 A d2/c
49 d1/d11/a1
49 d1/d11/a1
50 R d1/d11/a1
50 R d1/d11/a1
51 $ hg update -C
51 $ hg update -C
52 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
52 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
53 $ rm d2/c
53 $ rm d2/c
54
54
55 rename --after a single file when src and tgt already tracked
55 rename --after a single file when src and tgt already tracked
56
56
57 $ mv d1/d11/a1 d2/c
57 $ mv d1/d11/a1 d2/c
58 $ hg addrem -s 0
58 $ hg addrem -s 0
59 removing d1/d11/a1
59 removing d1/d11/a1
60 adding d2/c
60 adding d2/c
61 $ hg rename --after d1/d11/a1 d2/c
61 $ hg rename --after d1/d11/a1 d2/c
62 $ hg status -C
62 $ hg status -C
63 A d2/c
63 A d2/c
64 d1/d11/a1
64 d1/d11/a1
65 R d1/d11/a1
65 R d1/d11/a1
66 $ hg update -C
66 $ hg update -C
67 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
67 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
68 $ rm d2/c
68 $ rm d2/c
69
69
70 rename --after a single file to a nonexistent target filename
70 rename --after a single file to a nonexistent target filename
71
71
72 $ hg rename --after d1/a dummy
72 $ hg rename --after d1/a dummy
73 d1/a: not recording move - dummy does not exist (glob)
73 d1/a: not recording move - dummy does not exist (glob)
74
74
75 move a single file to an existing directory
75 move a single file to an existing directory
76
76
77 $ hg rename d1/d11/a1 d2
77 $ hg rename d1/d11/a1 d2
78 $ hg status -C
78 $ hg status -C
79 A d2/a1
79 A d2/a1
80 d1/d11/a1
80 d1/d11/a1
81 R d1/d11/a1
81 R d1/d11/a1
82 $ hg update -C
82 $ hg update -C
83 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
83 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
84 $ rm d2/a1
84 $ rm d2/a1
85
85
86 move --after a single file to an existing directory
86 move --after a single file to an existing directory
87
87
88 $ mv d1/d11/a1 d2
88 $ mv d1/d11/a1 d2
89 $ hg rename --after d1/d11/a1 d2
89 $ hg rename --after d1/d11/a1 d2
90 $ hg status -C
90 $ hg status -C
91 A d2/a1
91 A d2/a1
92 d1/d11/a1
92 d1/d11/a1
93 R d1/d11/a1
93 R d1/d11/a1
94 $ hg update -C
94 $ hg update -C
95 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
95 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
96 $ rm d2/a1
96 $ rm d2/a1
97
97
98 rename a file using a relative path
98 rename a file using a relative path
99
99
100 $ (cd d1/d11; hg rename ../../d2/b e)
100 $ (cd d1/d11; hg rename ../../d2/b e)
101 $ hg status -C
101 $ hg status -C
102 A d1/d11/e
102 A d1/d11/e
103 d2/b
103 d2/b
104 R d2/b
104 R d2/b
105 $ hg update -C
105 $ hg update -C
106 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
106 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
107 $ rm d1/d11/e
107 $ rm d1/d11/e
108
108
109 rename --after a file using a relative path
109 rename --after a file using a relative path
110
110
111 $ (cd d1/d11; mv ../../d2/b e; hg rename --after ../../d2/b e)
111 $ (cd d1/d11; mv ../../d2/b e; hg rename --after ../../d2/b e)
112 $ hg status -C
112 $ hg status -C
113 A d1/d11/e
113 A d1/d11/e
114 d2/b
114 d2/b
115 R d2/b
115 R d2/b
116 $ hg update -C
116 $ hg update -C
117 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
117 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
118 $ rm d1/d11/e
118 $ rm d1/d11/e
119
119
120 rename directory d1 as d3
120 rename directory d1 as d3
121
121
122 $ hg rename d1/ d3
122 $ hg rename d1/ d3
123 moving d1/a to d3/a (glob)
123 moving d1/a to d3/a (glob)
124 moving d1/b to d3/b (glob)
124 moving d1/b to d3/b (glob)
125 moving d1/ba to d3/ba (glob)
125 moving d1/ba to d3/ba (glob)
126 moving d1/d11/a1 to d3/d11/a1 (glob)
126 moving d1/d11/a1 to d3/d11/a1 (glob)
127 $ hg status -C
127 $ hg status -C
128 A d3/a
128 A d3/a
129 d1/a
129 d1/a
130 A d3/b
130 A d3/b
131 d1/b
131 d1/b
132 A d3/ba
132 A d3/ba
133 d1/ba
133 d1/ba
134 A d3/d11/a1
134 A d3/d11/a1
135 d1/d11/a1
135 d1/d11/a1
136 R d1/a
136 R d1/a
137 R d1/b
137 R d1/b
138 R d1/ba
138 R d1/ba
139 R d1/d11/a1
139 R d1/d11/a1
140 $ hg update -C
140 $ hg update -C
141 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
141 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
142 $ rm -rf d3
142 $ rm -rf d3
143
143
144 rename --after directory d1 as d3
144 rename --after directory d1 as d3
145
145
146 $ mv d1 d3
146 $ mv d1 d3
147 $ hg rename --after d1 d3
147 $ hg rename --after d1 d3
148 moving d1/a to d3/a (glob)
148 moving d1/a to d3/a (glob)
149 moving d1/b to d3/b (glob)
149 moving d1/b to d3/b (glob)
150 moving d1/ba to d3/ba (glob)
150 moving d1/ba to d3/ba (glob)
151 moving d1/d11/a1 to d3/d11/a1 (glob)
151 moving d1/d11/a1 to d3/d11/a1 (glob)
152 $ hg status -C
152 $ hg status -C
153 A d3/a
153 A d3/a
154 d1/a
154 d1/a
155 A d3/b
155 A d3/b
156 d1/b
156 d1/b
157 A d3/ba
157 A d3/ba
158 d1/ba
158 d1/ba
159 A d3/d11/a1
159 A d3/d11/a1
160 d1/d11/a1
160 d1/d11/a1
161 R d1/a
161 R d1/a
162 R d1/b
162 R d1/b
163 R d1/ba
163 R d1/ba
164 R d1/d11/a1
164 R d1/d11/a1
165 $ hg update -C
165 $ hg update -C
166 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
166 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
167 $ rm -rf d3
167 $ rm -rf d3
168
168
169 move a directory using a relative path
169 move a directory using a relative path
170
170
171 $ (cd d2; mkdir d3; hg rename ../d1/d11 d3)
171 $ (cd d2; mkdir d3; hg rename ../d1/d11 d3)
172 moving ../d1/d11/a1 to d3/d11/a1 (glob)
172 moving ../d1/d11/a1 to d3/d11/a1 (glob)
173 $ hg status -C
173 $ hg status -C
174 A d2/d3/d11/a1
174 A d2/d3/d11/a1
175 d1/d11/a1
175 d1/d11/a1
176 R d1/d11/a1
176 R d1/d11/a1
177 $ hg update -C
177 $ hg update -C
178 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
178 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
179 $ rm -rf d2/d3
179 $ rm -rf d2/d3
180
180
181 move --after a directory using a relative path
181 move --after a directory using a relative path
182
182
183 $ (cd d2; mkdir d3; mv ../d1/d11 d3; hg rename --after ../d1/d11 d3)
183 $ (cd d2; mkdir d3; mv ../d1/d11 d3; hg rename --after ../d1/d11 d3)
184 moving ../d1/d11/a1 to d3/d11/a1 (glob)
184 moving ../d1/d11/a1 to d3/d11/a1 (glob)
185 $ hg status -C
185 $ hg status -C
186 A d2/d3/d11/a1
186 A d2/d3/d11/a1
187 d1/d11/a1
187 d1/d11/a1
188 R d1/d11/a1
188 R d1/d11/a1
189 $ hg update -C
189 $ hg update -C
190 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
190 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
191 $ rm -rf d2/d3
191 $ rm -rf d2/d3
192
192
193 move directory d1/d11 to an existing directory d2 (removes empty d1)
193 move directory d1/d11 to an existing directory d2 (removes empty d1)
194
194
195 $ hg rename d1/d11/ d2
195 $ hg rename d1/d11/ d2
196 moving d1/d11/a1 to d2/d11/a1 (glob)
196 moving d1/d11/a1 to d2/d11/a1 (glob)
197 $ hg status -C
197 $ hg status -C
198 A d2/d11/a1
198 A d2/d11/a1
199 d1/d11/a1
199 d1/d11/a1
200 R d1/d11/a1
200 R d1/d11/a1
201 $ hg update -C
201 $ hg update -C
202 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
202 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
203 $ rm -rf d2/d11
203 $ rm -rf d2/d11
204
204
205 move directories d1 and d2 to a new directory d3
205 move directories d1 and d2 to a new directory d3
206
206
207 $ mkdir d3
207 $ mkdir d3
208 $ hg rename d1 d2 d3
208 $ hg rename d1 d2 d3
209 moving d1/a to d3/d1/a (glob)
209 moving d1/a to d3/d1/a (glob)
210 moving d1/b to d3/d1/b (glob)
210 moving d1/b to d3/d1/b (glob)
211 moving d1/ba to d3/d1/ba (glob)
211 moving d1/ba to d3/d1/ba (glob)
212 moving d1/d11/a1 to d3/d1/d11/a1 (glob)
212 moving d1/d11/a1 to d3/d1/d11/a1 (glob)
213 moving d2/b to d3/d2/b (glob)
213 moving d2/b to d3/d2/b (glob)
214 $ hg status -C
214 $ hg status -C
215 A d3/d1/a
215 A d3/d1/a
216 d1/a
216 d1/a
217 A d3/d1/b
217 A d3/d1/b
218 d1/b
218 d1/b
219 A d3/d1/ba
219 A d3/d1/ba
220 d1/ba
220 d1/ba
221 A d3/d1/d11/a1
221 A d3/d1/d11/a1
222 d1/d11/a1
222 d1/d11/a1
223 A d3/d2/b
223 A d3/d2/b
224 d2/b
224 d2/b
225 R d1/a
225 R d1/a
226 R d1/b
226 R d1/b
227 R d1/ba
227 R d1/ba
228 R d1/d11/a1
228 R d1/d11/a1
229 R d2/b
229 R d2/b
230 $ hg update -C
230 $ hg update -C
231 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
231 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
232 $ rm -rf d3
232 $ rm -rf d3
233
233
234 move --after directories d1 and d2 to a new directory d3
234 move --after directories d1 and d2 to a new directory d3
235
235
236 $ mkdir d3
236 $ mkdir d3
237 $ mv d1 d2 d3
237 $ mv d1 d2 d3
238 $ hg rename --after d1 d2 d3
238 $ hg rename --after d1 d2 d3
239 moving d1/a to d3/d1/a (glob)
239 moving d1/a to d3/d1/a (glob)
240 moving d1/b to d3/d1/b (glob)
240 moving d1/b to d3/d1/b (glob)
241 moving d1/ba to d3/d1/ba (glob)
241 moving d1/ba to d3/d1/ba (glob)
242 moving d1/d11/a1 to d3/d1/d11/a1 (glob)
242 moving d1/d11/a1 to d3/d1/d11/a1 (glob)
243 moving d2/b to d3/d2/b (glob)
243 moving d2/b to d3/d2/b (glob)
244 $ hg status -C
244 $ hg status -C
245 A d3/d1/a
245 A d3/d1/a
246 d1/a
246 d1/a
247 A d3/d1/b
247 A d3/d1/b
248 d1/b
248 d1/b
249 A d3/d1/ba
249 A d3/d1/ba
250 d1/ba
250 d1/ba
251 A d3/d1/d11/a1
251 A d3/d1/d11/a1
252 d1/d11/a1
252 d1/d11/a1
253 A d3/d2/b
253 A d3/d2/b
254 d2/b
254 d2/b
255 R d1/a
255 R d1/a
256 R d1/b
256 R d1/b
257 R d1/ba
257 R d1/ba
258 R d1/d11/a1
258 R d1/d11/a1
259 R d2/b
259 R d2/b
260 $ hg update -C
260 $ hg update -C
261 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
261 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
262 $ rm -rf d3
262 $ rm -rf d3
263
263
264 move everything under directory d1 to existing directory d2, do not
264 move everything under directory d1 to existing directory d2, do not
265 overwrite existing files (d2/b)
265 overwrite existing files (d2/b)
266
266
267 $ hg rename d1/* d2
267 $ hg rename d1/* d2
268 d2/b: not overwriting - file already committed
268 d2/b: not overwriting - file already committed
269 (hg rename --force to replace the file by recording a rename)
269 (hg rename --force to replace the file by recording a rename)
270 moving d1/d11/a1 to d2/d11/a1 (glob)
270 moving d1/d11/a1 to d2/d11/a1 (glob)
271 $ hg status -C
271 $ hg status -C
272 A d2/a
272 A d2/a
273 d1/a
273 d1/a
274 A d2/ba
274 A d2/ba
275 d1/ba
275 d1/ba
276 A d2/d11/a1
276 A d2/d11/a1
277 d1/d11/a1
277 d1/d11/a1
278 R d1/a
278 R d1/a
279 R d1/ba
279 R d1/ba
280 R d1/d11/a1
280 R d1/d11/a1
281 $ diff -u d1/b d2/b
281 $ diff -u d1/b d2/b
282 --- d1/b * (glob)
282 --- d1/b * (glob)
283 +++ d2/b * (glob)
283 +++ d2/b * (glob)
284 @@ * (glob)
284 @@ * (glob)
285 -d1/b
285 -d1/b
286 +d2/b
286 +d2/b
287 [1]
287 [1]
288 $ hg update -C
288 $ hg update -C
289 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
289 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
290 $ rm d2/a d2/ba d2/d11/a1
290 $ rm d2/a d2/ba d2/d11/a1
291
291
292 attempt to move one file into a non-existent directory
292 attempt to move one file into a non-existent directory
293
293
294 $ hg rename d1/a dx/
294 $ hg rename d1/a dx/
295 abort: destination dx/ is not a directory
295 abort: destination dx/ is not a directory
296 [255]
296 [255]
297 $ hg status -C
297 $ hg status -C
298 $ hg update -C
298 $ hg update -C
299 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
299 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
300
300
301 attempt to move potentially more than one file into a non-existent directory
301 attempt to move potentially more than one file into a non-existent directory
302
302
303 $ hg rename 'glob:d1/**' dx
303 $ hg rename 'glob:d1/**' dx
304 abort: with multiple sources, destination must be an existing directory
304 abort: with multiple sources, destination must be an existing directory
305 [255]
305 [255]
306
306
307 move every file under d1 to d2/d21 (glob)
307 move every file under d1 to d2/d21 (glob)
308
308
309 $ mkdir d2/d21
309 $ mkdir d2/d21
310 $ hg rename 'glob:d1/**' d2/d21
310 $ hg rename 'glob:d1/**' d2/d21
311 moving d1/a to d2/d21/a (glob)
311 moving d1/a to d2/d21/a (glob)
312 moving d1/b to d2/d21/b (glob)
312 moving d1/b to d2/d21/b (glob)
313 moving d1/ba to d2/d21/ba (glob)
313 moving d1/ba to d2/d21/ba (glob)
314 moving d1/d11/a1 to d2/d21/a1 (glob)
314 moving d1/d11/a1 to d2/d21/a1 (glob)
315 $ hg status -C
315 $ hg status -C
316 A d2/d21/a
316 A d2/d21/a
317 d1/a
317 d1/a
318 A d2/d21/a1
318 A d2/d21/a1
319 d1/d11/a1
319 d1/d11/a1
320 A d2/d21/b
320 A d2/d21/b
321 d1/b
321 d1/b
322 A d2/d21/ba
322 A d2/d21/ba
323 d1/ba
323 d1/ba
324 R d1/a
324 R d1/a
325 R d1/b
325 R d1/b
326 R d1/ba
326 R d1/ba
327 R d1/d11/a1
327 R d1/d11/a1
328 $ hg update -C
328 $ hg update -C
329 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
329 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
330 $ rm -rf d2/d21
330 $ rm -rf d2/d21
331
331
332 move --after some files under d1 to d2/d21 (glob)
332 move --after some files under d1 to d2/d21 (glob)
333
333
334 $ mkdir d2/d21
334 $ mkdir d2/d21
335 $ mv d1/a d1/d11/a1 d2/d21
335 $ mv d1/a d1/d11/a1 d2/d21
336 $ hg rename --after 'glob:d1/**' d2/d21
336 $ hg rename --after 'glob:d1/**' d2/d21
337 moving d1/a to d2/d21/a (glob)
337 moving d1/a to d2/d21/a (glob)
338 d1/b: not recording move - d2/d21/b does not exist (glob)
338 d1/b: not recording move - d2/d21/b does not exist (glob)
339 d1/ba: not recording move - d2/d21/ba does not exist (glob)
339 d1/ba: not recording move - d2/d21/ba does not exist (glob)
340 moving d1/d11/a1 to d2/d21/a1 (glob)
340 moving d1/d11/a1 to d2/d21/a1 (glob)
341 $ hg status -C
341 $ hg status -C
342 A d2/d21/a
342 A d2/d21/a
343 d1/a
343 d1/a
344 A d2/d21/a1
344 A d2/d21/a1
345 d1/d11/a1
345 d1/d11/a1
346 R d1/a
346 R d1/a
347 R d1/d11/a1
347 R d1/d11/a1
348 $ hg update -C
348 $ hg update -C
349 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
349 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
350 $ rm -rf d2/d21
350 $ rm -rf d2/d21
351
351
352 move every file under d1 starting with an 'a' to d2/d21 (regexp)
352 move every file under d1 starting with an 'a' to d2/d21 (regexp)
353
353
354 $ mkdir d2/d21
354 $ mkdir d2/d21
355 $ hg rename 're:d1/([^a][^/]*/)*a.*' d2/d21
355 $ hg rename 're:d1/([^a][^/]*/)*a.*' d2/d21
356 moving d1/a to d2/d21/a (glob)
356 moving d1/a to d2/d21/a (glob)
357 moving d1/d11/a1 to d2/d21/a1 (glob)
357 moving d1/d11/a1 to d2/d21/a1 (glob)
358 $ hg status -C
358 $ hg status -C
359 A d2/d21/a
359 A d2/d21/a
360 d1/a
360 d1/a
361 A d2/d21/a1
361 A d2/d21/a1
362 d1/d11/a1
362 d1/d11/a1
363 R d1/a
363 R d1/a
364 R d1/d11/a1
364 R d1/d11/a1
365 $ hg update -C
365 $ hg update -C
366 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
366 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
367 $ rm -rf d2/d21
367 $ rm -rf d2/d21
368
368
369 attempt to overwrite an existing file
369 attempt to overwrite an existing file
370
370
371 $ echo "ca" > d1/ca
371 $ echo "ca" > d1/ca
372 $ hg rename d1/ba d1/ca
372 $ hg rename d1/ba d1/ca
373 d1/ca: not overwriting - file exists
373 d1/ca: not overwriting - file exists
374 (hg rename --after to record the rename)
374 (hg rename --after to record the rename)
375 $ hg status -C
375 $ hg status -C
376 ? d1/ca
376 ? d1/ca
377 $ hg update -C
377 $ hg update -C
378 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
378 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
379
379
380 forced overwrite of an existing file
380 forced overwrite of an existing file
381
381
382 $ echo "ca" > d1/ca
382 $ echo "ca" > d1/ca
383 $ hg rename --force d1/ba d1/ca
383 $ hg rename --force d1/ba d1/ca
384 $ hg status -C
384 $ hg status -C
385 A d1/ca
385 A d1/ca
386 d1/ba
386 d1/ba
387 R d1/ba
387 R d1/ba
388 $ hg update -C
388 $ hg update -C
389 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
389 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
390 $ rm d1/ca
390 $ rm d1/ca
391
391
392 attempt to overwrite an existing broken symlink
392 attempt to overwrite an existing broken symlink
393
393
394 #if symlink
394 #if symlink
395 $ ln -s ba d1/ca
395 $ ln -s ba d1/ca
396 $ hg rename --traceback d1/ba d1/ca
396 $ hg rename --traceback d1/ba d1/ca
397 d1/ca: not overwriting - file exists
397 d1/ca: not overwriting - file exists
398 (hg rename --after to record the rename)
398 (hg rename --after to record the rename)
399 $ hg status -C
399 $ hg status -C
400 ? d1/ca
400 ? d1/ca
401 $ hg update -C
401 $ hg update -C
402 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
402 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
403 $ rm d1/ca
403 $ rm d1/ca
404
404
405 replace a symlink with a file
405 replace a symlink with a file
406
406
407 $ ln -s ba d1/ca
407 $ ln -s ba d1/ca
408 $ hg rename --force d1/ba d1/ca
408 $ hg rename --force d1/ba d1/ca
409 $ hg status -C
409 $ hg status -C
410 A d1/ca
410 A d1/ca
411 d1/ba
411 d1/ba
412 R d1/ba
412 R d1/ba
413 $ hg update -C
413 $ hg update -C
414 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
414 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
415 $ rm d1/ca
415 $ rm d1/ca
416 #endif
416 #endif
417
417
418 do not copy more than one source file to the same destination file
418 do not copy more than one source file to the same destination file
419
419
420 $ mkdir d3
420 $ mkdir d3
421 $ hg rename d1/* d2/* d3
421 $ hg rename d1/* d2/* d3
422 moving d1/d11/a1 to d3/d11/a1 (glob)
422 moving d1/d11/a1 to d3/d11/a1 (glob)
423 d3/b: not overwriting - d2/b collides with d1/b
423 d3/b: not overwriting - d2/b collides with d1/b
424 $ hg status -C
424 $ hg status -C
425 A d3/a
425 A d3/a
426 d1/a
426 d1/a
427 A d3/b
427 A d3/b
428 d1/b
428 d1/b
429 A d3/ba
429 A d3/ba
430 d1/ba
430 d1/ba
431 A d3/d11/a1
431 A d3/d11/a1
432 d1/d11/a1
432 d1/d11/a1
433 R d1/a
433 R d1/a
434 R d1/b
434 R d1/b
435 R d1/ba
435 R d1/ba
436 R d1/d11/a1
436 R d1/d11/a1
437 $ hg update -C
437 $ hg update -C
438 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
438 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
439 $ rm -rf d3
439 $ rm -rf d3
440
440
441 move a whole subtree with "hg rename ."
441 move a whole subtree with "hg rename ."
442
442
443 $ mkdir d3
443 $ mkdir d3
444 $ (cd d1; hg rename . ../d3)
444 $ (cd d1; hg rename . ../d3)
445 moving a to ../d3/d1/a
445 moving a to ../d3/d1/a
446 moving b to ../d3/d1/b
446 moving b to ../d3/d1/b
447 moving ba to ../d3/d1/ba
447 moving ba to ../d3/d1/ba
448 moving d11/a1 to ../d3/d1/d11/a1 (glob)
448 moving d11/a1 to ../d3/d1/d11/a1 (glob)
449 $ hg status -C
449 $ hg status -C
450 A d3/d1/a
450 A d3/d1/a
451 d1/a
451 d1/a
452 A d3/d1/b
452 A d3/d1/b
453 d1/b
453 d1/b
454 A d3/d1/ba
454 A d3/d1/ba
455 d1/ba
455 d1/ba
456 A d3/d1/d11/a1
456 A d3/d1/d11/a1
457 d1/d11/a1
457 d1/d11/a1
458 R d1/a
458 R d1/a
459 R d1/b
459 R d1/b
460 R d1/ba
460 R d1/ba
461 R d1/d11/a1
461 R d1/d11/a1
462 $ hg update -C
462 $ hg update -C
463 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
463 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
464 $ rm -rf d3
464 $ rm -rf d3
465
465
466 move a whole subtree with "hg rename --after ."
466 move a whole subtree with "hg rename --after ."
467
467
468 $ mkdir d3
468 $ mkdir d3
469 $ mv d1/* d3
469 $ mv d1/* d3
470 $ (cd d1; hg rename --after . ../d3)
470 $ (cd d1; hg rename --after . ../d3)
471 moving a to ../d3/a
471 moving a to ../d3/a
472 moving b to ../d3/b
472 moving b to ../d3/b
473 moving ba to ../d3/ba
473 moving ba to ../d3/ba
474 moving d11/a1 to ../d3/d11/a1 (glob)
474 moving d11/a1 to ../d3/d11/a1 (glob)
475 $ hg status -C
475 $ hg status -C
476 A d3/a
476 A d3/a
477 d1/a
477 d1/a
478 A d3/b
478 A d3/b
479 d1/b
479 d1/b
480 A d3/ba
480 A d3/ba
481 d1/ba
481 d1/ba
482 A d3/d11/a1
482 A d3/d11/a1
483 d1/d11/a1
483 d1/d11/a1
484 R d1/a
484 R d1/a
485 R d1/b
485 R d1/b
486 R d1/ba
486 R d1/ba
487 R d1/d11/a1
487 R d1/d11/a1
488 $ hg update -C
488 $ hg update -C
489 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
489 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
490 $ rm -rf d3
490 $ rm -rf d3
491
491
492 move the parent tree with "hg rename .."
492 move the parent tree with "hg rename .."
493
493
494 $ (cd d1/d11; hg rename .. ../../d3)
494 $ (cd d1/d11; hg rename .. ../../d3)
495 moving ../a to ../../d3/a (glob)
495 moving ../a to ../../d3/a (glob)
496 moving ../b to ../../d3/b (glob)
496 moving ../b to ../../d3/b (glob)
497 moving ../ba to ../../d3/ba (glob)
497 moving ../ba to ../../d3/ba (glob)
498 moving a1 to ../../d3/d11/a1
498 moving a1 to ../../d3/d11/a1
499 $ hg status -C
499 $ hg status -C
500 A d3/a
500 A d3/a
501 d1/a
501 d1/a
502 A d3/b
502 A d3/b
503 d1/b
503 d1/b
504 A d3/ba
504 A d3/ba
505 d1/ba
505 d1/ba
506 A d3/d11/a1
506 A d3/d11/a1
507 d1/d11/a1
507 d1/d11/a1
508 R d1/a
508 R d1/a
509 R d1/b
509 R d1/b
510 R d1/ba
510 R d1/ba
511 R d1/d11/a1
511 R d1/d11/a1
512 $ hg update -C
512 $ hg update -C
513 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
513 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
514 $ rm -rf d3
514 $ rm -rf d3
515
515
516 skip removed files
516 skip removed files
517
517
518 $ hg remove d1/b
518 $ hg remove d1/b
519 $ hg rename d1 d3
519 $ hg rename d1 d3
520 moving d1/a to d3/a (glob)
520 moving d1/a to d3/a (glob)
521 moving d1/ba to d3/ba (glob)
521 moving d1/ba to d3/ba (glob)
522 moving d1/d11/a1 to d3/d11/a1 (glob)
522 moving d1/d11/a1 to d3/d11/a1 (glob)
523 $ hg status -C
523 $ hg status -C
524 A d3/a
524 A d3/a
525 d1/a
525 d1/a
526 A d3/ba
526 A d3/ba
527 d1/ba
527 d1/ba
528 A d3/d11/a1
528 A d3/d11/a1
529 d1/d11/a1
529 d1/d11/a1
530 R d1/a
530 R d1/a
531 R d1/b
531 R d1/b
532 R d1/ba
532 R d1/ba
533 R d1/d11/a1
533 R d1/d11/a1
534 $ hg update -C
534 $ hg update -C
535 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
535 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
536 $ rm -rf d3
536 $ rm -rf d3
537
537
538 transitive rename
538 transitive rename
539
539
540 $ hg rename d1/b d1/bb
540 $ hg rename d1/b d1/bb
541 $ hg rename d1/bb d1/bc
541 $ hg rename d1/bb d1/bc
542 $ hg status -C
542 $ hg status -C
543 A d1/bc
543 A d1/bc
544 d1/b
544 d1/b
545 R d1/b
545 R d1/b
546 $ hg update -C
546 $ hg update -C
547 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
547 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
548 $ rm d1/bc
548 $ rm d1/bc
549
549
550 transitive rename --after
550 transitive rename --after
551
551
552 $ hg rename d1/b d1/bb
552 $ hg rename d1/b d1/bb
553 $ mv d1/bb d1/bc
553 $ mv d1/bb d1/bc
554 $ hg rename --after d1/bb d1/bc
554 $ hg rename --after d1/bb d1/bc
555 $ hg status -C
555 $ hg status -C
556 A d1/bc
556 A d1/bc
557 d1/b
557 d1/b
558 R d1/b
558 R d1/b
559 $ hg update -C
559 $ hg update -C
560 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
560 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
561 $ rm d1/bc
561 $ rm d1/bc
562
562
563 $ echo "# idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)"
563 $ echo "# idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)"
564 # idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)
564 # idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)
565 $ hg rename d1/b d1/bb
565 $ hg rename d1/b d1/bb
566 $ echo "some stuff added to d1/bb" >> d1/bb
566 $ echo "some stuff added to d1/bb" >> d1/bb
567 $ hg rename d1/bb d1/b
567 $ hg rename d1/bb d1/b
568 $ hg status -C
568 $ hg status -C
569 M d1/b
569 M d1/b
570 $ hg update -C
570 $ hg update -C
571 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
571 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
572
572
573 overwriting with renames (issue1959)
573 overwriting with renames (issue1959)
574
574
575 $ hg rename d1/a d1/c
575 $ hg rename d1/a d1/c
576 $ hg rename d1/b d1/a
576 $ hg rename d1/b d1/a
577 $ hg status -C
577 $ hg status -C
578 M d1/a
578 M d1/a
579 d1/b
579 d1/b
580 A d1/c
580 A d1/c
581 d1/a
581 d1/a
582 R d1/b
582 R d1/b
583 $ hg diff --git
583 $ hg diff --git
584 diff --git a/d1/a b/d1/a
584 diff --git a/d1/a b/d1/a
585 --- a/d1/a
585 --- a/d1/a
586 +++ b/d1/a
586 +++ b/d1/a
587 @@ -1,1 +1,1 @@
587 @@ -1,1 +1,1 @@
588 -d1/a
588 -d1/a
589 +d1/b
589 +d1/b
590 diff --git a/d1/b b/d1/b
590 diff --git a/d1/b b/d1/b
591 deleted file mode 100644
591 deleted file mode 100644
592 --- a/d1/b
592 --- a/d1/b
593 +++ /dev/null
593 +++ /dev/null
594 @@ -1,1 +0,0 @@
594 @@ -1,1 +0,0 @@
595 -d1/b
595 -d1/b
596 diff --git a/d1/a b/d1/c
596 diff --git a/d1/a b/d1/c
597 copy from d1/a
597 copy from d1/a
598 copy to d1/c
598 copy to d1/c
599 $ hg update -C
599 $ hg update -C
600 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
600 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
601 $ rm d1/c # The file was marked as added, so 'hg update' action was 'forget'
601 $ rm d1/c # The file was marked as added, so 'hg update' action was 'forget'
602
602
603 check illegal path components
603 check illegal path components
604
604
605 $ hg rename d1/d11/a1 .hg/foo
605 $ hg rename d1/d11/a1 .hg/foo
606 abort: path contains illegal component: .hg/foo (glob)
606 abort: path contains illegal component: .hg/foo (glob)
607 [255]
607 [255]
608 $ hg status -C
608 $ hg status -C
609 $ hg rename d1/d11/a1 ../foo
609 $ hg rename d1/d11/a1 ../foo
610 abort: ../foo not under root '$TESTTMP'
610 abort: ../foo not under root '$TESTTMP'
611 [255]
611 [255]
612 $ hg status -C
612 $ hg status -C
613
613
614 $ mv d1/d11/a1 .hg/foo
614 $ mv d1/d11/a1 .hg/foo
615 $ hg rename --after d1/d11/a1 .hg/foo
615 $ hg rename --after d1/d11/a1 .hg/foo
616 abort: path contains illegal component: .hg/foo (glob)
616 abort: path contains illegal component: .hg/foo (glob)
617 [255]
617 [255]
618 $ hg status -C
618 $ hg status -C
619 ! d1/d11/a1
619 ! d1/d11/a1
620 $ hg update -C
620 $ hg update -C
621 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
621 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
622 $ rm .hg/foo
622 $ rm .hg/foo
623
623
624 $ hg rename d1/d11/a1 .hg
624 $ hg rename d1/d11/a1 .hg
625 abort: path contains illegal component: .hg/a1 (glob)
625 abort: path contains illegal component: .hg/a1 (glob)
626 [255]
626 [255]
627 $ hg --config extensions.largefiles= rename d1/d11/a1 .hg
627 $ hg --config extensions.largefiles= rename d1/d11/a1 .hg
628 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
628 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
629 abort: path contains illegal component: .hg/a1 (glob)
629 abort: path contains illegal component: .hg/a1 (glob)
630 [255]
630 [255]
631 $ hg status -C
631 $ hg status -C
632 $ hg rename d1/d11/a1 ..
632 $ hg rename d1/d11/a1 ..
633 abort: ../a1 not under root '$TESTTMP' (glob)
633 abort: ../a1 not under root '$TESTTMP' (glob)
634 [255]
634 [255]
635 $ hg --config extensions.largefiles= rename d1/d11/a1 ..
635 $ hg --config extensions.largefiles= rename d1/d11/a1 ..
636 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
636 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
637 abort: ../a1 not under root '$TESTTMP' (glob)
637 abort: ../a1 not under root '$TESTTMP' (glob)
638 [255]
638 [255]
639 $ hg status -C
639 $ hg status -C
640
640
641 $ mv d1/d11/a1 .hg
641 $ mv d1/d11/a1 .hg
642 $ hg rename --after d1/d11/a1 .hg
642 $ hg rename --after d1/d11/a1 .hg
643 abort: path contains illegal component: .hg/a1 (glob)
643 abort: path contains illegal component: .hg/a1 (glob)
644 [255]
644 [255]
645 $ hg status -C
645 $ hg status -C
646 ! d1/d11/a1
646 ! d1/d11/a1
647 $ hg update -C
647 $ hg update -C
648 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
648 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
649 $ rm .hg/a1
649 $ rm .hg/a1
650
650
651 $ (cd d1/d11; hg rename ../../d2/b ../../.hg/foo)
651 $ (cd d1/d11; hg rename ../../d2/b ../../.hg/foo)
652 abort: path contains illegal component: .hg/foo (glob)
652 abort: path contains illegal component: .hg/foo (glob)
653 [255]
653 [255]
654 $ hg status -C
654 $ hg status -C
655 $ (cd d1/d11; hg rename ../../d2/b ../../../foo)
655 $ (cd d1/d11; hg rename ../../d2/b ../../../foo)
656 abort: ../../../foo not under root '$TESTTMP'
656 abort: ../../../foo not under root '$TESTTMP'
657 [255]
657 [255]
658 $ hg status -C
658 $ hg status -C
659
659
General Comments 0
You need to be logged in to leave comments. Login now