##// END OF EJS Templates
revpair: restrict odd-range handling to top-level x:y expression (issue4774)...
Yuya Nishihara -
r26020:cc3a30ff default
parent child Browse files
Show More
@@ -1,1123 +1,1127 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 i18n import _
8 from i18n import _
9 from mercurial.node import wdirrev
9 from mercurial.node import wdirrev
10 import util, error, osutil, revset, similar, encoding, phases
10 import util, error, osutil, revset, similar, encoding, phases
11 import pathutil
11 import pathutil
12 import match as matchmod
12 import match as matchmod
13 import os, errno, re, glob, tempfile, shutil, stat
13 import os, errno, re, glob, tempfile, shutil, stat
14
14
15 if os.name == 'nt':
15 if os.name == 'nt':
16 import scmwindows as scmplatform
16 import scmwindows as scmplatform
17 else:
17 else:
18 import scmposix as scmplatform
18 import scmposix as scmplatform
19
19
20 systemrcpath = scmplatform.systemrcpath
20 systemrcpath = scmplatform.systemrcpath
21 userrcpath = scmplatform.userrcpath
21 userrcpath = scmplatform.userrcpath
22
22
23 class status(tuple):
23 class status(tuple):
24 '''Named tuple with a list of files per status. The 'deleted', 'unknown'
24 '''Named tuple with a list of files per status. The 'deleted', 'unknown'
25 and 'ignored' properties are only relevant to the working copy.
25 and 'ignored' properties are only relevant to the working copy.
26 '''
26 '''
27
27
28 __slots__ = ()
28 __slots__ = ()
29
29
30 def __new__(cls, modified, added, removed, deleted, unknown, ignored,
30 def __new__(cls, modified, added, removed, deleted, unknown, ignored,
31 clean):
31 clean):
32 return tuple.__new__(cls, (modified, added, removed, deleted, unknown,
32 return tuple.__new__(cls, (modified, added, removed, deleted, unknown,
33 ignored, clean))
33 ignored, clean))
34
34
35 @property
35 @property
36 def modified(self):
36 def modified(self):
37 '''files that have been modified'''
37 '''files that have been modified'''
38 return self[0]
38 return self[0]
39
39
40 @property
40 @property
41 def added(self):
41 def added(self):
42 '''files that have been added'''
42 '''files that have been added'''
43 return self[1]
43 return self[1]
44
44
45 @property
45 @property
46 def removed(self):
46 def removed(self):
47 '''files that have been removed'''
47 '''files that have been removed'''
48 return self[2]
48 return self[2]
49
49
50 @property
50 @property
51 def deleted(self):
51 def deleted(self):
52 '''files that are in the dirstate, but have been deleted from the
52 '''files that are in the dirstate, but have been deleted from the
53 working copy (aka "missing")
53 working copy (aka "missing")
54 '''
54 '''
55 return self[3]
55 return self[3]
56
56
57 @property
57 @property
58 def unknown(self):
58 def unknown(self):
59 '''files not in the dirstate that are not ignored'''
59 '''files not in the dirstate that are not ignored'''
60 return self[4]
60 return self[4]
61
61
62 @property
62 @property
63 def ignored(self):
63 def ignored(self):
64 '''files not in the dirstate that are ignored (by _dirignore())'''
64 '''files not in the dirstate that are ignored (by _dirignore())'''
65 return self[5]
65 return self[5]
66
66
67 @property
67 @property
68 def clean(self):
68 def clean(self):
69 '''files that have not been modified'''
69 '''files that have not been modified'''
70 return self[6]
70 return self[6]
71
71
72 def __repr__(self, *args, **kwargs):
72 def __repr__(self, *args, **kwargs):
73 return (('<status modified=%r, added=%r, removed=%r, deleted=%r, '
73 return (('<status modified=%r, added=%r, removed=%r, deleted=%r, '
74 'unknown=%r, ignored=%r, clean=%r>') % self)
74 'unknown=%r, ignored=%r, clean=%r>') % self)
75
75
76 def itersubrepos(ctx1, ctx2):
76 def itersubrepos(ctx1, ctx2):
77 """find subrepos in ctx1 or ctx2"""
77 """find subrepos in ctx1 or ctx2"""
78 # Create a (subpath, ctx) mapping where we prefer subpaths from
78 # Create a (subpath, ctx) mapping where we prefer subpaths from
79 # ctx1. The subpaths from ctx2 are important when the .hgsub file
79 # ctx1. The subpaths from ctx2 are important when the .hgsub file
80 # has been modified (in ctx2) but not yet committed (in ctx1).
80 # has been modified (in ctx2) but not yet committed (in ctx1).
81 subpaths = dict.fromkeys(ctx2.substate, ctx2)
81 subpaths = dict.fromkeys(ctx2.substate, ctx2)
82 subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
82 subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
83
83
84 missing = set()
84 missing = set()
85
85
86 for subpath in ctx2.substate:
86 for subpath in ctx2.substate:
87 if subpath not in ctx1.substate:
87 if subpath not in ctx1.substate:
88 del subpaths[subpath]
88 del subpaths[subpath]
89 missing.add(subpath)
89 missing.add(subpath)
90
90
91 for subpath, ctx in sorted(subpaths.iteritems()):
91 for subpath, ctx in sorted(subpaths.iteritems()):
92 yield subpath, ctx.sub(subpath)
92 yield subpath, ctx.sub(subpath)
93
93
94 # Yield an empty subrepo based on ctx1 for anything only in ctx2. That way,
94 # Yield an empty subrepo based on ctx1 for anything only in ctx2. That way,
95 # status and diff will have an accurate result when it does
95 # status and diff will have an accurate result when it does
96 # 'sub.{status|diff}(rev2)'. Otherwise, the ctx2 subrepo is compared
96 # 'sub.{status|diff}(rev2)'. Otherwise, the ctx2 subrepo is compared
97 # against itself.
97 # against itself.
98 for subpath in missing:
98 for subpath in missing:
99 yield subpath, ctx2.nullsub(subpath, ctx1)
99 yield subpath, ctx2.nullsub(subpath, ctx1)
100
100
101 def nochangesfound(ui, repo, excluded=None):
101 def nochangesfound(ui, repo, excluded=None):
102 '''Report no changes for push/pull, excluded is None or a list of
102 '''Report no changes for push/pull, excluded is None or a list of
103 nodes excluded from the push/pull.
103 nodes excluded from the push/pull.
104 '''
104 '''
105 secretlist = []
105 secretlist = []
106 if excluded:
106 if excluded:
107 for n in excluded:
107 for n in excluded:
108 if n not in repo:
108 if n not in repo:
109 # discovery should not have included the filtered revision,
109 # discovery should not have included the filtered revision,
110 # we have to explicitly exclude it until discovery is cleanup.
110 # we have to explicitly exclude it until discovery is cleanup.
111 continue
111 continue
112 ctx = repo[n]
112 ctx = repo[n]
113 if ctx.phase() >= phases.secret and not ctx.extinct():
113 if ctx.phase() >= phases.secret and not ctx.extinct():
114 secretlist.append(n)
114 secretlist.append(n)
115
115
116 if secretlist:
116 if secretlist:
117 ui.status(_("no changes found (ignored %d secret changesets)\n")
117 ui.status(_("no changes found (ignored %d secret changesets)\n")
118 % len(secretlist))
118 % len(secretlist))
119 else:
119 else:
120 ui.status(_("no changes found\n"))
120 ui.status(_("no changes found\n"))
121
121
122 def checknewlabel(repo, lbl, kind):
122 def checknewlabel(repo, lbl, kind):
123 # Do not use the "kind" parameter in ui output.
123 # Do not use the "kind" parameter in ui output.
124 # It makes strings difficult to translate.
124 # It makes strings difficult to translate.
125 if lbl in ['tip', '.', 'null']:
125 if lbl in ['tip', '.', 'null']:
126 raise util.Abort(_("the name '%s' is reserved") % lbl)
126 raise util.Abort(_("the name '%s' is reserved") % lbl)
127 for c in (':', '\0', '\n', '\r'):
127 for c in (':', '\0', '\n', '\r'):
128 if c in lbl:
128 if c in lbl:
129 raise util.Abort(_("%r cannot be used in a name") % c)
129 raise util.Abort(_("%r cannot be used in a name") % c)
130 try:
130 try:
131 int(lbl)
131 int(lbl)
132 raise util.Abort(_("cannot use an integer as a name"))
132 raise util.Abort(_("cannot use an integer as a name"))
133 except ValueError:
133 except ValueError:
134 pass
134 pass
135
135
136 def checkfilename(f):
136 def checkfilename(f):
137 '''Check that the filename f is an acceptable filename for a tracked file'''
137 '''Check that the filename f is an acceptable filename for a tracked file'''
138 if '\r' in f or '\n' in f:
138 if '\r' in f or '\n' in f:
139 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
139 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
140
140
141 def checkportable(ui, f):
141 def checkportable(ui, f):
142 '''Check if filename f is portable and warn or abort depending on config'''
142 '''Check if filename f is portable and warn or abort depending on config'''
143 checkfilename(f)
143 checkfilename(f)
144 abort, warn = checkportabilityalert(ui)
144 abort, warn = checkportabilityalert(ui)
145 if abort or warn:
145 if abort or warn:
146 msg = util.checkwinfilename(f)
146 msg = util.checkwinfilename(f)
147 if msg:
147 if msg:
148 msg = "%s: %r" % (msg, f)
148 msg = "%s: %r" % (msg, f)
149 if abort:
149 if abort:
150 raise util.Abort(msg)
150 raise util.Abort(msg)
151 ui.warn(_("warning: %s\n") % msg)
151 ui.warn(_("warning: %s\n") % msg)
152
152
153 def checkportabilityalert(ui):
153 def checkportabilityalert(ui):
154 '''check if the user's config requests nothing, a warning, or abort for
154 '''check if the user's config requests nothing, a warning, or abort for
155 non-portable filenames'''
155 non-portable filenames'''
156 val = ui.config('ui', 'portablefilenames', 'warn')
156 val = ui.config('ui', 'portablefilenames', 'warn')
157 lval = val.lower()
157 lval = val.lower()
158 bval = util.parsebool(val)
158 bval = util.parsebool(val)
159 abort = os.name == 'nt' or lval == 'abort'
159 abort = os.name == 'nt' or lval == 'abort'
160 warn = bval or lval == 'warn'
160 warn = bval or lval == 'warn'
161 if bval is None and not (warn or abort or lval == 'ignore'):
161 if bval is None and not (warn or abort or lval == 'ignore'):
162 raise error.ConfigError(
162 raise error.ConfigError(
163 _("ui.portablefilenames value is invalid ('%s')") % val)
163 _("ui.portablefilenames value is invalid ('%s')") % val)
164 return abort, warn
164 return abort, warn
165
165
166 class casecollisionauditor(object):
166 class casecollisionauditor(object):
167 def __init__(self, ui, abort, dirstate):
167 def __init__(self, ui, abort, dirstate):
168 self._ui = ui
168 self._ui = ui
169 self._abort = abort
169 self._abort = abort
170 allfiles = '\0'.join(dirstate._map)
170 allfiles = '\0'.join(dirstate._map)
171 self._loweredfiles = set(encoding.lower(allfiles).split('\0'))
171 self._loweredfiles = set(encoding.lower(allfiles).split('\0'))
172 self._dirstate = dirstate
172 self._dirstate = dirstate
173 # The purpose of _newfiles is so that we don't complain about
173 # The purpose of _newfiles is so that we don't complain about
174 # case collisions if someone were to call this object with the
174 # case collisions if someone were to call this object with the
175 # same filename twice.
175 # same filename twice.
176 self._newfiles = set()
176 self._newfiles = set()
177
177
178 def __call__(self, f):
178 def __call__(self, f):
179 if f in self._newfiles:
179 if f in self._newfiles:
180 return
180 return
181 fl = encoding.lower(f)
181 fl = encoding.lower(f)
182 if fl in self._loweredfiles and f not in self._dirstate:
182 if fl in self._loweredfiles and f not in self._dirstate:
183 msg = _('possible case-folding collision for %s') % f
183 msg = _('possible case-folding collision for %s') % f
184 if self._abort:
184 if self._abort:
185 raise util.Abort(msg)
185 raise util.Abort(msg)
186 self._ui.warn(_("warning: %s\n") % msg)
186 self._ui.warn(_("warning: %s\n") % msg)
187 self._loweredfiles.add(fl)
187 self._loweredfiles.add(fl)
188 self._newfiles.add(f)
188 self._newfiles.add(f)
189
189
190 def filteredhash(repo, maxrev):
190 def filteredhash(repo, maxrev):
191 """build hash of filtered revisions in the current repoview.
191 """build hash of filtered revisions in the current repoview.
192
192
193 Multiple caches perform up-to-date validation by checking that the
193 Multiple caches perform up-to-date validation by checking that the
194 tiprev and tipnode stored in the cache file match the current repository.
194 tiprev and tipnode stored in the cache file match the current repository.
195 However, this is not sufficient for validating repoviews because the set
195 However, this is not sufficient for validating repoviews because the set
196 of revisions in the view may change without the repository tiprev and
196 of revisions in the view may change without the repository tiprev and
197 tipnode changing.
197 tipnode changing.
198
198
199 This function hashes all the revs filtered from the view and returns
199 This function hashes all the revs filtered from the view and returns
200 that SHA-1 digest.
200 that SHA-1 digest.
201 """
201 """
202 cl = repo.changelog
202 cl = repo.changelog
203 if not cl.filteredrevs:
203 if not cl.filteredrevs:
204 return None
204 return None
205 key = None
205 key = None
206 revs = sorted(r for r in cl.filteredrevs if r <= maxrev)
206 revs = sorted(r for r in cl.filteredrevs if r <= maxrev)
207 if revs:
207 if revs:
208 s = util.sha1()
208 s = util.sha1()
209 for rev in revs:
209 for rev in revs:
210 s.update('%s;' % rev)
210 s.update('%s;' % rev)
211 key = s.digest()
211 key = s.digest()
212 return key
212 return key
213
213
214 class abstractvfs(object):
214 class abstractvfs(object):
215 """Abstract base class; cannot be instantiated"""
215 """Abstract base class; cannot be instantiated"""
216
216
217 def __init__(self, *args, **kwargs):
217 def __init__(self, *args, **kwargs):
218 '''Prevent instantiation; don't call this from subclasses.'''
218 '''Prevent instantiation; don't call this from subclasses.'''
219 raise NotImplementedError('attempted instantiating ' + str(type(self)))
219 raise NotImplementedError('attempted instantiating ' + str(type(self)))
220
220
221 def tryread(self, path):
221 def tryread(self, path):
222 '''gracefully return an empty string for missing files'''
222 '''gracefully return an empty string for missing files'''
223 try:
223 try:
224 return self.read(path)
224 return self.read(path)
225 except IOError as inst:
225 except IOError as inst:
226 if inst.errno != errno.ENOENT:
226 if inst.errno != errno.ENOENT:
227 raise
227 raise
228 return ""
228 return ""
229
229
230 def tryreadlines(self, path, mode='rb'):
230 def tryreadlines(self, path, mode='rb'):
231 '''gracefully return an empty array for missing files'''
231 '''gracefully return an empty array for missing files'''
232 try:
232 try:
233 return self.readlines(path, mode=mode)
233 return self.readlines(path, mode=mode)
234 except IOError as inst:
234 except IOError as inst:
235 if inst.errno != errno.ENOENT:
235 if inst.errno != errno.ENOENT:
236 raise
236 raise
237 return []
237 return []
238
238
239 def open(self, path, mode="r", text=False, atomictemp=False,
239 def open(self, path, mode="r", text=False, atomictemp=False,
240 notindexed=False):
240 notindexed=False):
241 '''Open ``path`` file, which is relative to vfs root.
241 '''Open ``path`` file, which is relative to vfs root.
242
242
243 Newly created directories are marked as "not to be indexed by
243 Newly created directories are marked as "not to be indexed by
244 the content indexing service", if ``notindexed`` is specified
244 the content indexing service", if ``notindexed`` is specified
245 for "write" mode access.
245 for "write" mode access.
246 '''
246 '''
247 self.open = self.__call__
247 self.open = self.__call__
248 return self.__call__(path, mode, text, atomictemp, notindexed)
248 return self.__call__(path, mode, text, atomictemp, notindexed)
249
249
250 def read(self, path):
250 def read(self, path):
251 fp = self(path, 'rb')
251 fp = self(path, 'rb')
252 try:
252 try:
253 return fp.read()
253 return fp.read()
254 finally:
254 finally:
255 fp.close()
255 fp.close()
256
256
257 def readlines(self, path, mode='rb'):
257 def readlines(self, path, mode='rb'):
258 fp = self(path, mode=mode)
258 fp = self(path, mode=mode)
259 try:
259 try:
260 return fp.readlines()
260 return fp.readlines()
261 finally:
261 finally:
262 fp.close()
262 fp.close()
263
263
264 def write(self, path, data):
264 def write(self, path, data):
265 fp = self(path, 'wb')
265 fp = self(path, 'wb')
266 try:
266 try:
267 return fp.write(data)
267 return fp.write(data)
268 finally:
268 finally:
269 fp.close()
269 fp.close()
270
270
271 def writelines(self, path, data, mode='wb', notindexed=False):
271 def writelines(self, path, data, mode='wb', notindexed=False):
272 fp = self(path, mode=mode, notindexed=notindexed)
272 fp = self(path, mode=mode, notindexed=notindexed)
273 try:
273 try:
274 return fp.writelines(data)
274 return fp.writelines(data)
275 finally:
275 finally:
276 fp.close()
276 fp.close()
277
277
278 def append(self, path, data):
278 def append(self, path, data):
279 fp = self(path, 'ab')
279 fp = self(path, 'ab')
280 try:
280 try:
281 return fp.write(data)
281 return fp.write(data)
282 finally:
282 finally:
283 fp.close()
283 fp.close()
284
284
285 def basename(self, path):
285 def basename(self, path):
286 """return base element of a path (as os.path.basename would do)
286 """return base element of a path (as os.path.basename would do)
287
287
288 This exists to allow handling of strange encoding if needed."""
288 This exists to allow handling of strange encoding if needed."""
289 return os.path.basename(path)
289 return os.path.basename(path)
290
290
291 def chmod(self, path, mode):
291 def chmod(self, path, mode):
292 return os.chmod(self.join(path), mode)
292 return os.chmod(self.join(path), mode)
293
293
294 def dirname(self, path):
294 def dirname(self, path):
295 """return dirname element of a path (as os.path.dirname would do)
295 """return dirname element of a path (as os.path.dirname would do)
296
296
297 This exists to allow handling of strange encoding if needed."""
297 This exists to allow handling of strange encoding if needed."""
298 return os.path.dirname(path)
298 return os.path.dirname(path)
299
299
300 def exists(self, path=None):
300 def exists(self, path=None):
301 return os.path.exists(self.join(path))
301 return os.path.exists(self.join(path))
302
302
303 def fstat(self, fp):
303 def fstat(self, fp):
304 return util.fstat(fp)
304 return util.fstat(fp)
305
305
306 def isdir(self, path=None):
306 def isdir(self, path=None):
307 return os.path.isdir(self.join(path))
307 return os.path.isdir(self.join(path))
308
308
309 def isfile(self, path=None):
309 def isfile(self, path=None):
310 return os.path.isfile(self.join(path))
310 return os.path.isfile(self.join(path))
311
311
312 def islink(self, path=None):
312 def islink(self, path=None):
313 return os.path.islink(self.join(path))
313 return os.path.islink(self.join(path))
314
314
315 def reljoin(self, *paths):
315 def reljoin(self, *paths):
316 """join various elements of a path together (as os.path.join would do)
316 """join various elements of a path together (as os.path.join would do)
317
317
318 The vfs base is not injected so that path stay relative. This exists
318 The vfs base is not injected so that path stay relative. This exists
319 to allow handling of strange encoding if needed."""
319 to allow handling of strange encoding if needed."""
320 return os.path.join(*paths)
320 return os.path.join(*paths)
321
321
322 def split(self, path):
322 def split(self, path):
323 """split top-most element of a path (as os.path.split would do)
323 """split top-most element of a path (as os.path.split would do)
324
324
325 This exists to allow handling of strange encoding if needed."""
325 This exists to allow handling of strange encoding if needed."""
326 return os.path.split(path)
326 return os.path.split(path)
327
327
328 def lexists(self, path=None):
328 def lexists(self, path=None):
329 return os.path.lexists(self.join(path))
329 return os.path.lexists(self.join(path))
330
330
331 def lstat(self, path=None):
331 def lstat(self, path=None):
332 return os.lstat(self.join(path))
332 return os.lstat(self.join(path))
333
333
334 def listdir(self, path=None):
334 def listdir(self, path=None):
335 return os.listdir(self.join(path))
335 return os.listdir(self.join(path))
336
336
337 def makedir(self, path=None, notindexed=True):
337 def makedir(self, path=None, notindexed=True):
338 return util.makedir(self.join(path), notindexed)
338 return util.makedir(self.join(path), notindexed)
339
339
340 def makedirs(self, path=None, mode=None):
340 def makedirs(self, path=None, mode=None):
341 return util.makedirs(self.join(path), mode)
341 return util.makedirs(self.join(path), mode)
342
342
343 def makelock(self, info, path):
343 def makelock(self, info, path):
344 return util.makelock(info, self.join(path))
344 return util.makelock(info, self.join(path))
345
345
346 def mkdir(self, path=None):
346 def mkdir(self, path=None):
347 return os.mkdir(self.join(path))
347 return os.mkdir(self.join(path))
348
348
349 def mkstemp(self, suffix='', prefix='tmp', dir=None, text=False):
349 def mkstemp(self, suffix='', prefix='tmp', dir=None, text=False):
350 fd, name = tempfile.mkstemp(suffix=suffix, prefix=prefix,
350 fd, name = tempfile.mkstemp(suffix=suffix, prefix=prefix,
351 dir=self.join(dir), text=text)
351 dir=self.join(dir), text=text)
352 dname, fname = util.split(name)
352 dname, fname = util.split(name)
353 if dir:
353 if dir:
354 return fd, os.path.join(dir, fname)
354 return fd, os.path.join(dir, fname)
355 else:
355 else:
356 return fd, fname
356 return fd, fname
357
357
358 def readdir(self, path=None, stat=None, skip=None):
358 def readdir(self, path=None, stat=None, skip=None):
359 return osutil.listdir(self.join(path), stat, skip)
359 return osutil.listdir(self.join(path), stat, skip)
360
360
361 def readlock(self, path):
361 def readlock(self, path):
362 return util.readlock(self.join(path))
362 return util.readlock(self.join(path))
363
363
364 def rename(self, src, dst):
364 def rename(self, src, dst):
365 return util.rename(self.join(src), self.join(dst))
365 return util.rename(self.join(src), self.join(dst))
366
366
367 def readlink(self, path):
367 def readlink(self, path):
368 return os.readlink(self.join(path))
368 return os.readlink(self.join(path))
369
369
370 def removedirs(self, path=None):
370 def removedirs(self, path=None):
371 """Remove a leaf directory and all empty intermediate ones
371 """Remove a leaf directory and all empty intermediate ones
372 """
372 """
373 return util.removedirs(self.join(path))
373 return util.removedirs(self.join(path))
374
374
375 def rmtree(self, path=None, ignore_errors=False, forcibly=False):
375 def rmtree(self, path=None, ignore_errors=False, forcibly=False):
376 """Remove a directory tree recursively
376 """Remove a directory tree recursively
377
377
378 If ``forcibly``, this tries to remove READ-ONLY files, too.
378 If ``forcibly``, this tries to remove READ-ONLY files, too.
379 """
379 """
380 if forcibly:
380 if forcibly:
381 def onerror(function, path, excinfo):
381 def onerror(function, path, excinfo):
382 if function is not os.remove:
382 if function is not os.remove:
383 raise
383 raise
384 # read-only files cannot be unlinked under Windows
384 # read-only files cannot be unlinked under Windows
385 s = os.stat(path)
385 s = os.stat(path)
386 if (s.st_mode & stat.S_IWRITE) != 0:
386 if (s.st_mode & stat.S_IWRITE) != 0:
387 raise
387 raise
388 os.chmod(path, stat.S_IMODE(s.st_mode) | stat.S_IWRITE)
388 os.chmod(path, stat.S_IMODE(s.st_mode) | stat.S_IWRITE)
389 os.remove(path)
389 os.remove(path)
390 else:
390 else:
391 onerror = None
391 onerror = None
392 return shutil.rmtree(self.join(path),
392 return shutil.rmtree(self.join(path),
393 ignore_errors=ignore_errors, onerror=onerror)
393 ignore_errors=ignore_errors, onerror=onerror)
394
394
395 def setflags(self, path, l, x):
395 def setflags(self, path, l, x):
396 return util.setflags(self.join(path), l, x)
396 return util.setflags(self.join(path), l, x)
397
397
398 def stat(self, path=None):
398 def stat(self, path=None):
399 return os.stat(self.join(path))
399 return os.stat(self.join(path))
400
400
401 def unlink(self, path=None):
401 def unlink(self, path=None):
402 return util.unlink(self.join(path))
402 return util.unlink(self.join(path))
403
403
404 def unlinkpath(self, path=None, ignoremissing=False):
404 def unlinkpath(self, path=None, ignoremissing=False):
405 return util.unlinkpath(self.join(path), ignoremissing)
405 return util.unlinkpath(self.join(path), ignoremissing)
406
406
407 def utime(self, path=None, t=None):
407 def utime(self, path=None, t=None):
408 return os.utime(self.join(path), t)
408 return os.utime(self.join(path), t)
409
409
410 def walk(self, path=None, onerror=None):
410 def walk(self, path=None, onerror=None):
411 """Yield (dirpath, dirs, files) tuple for each directories under path
411 """Yield (dirpath, dirs, files) tuple for each directories under path
412
412
413 ``dirpath`` is relative one from the root of this vfs. This
413 ``dirpath`` is relative one from the root of this vfs. This
414 uses ``os.sep`` as path separator, even you specify POSIX
414 uses ``os.sep`` as path separator, even you specify POSIX
415 style ``path``.
415 style ``path``.
416
416
417 "The root of this vfs" is represented as empty ``dirpath``.
417 "The root of this vfs" is represented as empty ``dirpath``.
418 """
418 """
419 root = os.path.normpath(self.join(None))
419 root = os.path.normpath(self.join(None))
420 # when dirpath == root, dirpath[prefixlen:] becomes empty
420 # when dirpath == root, dirpath[prefixlen:] becomes empty
421 # because len(dirpath) < prefixlen.
421 # because len(dirpath) < prefixlen.
422 prefixlen = len(pathutil.normasprefix(root))
422 prefixlen = len(pathutil.normasprefix(root))
423 for dirpath, dirs, files in os.walk(self.join(path), onerror=onerror):
423 for dirpath, dirs, files in os.walk(self.join(path), onerror=onerror):
424 yield (dirpath[prefixlen:], dirs, files)
424 yield (dirpath[prefixlen:], dirs, files)
425
425
426 class vfs(abstractvfs):
426 class vfs(abstractvfs):
427 '''Operate files relative to a base directory
427 '''Operate files relative to a base directory
428
428
429 This class is used to hide the details of COW semantics and
429 This class is used to hide the details of COW semantics and
430 remote file access from higher level code.
430 remote file access from higher level code.
431 '''
431 '''
432 def __init__(self, base, audit=True, expandpath=False, realpath=False):
432 def __init__(self, base, audit=True, expandpath=False, realpath=False):
433 if expandpath:
433 if expandpath:
434 base = util.expandpath(base)
434 base = util.expandpath(base)
435 if realpath:
435 if realpath:
436 base = os.path.realpath(base)
436 base = os.path.realpath(base)
437 self.base = base
437 self.base = base
438 self._setmustaudit(audit)
438 self._setmustaudit(audit)
439 self.createmode = None
439 self.createmode = None
440 self._trustnlink = None
440 self._trustnlink = None
441
441
442 def _getmustaudit(self):
442 def _getmustaudit(self):
443 return self._audit
443 return self._audit
444
444
445 def _setmustaudit(self, onoff):
445 def _setmustaudit(self, onoff):
446 self._audit = onoff
446 self._audit = onoff
447 if onoff:
447 if onoff:
448 self.audit = pathutil.pathauditor(self.base)
448 self.audit = pathutil.pathauditor(self.base)
449 else:
449 else:
450 self.audit = util.always
450 self.audit = util.always
451
451
452 mustaudit = property(_getmustaudit, _setmustaudit)
452 mustaudit = property(_getmustaudit, _setmustaudit)
453
453
454 @util.propertycache
454 @util.propertycache
455 def _cansymlink(self):
455 def _cansymlink(self):
456 return util.checklink(self.base)
456 return util.checklink(self.base)
457
457
458 @util.propertycache
458 @util.propertycache
459 def _chmod(self):
459 def _chmod(self):
460 return util.checkexec(self.base)
460 return util.checkexec(self.base)
461
461
462 def _fixfilemode(self, name):
462 def _fixfilemode(self, name):
463 if self.createmode is None or not self._chmod:
463 if self.createmode is None or not self._chmod:
464 return
464 return
465 os.chmod(name, self.createmode & 0o666)
465 os.chmod(name, self.createmode & 0o666)
466
466
467 def __call__(self, path, mode="r", text=False, atomictemp=False,
467 def __call__(self, path, mode="r", text=False, atomictemp=False,
468 notindexed=False):
468 notindexed=False):
469 '''Open ``path`` file, which is relative to vfs root.
469 '''Open ``path`` file, which is relative to vfs root.
470
470
471 Newly created directories are marked as "not to be indexed by
471 Newly created directories are marked as "not to be indexed by
472 the content indexing service", if ``notindexed`` is specified
472 the content indexing service", if ``notindexed`` is specified
473 for "write" mode access.
473 for "write" mode access.
474 '''
474 '''
475 if self._audit:
475 if self._audit:
476 r = util.checkosfilename(path)
476 r = util.checkosfilename(path)
477 if r:
477 if r:
478 raise util.Abort("%s: %r" % (r, path))
478 raise util.Abort("%s: %r" % (r, path))
479 self.audit(path)
479 self.audit(path)
480 f = self.join(path)
480 f = self.join(path)
481
481
482 if not text and "b" not in mode:
482 if not text and "b" not in mode:
483 mode += "b" # for that other OS
483 mode += "b" # for that other OS
484
484
485 nlink = -1
485 nlink = -1
486 if mode not in ('r', 'rb'):
486 if mode not in ('r', 'rb'):
487 dirname, basename = util.split(f)
487 dirname, basename = util.split(f)
488 # If basename is empty, then the path is malformed because it points
488 # If basename is empty, then the path is malformed because it points
489 # to a directory. Let the posixfile() call below raise IOError.
489 # to a directory. Let the posixfile() call below raise IOError.
490 if basename:
490 if basename:
491 if atomictemp:
491 if atomictemp:
492 util.ensuredirs(dirname, self.createmode, notindexed)
492 util.ensuredirs(dirname, self.createmode, notindexed)
493 return util.atomictempfile(f, mode, self.createmode)
493 return util.atomictempfile(f, mode, self.createmode)
494 try:
494 try:
495 if 'w' in mode:
495 if 'w' in mode:
496 util.unlink(f)
496 util.unlink(f)
497 nlink = 0
497 nlink = 0
498 else:
498 else:
499 # nlinks() may behave differently for files on Windows
499 # nlinks() may behave differently for files on Windows
500 # shares if the file is open.
500 # shares if the file is open.
501 fd = util.posixfile(f)
501 fd = util.posixfile(f)
502 nlink = util.nlinks(f)
502 nlink = util.nlinks(f)
503 if nlink < 1:
503 if nlink < 1:
504 nlink = 2 # force mktempcopy (issue1922)
504 nlink = 2 # force mktempcopy (issue1922)
505 fd.close()
505 fd.close()
506 except (OSError, IOError) as e:
506 except (OSError, IOError) as e:
507 if e.errno != errno.ENOENT:
507 if e.errno != errno.ENOENT:
508 raise
508 raise
509 nlink = 0
509 nlink = 0
510 util.ensuredirs(dirname, self.createmode, notindexed)
510 util.ensuredirs(dirname, self.createmode, notindexed)
511 if nlink > 0:
511 if nlink > 0:
512 if self._trustnlink is None:
512 if self._trustnlink is None:
513 self._trustnlink = nlink > 1 or util.checknlink(f)
513 self._trustnlink = nlink > 1 or util.checknlink(f)
514 if nlink > 1 or not self._trustnlink:
514 if nlink > 1 or not self._trustnlink:
515 util.rename(util.mktempcopy(f), f)
515 util.rename(util.mktempcopy(f), f)
516 fp = util.posixfile(f, mode)
516 fp = util.posixfile(f, mode)
517 if nlink == 0:
517 if nlink == 0:
518 self._fixfilemode(f)
518 self._fixfilemode(f)
519 return fp
519 return fp
520
520
521 def symlink(self, src, dst):
521 def symlink(self, src, dst):
522 self.audit(dst)
522 self.audit(dst)
523 linkname = self.join(dst)
523 linkname = self.join(dst)
524 try:
524 try:
525 os.unlink(linkname)
525 os.unlink(linkname)
526 except OSError:
526 except OSError:
527 pass
527 pass
528
528
529 util.ensuredirs(os.path.dirname(linkname), self.createmode)
529 util.ensuredirs(os.path.dirname(linkname), self.createmode)
530
530
531 if self._cansymlink:
531 if self._cansymlink:
532 try:
532 try:
533 os.symlink(src, linkname)
533 os.symlink(src, linkname)
534 except OSError as err:
534 except OSError as err:
535 raise OSError(err.errno, _('could not symlink to %r: %s') %
535 raise OSError(err.errno, _('could not symlink to %r: %s') %
536 (src, err.strerror), linkname)
536 (src, err.strerror), linkname)
537 else:
537 else:
538 self.write(dst, src)
538 self.write(dst, src)
539
539
540 def join(self, path, *insidef):
540 def join(self, path, *insidef):
541 if path:
541 if path:
542 return os.path.join(self.base, path, *insidef)
542 return os.path.join(self.base, path, *insidef)
543 else:
543 else:
544 return self.base
544 return self.base
545
545
546 opener = vfs
546 opener = vfs
547
547
548 class auditvfs(object):
548 class auditvfs(object):
549 def __init__(self, vfs):
549 def __init__(self, vfs):
550 self.vfs = vfs
550 self.vfs = vfs
551
551
552 def _getmustaudit(self):
552 def _getmustaudit(self):
553 return self.vfs.mustaudit
553 return self.vfs.mustaudit
554
554
555 def _setmustaudit(self, onoff):
555 def _setmustaudit(self, onoff):
556 self.vfs.mustaudit = onoff
556 self.vfs.mustaudit = onoff
557
557
558 mustaudit = property(_getmustaudit, _setmustaudit)
558 mustaudit = property(_getmustaudit, _setmustaudit)
559
559
560 class filtervfs(abstractvfs, auditvfs):
560 class filtervfs(abstractvfs, auditvfs):
561 '''Wrapper vfs for filtering filenames with a function.'''
561 '''Wrapper vfs for filtering filenames with a function.'''
562
562
563 def __init__(self, vfs, filter):
563 def __init__(self, vfs, filter):
564 auditvfs.__init__(self, vfs)
564 auditvfs.__init__(self, vfs)
565 self._filter = filter
565 self._filter = filter
566
566
567 def __call__(self, path, *args, **kwargs):
567 def __call__(self, path, *args, **kwargs):
568 return self.vfs(self._filter(path), *args, **kwargs)
568 return self.vfs(self._filter(path), *args, **kwargs)
569
569
570 def join(self, path, *insidef):
570 def join(self, path, *insidef):
571 if path:
571 if path:
572 return self.vfs.join(self._filter(self.vfs.reljoin(path, *insidef)))
572 return self.vfs.join(self._filter(self.vfs.reljoin(path, *insidef)))
573 else:
573 else:
574 return self.vfs.join(path)
574 return self.vfs.join(path)
575
575
576 filteropener = filtervfs
576 filteropener = filtervfs
577
577
578 class readonlyvfs(abstractvfs, auditvfs):
578 class readonlyvfs(abstractvfs, auditvfs):
579 '''Wrapper vfs preventing any writing.'''
579 '''Wrapper vfs preventing any writing.'''
580
580
581 def __init__(self, vfs):
581 def __init__(self, vfs):
582 auditvfs.__init__(self, vfs)
582 auditvfs.__init__(self, vfs)
583
583
584 def __call__(self, path, mode='r', *args, **kw):
584 def __call__(self, path, mode='r', *args, **kw):
585 if mode not in ('r', 'rb'):
585 if mode not in ('r', 'rb'):
586 raise util.Abort('this vfs is read only')
586 raise util.Abort('this vfs is read only')
587 return self.vfs(path, mode, *args, **kw)
587 return self.vfs(path, mode, *args, **kw)
588
588
589
589
590 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
590 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
591 '''yield every hg repository under path, always recursively.
591 '''yield every hg repository under path, always recursively.
592 The recurse flag will only control recursion into repo working dirs'''
592 The recurse flag will only control recursion into repo working dirs'''
593 def errhandler(err):
593 def errhandler(err):
594 if err.filename == path:
594 if err.filename == path:
595 raise err
595 raise err
596 samestat = getattr(os.path, 'samestat', None)
596 samestat = getattr(os.path, 'samestat', None)
597 if followsym and samestat is not None:
597 if followsym and samestat is not None:
598 def adddir(dirlst, dirname):
598 def adddir(dirlst, dirname):
599 match = False
599 match = False
600 dirstat = os.stat(dirname)
600 dirstat = os.stat(dirname)
601 for lstdirstat in dirlst:
601 for lstdirstat in dirlst:
602 if samestat(dirstat, lstdirstat):
602 if samestat(dirstat, lstdirstat):
603 match = True
603 match = True
604 break
604 break
605 if not match:
605 if not match:
606 dirlst.append(dirstat)
606 dirlst.append(dirstat)
607 return not match
607 return not match
608 else:
608 else:
609 followsym = False
609 followsym = False
610
610
611 if (seen_dirs is None) and followsym:
611 if (seen_dirs is None) and followsym:
612 seen_dirs = []
612 seen_dirs = []
613 adddir(seen_dirs, path)
613 adddir(seen_dirs, path)
614 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
614 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
615 dirs.sort()
615 dirs.sort()
616 if '.hg' in dirs:
616 if '.hg' in dirs:
617 yield root # found a repository
617 yield root # found a repository
618 qroot = os.path.join(root, '.hg', 'patches')
618 qroot = os.path.join(root, '.hg', 'patches')
619 if os.path.isdir(os.path.join(qroot, '.hg')):
619 if os.path.isdir(os.path.join(qroot, '.hg')):
620 yield qroot # we have a patch queue repo here
620 yield qroot # we have a patch queue repo here
621 if recurse:
621 if recurse:
622 # avoid recursing inside the .hg directory
622 # avoid recursing inside the .hg directory
623 dirs.remove('.hg')
623 dirs.remove('.hg')
624 else:
624 else:
625 dirs[:] = [] # don't descend further
625 dirs[:] = [] # don't descend further
626 elif followsym:
626 elif followsym:
627 newdirs = []
627 newdirs = []
628 for d in dirs:
628 for d in dirs:
629 fname = os.path.join(root, d)
629 fname = os.path.join(root, d)
630 if adddir(seen_dirs, fname):
630 if adddir(seen_dirs, fname):
631 if os.path.islink(fname):
631 if os.path.islink(fname):
632 for hgname in walkrepos(fname, True, seen_dirs):
632 for hgname in walkrepos(fname, True, seen_dirs):
633 yield hgname
633 yield hgname
634 else:
634 else:
635 newdirs.append(d)
635 newdirs.append(d)
636 dirs[:] = newdirs
636 dirs[:] = newdirs
637
637
638 def osrcpath():
638 def osrcpath():
639 '''return default os-specific hgrc search path'''
639 '''return default os-specific hgrc search path'''
640 path = []
640 path = []
641 defaultpath = os.path.join(util.datapath, 'default.d')
641 defaultpath = os.path.join(util.datapath, 'default.d')
642 if os.path.isdir(defaultpath):
642 if os.path.isdir(defaultpath):
643 for f, kind in osutil.listdir(defaultpath):
643 for f, kind in osutil.listdir(defaultpath):
644 if f.endswith('.rc'):
644 if f.endswith('.rc'):
645 path.append(os.path.join(defaultpath, f))
645 path.append(os.path.join(defaultpath, f))
646 path.extend(systemrcpath())
646 path.extend(systemrcpath())
647 path.extend(userrcpath())
647 path.extend(userrcpath())
648 path = [os.path.normpath(f) for f in path]
648 path = [os.path.normpath(f) for f in path]
649 return path
649 return path
650
650
651 _rcpath = None
651 _rcpath = None
652
652
653 def rcpath():
653 def rcpath():
654 '''return hgrc search path. if env var HGRCPATH is set, use it.
654 '''return hgrc search path. if env var HGRCPATH is set, use it.
655 for each item in path, if directory, use files ending in .rc,
655 for each item in path, if directory, use files ending in .rc,
656 else use item.
656 else use item.
657 make HGRCPATH empty to only look in .hg/hgrc of current repo.
657 make HGRCPATH empty to only look in .hg/hgrc of current repo.
658 if no HGRCPATH, use default os-specific path.'''
658 if no HGRCPATH, use default os-specific path.'''
659 global _rcpath
659 global _rcpath
660 if _rcpath is None:
660 if _rcpath is None:
661 if 'HGRCPATH' in os.environ:
661 if 'HGRCPATH' in os.environ:
662 _rcpath = []
662 _rcpath = []
663 for p in os.environ['HGRCPATH'].split(os.pathsep):
663 for p in os.environ['HGRCPATH'].split(os.pathsep):
664 if not p:
664 if not p:
665 continue
665 continue
666 p = util.expandpath(p)
666 p = util.expandpath(p)
667 if os.path.isdir(p):
667 if os.path.isdir(p):
668 for f, kind in osutil.listdir(p):
668 for f, kind in osutil.listdir(p):
669 if f.endswith('.rc'):
669 if f.endswith('.rc'):
670 _rcpath.append(os.path.join(p, f))
670 _rcpath.append(os.path.join(p, f))
671 else:
671 else:
672 _rcpath.append(p)
672 _rcpath.append(p)
673 else:
673 else:
674 _rcpath = osrcpath()
674 _rcpath = osrcpath()
675 return _rcpath
675 return _rcpath
676
676
677 def intrev(rev):
677 def intrev(rev):
678 """Return integer for a given revision that can be used in comparison or
678 """Return integer for a given revision that can be used in comparison or
679 arithmetic operation"""
679 arithmetic operation"""
680 if rev is None:
680 if rev is None:
681 return wdirrev
681 return wdirrev
682 return rev
682 return rev
683
683
684 def revsingle(repo, revspec, default='.'):
684 def revsingle(repo, revspec, default='.'):
685 if not revspec and revspec != 0:
685 if not revspec and revspec != 0:
686 return repo[default]
686 return repo[default]
687
687
688 l = revrange(repo, [revspec])
688 l = revrange(repo, [revspec])
689 if not l:
689 if not l:
690 raise util.Abort(_('empty revision set'))
690 raise util.Abort(_('empty revision set'))
691 return repo[l.last()]
691 return repo[l.last()]
692
692
693 def _pairspec(revspec):
694 tree = revset.parse(revspec)
695 tree = revset.optimize(tree, True)[1] # fix up "x^:y" -> "(x^):y"
696 return tree and tree[0] in ('range', 'rangepre', 'rangepost', 'rangeall')
697
693 def revpair(repo, revs):
698 def revpair(repo, revs):
694 if not revs:
699 if not revs:
695 return repo.dirstate.p1(), None
700 return repo.dirstate.p1(), None
696
701
697 l = revrange(repo, revs)
702 l = revrange(repo, revs)
698
703
699 if not l:
704 if not l:
700 first = second = None
705 first = second = None
701 elif l.isascending():
706 elif l.isascending():
702 first = l.min()
707 first = l.min()
703 second = l.max()
708 second = l.max()
704 elif l.isdescending():
709 elif l.isdescending():
705 first = l.max()
710 first = l.max()
706 second = l.min()
711 second = l.min()
707 else:
712 else:
708 first = l.first()
713 first = l.first()
709 second = l.last()
714 second = l.last()
710
715
711 if first is None:
716 if first is None:
712 raise util.Abort(_('empty revision range'))
717 raise util.Abort(_('empty revision range'))
713
718
714 if first == second and len(revs) == 1 and _revrangesep not in revs[0]:
719 # if top-level is range expression, the result must always be a pair
720 if first == second and len(revs) == 1 and not _pairspec(revs[0]):
715 return repo.lookup(first), None
721 return repo.lookup(first), None
716
722
717 return repo.lookup(first), repo.lookup(second)
723 return repo.lookup(first), repo.lookup(second)
718
724
719 _revrangesep = ':'
720
721 def revrange(repo, revs):
725 def revrange(repo, revs):
722 """Yield revision as strings from a list of revision specifications."""
726 """Yield revision as strings from a list of revision specifications."""
723 allspecs = []
727 allspecs = []
724 for spec in revs:
728 for spec in revs:
725 if isinstance(spec, int):
729 if isinstance(spec, int):
726 spec = revset.formatspec('rev(%d)', spec)
730 spec = revset.formatspec('rev(%d)', spec)
727 allspecs.append(spec)
731 allspecs.append(spec)
728 m = revset.matchany(repo.ui, allspecs, repo)
732 m = revset.matchany(repo.ui, allspecs, repo)
729 return m(repo)
733 return m(repo)
730
734
731 def expandpats(pats):
735 def expandpats(pats):
732 '''Expand bare globs when running on windows.
736 '''Expand bare globs when running on windows.
733 On posix we assume it already has already been done by sh.'''
737 On posix we assume it already has already been done by sh.'''
734 if not util.expandglobs:
738 if not util.expandglobs:
735 return list(pats)
739 return list(pats)
736 ret = []
740 ret = []
737 for kindpat in pats:
741 for kindpat in pats:
738 kind, pat = matchmod._patsplit(kindpat, None)
742 kind, pat = matchmod._patsplit(kindpat, None)
739 if kind is None:
743 if kind is None:
740 try:
744 try:
741 globbed = glob.glob(pat)
745 globbed = glob.glob(pat)
742 except re.error:
746 except re.error:
743 globbed = [pat]
747 globbed = [pat]
744 if globbed:
748 if globbed:
745 ret.extend(globbed)
749 ret.extend(globbed)
746 continue
750 continue
747 ret.append(kindpat)
751 ret.append(kindpat)
748 return ret
752 return ret
749
753
750 def matchandpats(ctx, pats=[], opts={}, globbed=False, default='relpath',
754 def matchandpats(ctx, pats=[], opts={}, globbed=False, default='relpath',
751 badfn=None):
755 badfn=None):
752 '''Return a matcher and the patterns that were used.
756 '''Return a matcher and the patterns that were used.
753 The matcher will warn about bad matches, unless an alternate badfn callback
757 The matcher will warn about bad matches, unless an alternate badfn callback
754 is provided.'''
758 is provided.'''
755 if pats == ("",):
759 if pats == ("",):
756 pats = []
760 pats = []
757 if not globbed and default == 'relpath':
761 if not globbed and default == 'relpath':
758 pats = expandpats(pats or [])
762 pats = expandpats(pats or [])
759
763
760 def bad(f, msg):
764 def bad(f, msg):
761 ctx.repo().ui.warn("%s: %s\n" % (m.rel(f), msg))
765 ctx.repo().ui.warn("%s: %s\n" % (m.rel(f), msg))
762
766
763 if badfn is None:
767 if badfn is None:
764 badfn = bad
768 badfn = bad
765
769
766 m = ctx.match(pats, opts.get('include'), opts.get('exclude'),
770 m = ctx.match(pats, opts.get('include'), opts.get('exclude'),
767 default, listsubrepos=opts.get('subrepos'), badfn=badfn)
771 default, listsubrepos=opts.get('subrepos'), badfn=badfn)
768
772
769 if m.always():
773 if m.always():
770 pats = []
774 pats = []
771 return m, pats
775 return m, pats
772
776
773 def match(ctx, pats=[], opts={}, globbed=False, default='relpath', badfn=None):
777 def match(ctx, pats=[], opts={}, globbed=False, default='relpath', badfn=None):
774 '''Return a matcher that will warn about bad matches.'''
778 '''Return a matcher that will warn about bad matches.'''
775 return matchandpats(ctx, pats, opts, globbed, default, badfn=badfn)[0]
779 return matchandpats(ctx, pats, opts, globbed, default, badfn=badfn)[0]
776
780
777 def matchall(repo):
781 def matchall(repo):
778 '''Return a matcher that will efficiently match everything.'''
782 '''Return a matcher that will efficiently match everything.'''
779 return matchmod.always(repo.root, repo.getcwd())
783 return matchmod.always(repo.root, repo.getcwd())
780
784
781 def matchfiles(repo, files, badfn=None):
785 def matchfiles(repo, files, badfn=None):
782 '''Return a matcher that will efficiently match exactly these files.'''
786 '''Return a matcher that will efficiently match exactly these files.'''
783 return matchmod.exact(repo.root, repo.getcwd(), files, badfn=badfn)
787 return matchmod.exact(repo.root, repo.getcwd(), files, badfn=badfn)
784
788
785 def addremove(repo, matcher, prefix, opts={}, dry_run=None, similarity=None):
789 def addremove(repo, matcher, prefix, opts={}, dry_run=None, similarity=None):
786 m = matcher
790 m = matcher
787 if dry_run is None:
791 if dry_run is None:
788 dry_run = opts.get('dry_run')
792 dry_run = opts.get('dry_run')
789 if similarity is None:
793 if similarity is None:
790 similarity = float(opts.get('similarity') or 0)
794 similarity = float(opts.get('similarity') or 0)
791
795
792 ret = 0
796 ret = 0
793 join = lambda f: os.path.join(prefix, f)
797 join = lambda f: os.path.join(prefix, f)
794
798
795 def matchessubrepo(matcher, subpath):
799 def matchessubrepo(matcher, subpath):
796 if matcher.exact(subpath):
800 if matcher.exact(subpath):
797 return True
801 return True
798 for f in matcher.files():
802 for f in matcher.files():
799 if f.startswith(subpath):
803 if f.startswith(subpath):
800 return True
804 return True
801 return False
805 return False
802
806
803 wctx = repo[None]
807 wctx = repo[None]
804 for subpath in sorted(wctx.substate):
808 for subpath in sorted(wctx.substate):
805 if opts.get('subrepos') or matchessubrepo(m, subpath):
809 if opts.get('subrepos') or matchessubrepo(m, subpath):
806 sub = wctx.sub(subpath)
810 sub = wctx.sub(subpath)
807 try:
811 try:
808 submatch = matchmod.narrowmatcher(subpath, m)
812 submatch = matchmod.narrowmatcher(subpath, m)
809 if sub.addremove(submatch, prefix, opts, dry_run, similarity):
813 if sub.addremove(submatch, prefix, opts, dry_run, similarity):
810 ret = 1
814 ret = 1
811 except error.LookupError:
815 except error.LookupError:
812 repo.ui.status(_("skipping missing subrepository: %s\n")
816 repo.ui.status(_("skipping missing subrepository: %s\n")
813 % join(subpath))
817 % join(subpath))
814
818
815 rejected = []
819 rejected = []
816 def badfn(f, msg):
820 def badfn(f, msg):
817 if f in m.files():
821 if f in m.files():
818 m.bad(f, msg)
822 m.bad(f, msg)
819 rejected.append(f)
823 rejected.append(f)
820
824
821 badmatch = matchmod.badmatch(m, badfn)
825 badmatch = matchmod.badmatch(m, badfn)
822 added, unknown, deleted, removed, forgotten = _interestingfiles(repo,
826 added, unknown, deleted, removed, forgotten = _interestingfiles(repo,
823 badmatch)
827 badmatch)
824
828
825 unknownset = set(unknown + forgotten)
829 unknownset = set(unknown + forgotten)
826 toprint = unknownset.copy()
830 toprint = unknownset.copy()
827 toprint.update(deleted)
831 toprint.update(deleted)
828 for abs in sorted(toprint):
832 for abs in sorted(toprint):
829 if repo.ui.verbose or not m.exact(abs):
833 if repo.ui.verbose or not m.exact(abs):
830 if abs in unknownset:
834 if abs in unknownset:
831 status = _('adding %s\n') % m.uipath(abs)
835 status = _('adding %s\n') % m.uipath(abs)
832 else:
836 else:
833 status = _('removing %s\n') % m.uipath(abs)
837 status = _('removing %s\n') % m.uipath(abs)
834 repo.ui.status(status)
838 repo.ui.status(status)
835
839
836 renames = _findrenames(repo, m, added + unknown, removed + deleted,
840 renames = _findrenames(repo, m, added + unknown, removed + deleted,
837 similarity)
841 similarity)
838
842
839 if not dry_run:
843 if not dry_run:
840 _markchanges(repo, unknown + forgotten, deleted, renames)
844 _markchanges(repo, unknown + forgotten, deleted, renames)
841
845
842 for f in rejected:
846 for f in rejected:
843 if f in m.files():
847 if f in m.files():
844 return 1
848 return 1
845 return ret
849 return ret
846
850
847 def marktouched(repo, files, similarity=0.0):
851 def marktouched(repo, files, similarity=0.0):
848 '''Assert that files have somehow been operated upon. files are relative to
852 '''Assert that files have somehow been operated upon. files are relative to
849 the repo root.'''
853 the repo root.'''
850 m = matchfiles(repo, files, badfn=lambda x, y: rejected.append(x))
854 m = matchfiles(repo, files, badfn=lambda x, y: rejected.append(x))
851 rejected = []
855 rejected = []
852
856
853 added, unknown, deleted, removed, forgotten = _interestingfiles(repo, m)
857 added, unknown, deleted, removed, forgotten = _interestingfiles(repo, m)
854
858
855 if repo.ui.verbose:
859 if repo.ui.verbose:
856 unknownset = set(unknown + forgotten)
860 unknownset = set(unknown + forgotten)
857 toprint = unknownset.copy()
861 toprint = unknownset.copy()
858 toprint.update(deleted)
862 toprint.update(deleted)
859 for abs in sorted(toprint):
863 for abs in sorted(toprint):
860 if abs in unknownset:
864 if abs in unknownset:
861 status = _('adding %s\n') % abs
865 status = _('adding %s\n') % abs
862 else:
866 else:
863 status = _('removing %s\n') % abs
867 status = _('removing %s\n') % abs
864 repo.ui.status(status)
868 repo.ui.status(status)
865
869
866 renames = _findrenames(repo, m, added + unknown, removed + deleted,
870 renames = _findrenames(repo, m, added + unknown, removed + deleted,
867 similarity)
871 similarity)
868
872
869 _markchanges(repo, unknown + forgotten, deleted, renames)
873 _markchanges(repo, unknown + forgotten, deleted, renames)
870
874
871 for f in rejected:
875 for f in rejected:
872 if f in m.files():
876 if f in m.files():
873 return 1
877 return 1
874 return 0
878 return 0
875
879
876 def _interestingfiles(repo, matcher):
880 def _interestingfiles(repo, matcher):
877 '''Walk dirstate with matcher, looking for files that addremove would care
881 '''Walk dirstate with matcher, looking for files that addremove would care
878 about.
882 about.
879
883
880 This is different from dirstate.status because it doesn't care about
884 This is different from dirstate.status because it doesn't care about
881 whether files are modified or clean.'''
885 whether files are modified or clean.'''
882 added, unknown, deleted, removed, forgotten = [], [], [], [], []
886 added, unknown, deleted, removed, forgotten = [], [], [], [], []
883 audit_path = pathutil.pathauditor(repo.root)
887 audit_path = pathutil.pathauditor(repo.root)
884
888
885 ctx = repo[None]
889 ctx = repo[None]
886 dirstate = repo.dirstate
890 dirstate = repo.dirstate
887 walkresults = dirstate.walk(matcher, sorted(ctx.substate), True, False,
891 walkresults = dirstate.walk(matcher, sorted(ctx.substate), True, False,
888 full=False)
892 full=False)
889 for abs, st in walkresults.iteritems():
893 for abs, st in walkresults.iteritems():
890 dstate = dirstate[abs]
894 dstate = dirstate[abs]
891 if dstate == '?' and audit_path.check(abs):
895 if dstate == '?' and audit_path.check(abs):
892 unknown.append(abs)
896 unknown.append(abs)
893 elif dstate != 'r' and not st:
897 elif dstate != 'r' and not st:
894 deleted.append(abs)
898 deleted.append(abs)
895 elif dstate == 'r' and st:
899 elif dstate == 'r' and st:
896 forgotten.append(abs)
900 forgotten.append(abs)
897 # for finding renames
901 # for finding renames
898 elif dstate == 'r' and not st:
902 elif dstate == 'r' and not st:
899 removed.append(abs)
903 removed.append(abs)
900 elif dstate == 'a':
904 elif dstate == 'a':
901 added.append(abs)
905 added.append(abs)
902
906
903 return added, unknown, deleted, removed, forgotten
907 return added, unknown, deleted, removed, forgotten
904
908
905 def _findrenames(repo, matcher, added, removed, similarity):
909 def _findrenames(repo, matcher, added, removed, similarity):
906 '''Find renames from removed files to added ones.'''
910 '''Find renames from removed files to added ones.'''
907 renames = {}
911 renames = {}
908 if similarity > 0:
912 if similarity > 0:
909 for old, new, score in similar.findrenames(repo, added, removed,
913 for old, new, score in similar.findrenames(repo, added, removed,
910 similarity):
914 similarity):
911 if (repo.ui.verbose or not matcher.exact(old)
915 if (repo.ui.verbose or not matcher.exact(old)
912 or not matcher.exact(new)):
916 or not matcher.exact(new)):
913 repo.ui.status(_('recording removal of %s as rename to %s '
917 repo.ui.status(_('recording removal of %s as rename to %s '
914 '(%d%% similar)\n') %
918 '(%d%% similar)\n') %
915 (matcher.rel(old), matcher.rel(new),
919 (matcher.rel(old), matcher.rel(new),
916 score * 100))
920 score * 100))
917 renames[new] = old
921 renames[new] = old
918 return renames
922 return renames
919
923
920 def _markchanges(repo, unknown, deleted, renames):
924 def _markchanges(repo, unknown, deleted, renames):
921 '''Marks the files in unknown as added, the files in deleted as removed,
925 '''Marks the files in unknown as added, the files in deleted as removed,
922 and the files in renames as copied.'''
926 and the files in renames as copied.'''
923 wctx = repo[None]
927 wctx = repo[None]
924 wlock = repo.wlock()
928 wlock = repo.wlock()
925 try:
929 try:
926 wctx.forget(deleted)
930 wctx.forget(deleted)
927 wctx.add(unknown)
931 wctx.add(unknown)
928 for new, old in renames.iteritems():
932 for new, old in renames.iteritems():
929 wctx.copy(old, new)
933 wctx.copy(old, new)
930 finally:
934 finally:
931 wlock.release()
935 wlock.release()
932
936
933 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
937 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
934 """Update the dirstate to reflect the intent of copying src to dst. For
938 """Update the dirstate to reflect the intent of copying src to dst. For
935 different reasons it might not end with dst being marked as copied from src.
939 different reasons it might not end with dst being marked as copied from src.
936 """
940 """
937 origsrc = repo.dirstate.copied(src) or src
941 origsrc = repo.dirstate.copied(src) or src
938 if dst == origsrc: # copying back a copy?
942 if dst == origsrc: # copying back a copy?
939 if repo.dirstate[dst] not in 'mn' and not dryrun:
943 if repo.dirstate[dst] not in 'mn' and not dryrun:
940 repo.dirstate.normallookup(dst)
944 repo.dirstate.normallookup(dst)
941 else:
945 else:
942 if repo.dirstate[origsrc] == 'a' and origsrc == src:
946 if repo.dirstate[origsrc] == 'a' and origsrc == src:
943 if not ui.quiet:
947 if not ui.quiet:
944 ui.warn(_("%s has not been committed yet, so no copy "
948 ui.warn(_("%s has not been committed yet, so no copy "
945 "data will be stored for %s.\n")
949 "data will be stored for %s.\n")
946 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
950 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
947 if repo.dirstate[dst] in '?r' and not dryrun:
951 if repo.dirstate[dst] in '?r' and not dryrun:
948 wctx.add([dst])
952 wctx.add([dst])
949 elif not dryrun:
953 elif not dryrun:
950 wctx.copy(origsrc, dst)
954 wctx.copy(origsrc, dst)
951
955
952 def readrequires(opener, supported):
956 def readrequires(opener, supported):
953 '''Reads and parses .hg/requires and checks if all entries found
957 '''Reads and parses .hg/requires and checks if all entries found
954 are in the list of supported features.'''
958 are in the list of supported features.'''
955 requirements = set(opener.read("requires").splitlines())
959 requirements = set(opener.read("requires").splitlines())
956 missings = []
960 missings = []
957 for r in requirements:
961 for r in requirements:
958 if r not in supported:
962 if r not in supported:
959 if not r or not r[0].isalnum():
963 if not r or not r[0].isalnum():
960 raise error.RequirementError(_(".hg/requires file is corrupt"))
964 raise error.RequirementError(_(".hg/requires file is corrupt"))
961 missings.append(r)
965 missings.append(r)
962 missings.sort()
966 missings.sort()
963 if missings:
967 if missings:
964 raise error.RequirementError(
968 raise error.RequirementError(
965 _("repository requires features unknown to this Mercurial: %s")
969 _("repository requires features unknown to this Mercurial: %s")
966 % " ".join(missings),
970 % " ".join(missings),
967 hint=_("see http://mercurial.selenic.com/wiki/MissingRequirement"
971 hint=_("see http://mercurial.selenic.com/wiki/MissingRequirement"
968 " for more information"))
972 " for more information"))
969 return requirements
973 return requirements
970
974
971 def writerequires(opener, requirements):
975 def writerequires(opener, requirements):
972 reqfile = opener("requires", "w")
976 reqfile = opener("requires", "w")
973 for r in sorted(requirements):
977 for r in sorted(requirements):
974 reqfile.write("%s\n" % r)
978 reqfile.write("%s\n" % r)
975 reqfile.close()
979 reqfile.close()
976
980
977 class filecachesubentry(object):
981 class filecachesubentry(object):
978 def __init__(self, path, stat):
982 def __init__(self, path, stat):
979 self.path = path
983 self.path = path
980 self.cachestat = None
984 self.cachestat = None
981 self._cacheable = None
985 self._cacheable = None
982
986
983 if stat:
987 if stat:
984 self.cachestat = filecachesubentry.stat(self.path)
988 self.cachestat = filecachesubentry.stat(self.path)
985
989
986 if self.cachestat:
990 if self.cachestat:
987 self._cacheable = self.cachestat.cacheable()
991 self._cacheable = self.cachestat.cacheable()
988 else:
992 else:
989 # None means we don't know yet
993 # None means we don't know yet
990 self._cacheable = None
994 self._cacheable = None
991
995
992 def refresh(self):
996 def refresh(self):
993 if self.cacheable():
997 if self.cacheable():
994 self.cachestat = filecachesubentry.stat(self.path)
998 self.cachestat = filecachesubentry.stat(self.path)
995
999
996 def cacheable(self):
1000 def cacheable(self):
997 if self._cacheable is not None:
1001 if self._cacheable is not None:
998 return self._cacheable
1002 return self._cacheable
999
1003
1000 # we don't know yet, assume it is for now
1004 # we don't know yet, assume it is for now
1001 return True
1005 return True
1002
1006
1003 def changed(self):
1007 def changed(self):
1004 # no point in going further if we can't cache it
1008 # no point in going further if we can't cache it
1005 if not self.cacheable():
1009 if not self.cacheable():
1006 return True
1010 return True
1007
1011
1008 newstat = filecachesubentry.stat(self.path)
1012 newstat = filecachesubentry.stat(self.path)
1009
1013
1010 # we may not know if it's cacheable yet, check again now
1014 # we may not know if it's cacheable yet, check again now
1011 if newstat and self._cacheable is None:
1015 if newstat and self._cacheable is None:
1012 self._cacheable = newstat.cacheable()
1016 self._cacheable = newstat.cacheable()
1013
1017
1014 # check again
1018 # check again
1015 if not self._cacheable:
1019 if not self._cacheable:
1016 return True
1020 return True
1017
1021
1018 if self.cachestat != newstat:
1022 if self.cachestat != newstat:
1019 self.cachestat = newstat
1023 self.cachestat = newstat
1020 return True
1024 return True
1021 else:
1025 else:
1022 return False
1026 return False
1023
1027
1024 @staticmethod
1028 @staticmethod
1025 def stat(path):
1029 def stat(path):
1026 try:
1030 try:
1027 return util.cachestat(path)
1031 return util.cachestat(path)
1028 except OSError as e:
1032 except OSError as e:
1029 if e.errno != errno.ENOENT:
1033 if e.errno != errno.ENOENT:
1030 raise
1034 raise
1031
1035
1032 class filecacheentry(object):
1036 class filecacheentry(object):
1033 def __init__(self, paths, stat=True):
1037 def __init__(self, paths, stat=True):
1034 self._entries = []
1038 self._entries = []
1035 for path in paths:
1039 for path in paths:
1036 self._entries.append(filecachesubentry(path, stat))
1040 self._entries.append(filecachesubentry(path, stat))
1037
1041
1038 def changed(self):
1042 def changed(self):
1039 '''true if any entry has changed'''
1043 '''true if any entry has changed'''
1040 for entry in self._entries:
1044 for entry in self._entries:
1041 if entry.changed():
1045 if entry.changed():
1042 return True
1046 return True
1043 return False
1047 return False
1044
1048
1045 def refresh(self):
1049 def refresh(self):
1046 for entry in self._entries:
1050 for entry in self._entries:
1047 entry.refresh()
1051 entry.refresh()
1048
1052
1049 class filecache(object):
1053 class filecache(object):
1050 '''A property like decorator that tracks files under .hg/ for updates.
1054 '''A property like decorator that tracks files under .hg/ for updates.
1051
1055
1052 Records stat info when called in _filecache.
1056 Records stat info when called in _filecache.
1053
1057
1054 On subsequent calls, compares old stat info with new info, and recreates the
1058 On subsequent calls, compares old stat info with new info, and recreates the
1055 object when any of the files changes, updating the new stat info in
1059 object when any of the files changes, updating the new stat info in
1056 _filecache.
1060 _filecache.
1057
1061
1058 Mercurial either atomic renames or appends for files under .hg,
1062 Mercurial either atomic renames or appends for files under .hg,
1059 so to ensure the cache is reliable we need the filesystem to be able
1063 so to ensure the cache is reliable we need the filesystem to be able
1060 to tell us if a file has been replaced. If it can't, we fallback to
1064 to tell us if a file has been replaced. If it can't, we fallback to
1061 recreating the object on every call (essentially the same behaviour as
1065 recreating the object on every call (essentially the same behaviour as
1062 propertycache).
1066 propertycache).
1063
1067
1064 '''
1068 '''
1065 def __init__(self, *paths):
1069 def __init__(self, *paths):
1066 self.paths = paths
1070 self.paths = paths
1067
1071
1068 def join(self, obj, fname):
1072 def join(self, obj, fname):
1069 """Used to compute the runtime path of a cached file.
1073 """Used to compute the runtime path of a cached file.
1070
1074
1071 Users should subclass filecache and provide their own version of this
1075 Users should subclass filecache and provide their own version of this
1072 function to call the appropriate join function on 'obj' (an instance
1076 function to call the appropriate join function on 'obj' (an instance
1073 of the class that its member function was decorated).
1077 of the class that its member function was decorated).
1074 """
1078 """
1075 return obj.join(fname)
1079 return obj.join(fname)
1076
1080
1077 def __call__(self, func):
1081 def __call__(self, func):
1078 self.func = func
1082 self.func = func
1079 self.name = func.__name__
1083 self.name = func.__name__
1080 return self
1084 return self
1081
1085
1082 def __get__(self, obj, type=None):
1086 def __get__(self, obj, type=None):
1083 # do we need to check if the file changed?
1087 # do we need to check if the file changed?
1084 if self.name in obj.__dict__:
1088 if self.name in obj.__dict__:
1085 assert self.name in obj._filecache, self.name
1089 assert self.name in obj._filecache, self.name
1086 return obj.__dict__[self.name]
1090 return obj.__dict__[self.name]
1087
1091
1088 entry = obj._filecache.get(self.name)
1092 entry = obj._filecache.get(self.name)
1089
1093
1090 if entry:
1094 if entry:
1091 if entry.changed():
1095 if entry.changed():
1092 entry.obj = self.func(obj)
1096 entry.obj = self.func(obj)
1093 else:
1097 else:
1094 paths = [self.join(obj, path) for path in self.paths]
1098 paths = [self.join(obj, path) for path in self.paths]
1095
1099
1096 # We stat -before- creating the object so our cache doesn't lie if
1100 # We stat -before- creating the object so our cache doesn't lie if
1097 # a writer modified between the time we read and stat
1101 # a writer modified between the time we read and stat
1098 entry = filecacheentry(paths, True)
1102 entry = filecacheentry(paths, True)
1099 entry.obj = self.func(obj)
1103 entry.obj = self.func(obj)
1100
1104
1101 obj._filecache[self.name] = entry
1105 obj._filecache[self.name] = entry
1102
1106
1103 obj.__dict__[self.name] = entry.obj
1107 obj.__dict__[self.name] = entry.obj
1104 return entry.obj
1108 return entry.obj
1105
1109
1106 def __set__(self, obj, value):
1110 def __set__(self, obj, value):
1107 if self.name not in obj._filecache:
1111 if self.name not in obj._filecache:
1108 # we add an entry for the missing value because X in __dict__
1112 # we add an entry for the missing value because X in __dict__
1109 # implies X in _filecache
1113 # implies X in _filecache
1110 paths = [self.join(obj, path) for path in self.paths]
1114 paths = [self.join(obj, path) for path in self.paths]
1111 ce = filecacheentry(paths, False)
1115 ce = filecacheentry(paths, False)
1112 obj._filecache[self.name] = ce
1116 obj._filecache[self.name] = ce
1113 else:
1117 else:
1114 ce = obj._filecache[self.name]
1118 ce = obj._filecache[self.name]
1115
1119
1116 ce.obj = value # update cached copy
1120 ce.obj = value # update cached copy
1117 obj.__dict__[self.name] = value # update copy returned by obj.x
1121 obj.__dict__[self.name] = value # update copy returned by obj.x
1118
1122
1119 def __delete__(self, obj):
1123 def __delete__(self, obj):
1120 try:
1124 try:
1121 del obj.__dict__[self.name]
1125 del obj.__dict__[self.name]
1122 except KeyError:
1126 except KeyError:
1123 raise AttributeError(self.name)
1127 raise AttributeError(self.name)
@@ -1,103 +1,137 b''
1 Testing diff --change
1 Testing diff --change
2
2
3 $ hg init a
3 $ hg init a
4 $ cd a
4 $ cd a
5
5
6 $ echo "first" > file.txt
6 $ echo "first" > file.txt
7 $ hg add file.txt
7 $ hg add file.txt
8 $ hg commit -m 'first commit' # 0
8 $ hg commit -m 'first commit' # 0
9
9
10 $ echo "second" > file.txt
10 $ echo "second" > file.txt
11 $ hg commit -m 'second commit' # 1
11 $ hg commit -m 'second commit' # 1
12
12
13 $ echo "third" > file.txt
13 $ echo "third" > file.txt
14 $ hg commit -m 'third commit' # 2
14 $ hg commit -m 'third commit' # 2
15
15
16 $ hg diff --nodates --change 1
16 $ hg diff --nodates --change 1
17 diff -r 4bb65dda5db4 -r e9b286083166 file.txt
17 diff -r 4bb65dda5db4 -r e9b286083166 file.txt
18 --- a/file.txt
18 --- a/file.txt
19 +++ b/file.txt
19 +++ b/file.txt
20 @@ -1,1 +1,1 @@
20 @@ -1,1 +1,1 @@
21 -first
21 -first
22 +second
22 +second
23
23
24 $ hg diff --change e9b286083166
24 $ hg diff --change e9b286083166
25 diff -r 4bb65dda5db4 -r e9b286083166 file.txt
25 diff -r 4bb65dda5db4 -r e9b286083166 file.txt
26 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
26 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
27 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
27 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
28 @@ -1,1 +1,1 @@
28 @@ -1,1 +1,1 @@
29 -first
29 -first
30 +second
30 +second
31
31
32 $ cd ..
32 $ cd ..
33
33
34 Test dumb revspecs (issue3474)
34 Test dumb revspecs: top-level "x:y", "x:", ":y" and ":" ranges should be handled
35 as pairs even if x == y, but not for "f(x:y)" nor "x::y" (issue3474, issue4774)
35
36
36 $ hg clone -q a dumbspec
37 $ hg clone -q a dumbspec
37 $ cd dumbspec
38 $ cd dumbspec
38 $ echo "wdir" > file.txt
39 $ echo "wdir" > file.txt
39
40
40 $ hg diff -r 2:2
41 $ hg diff -r 2:2
42 $ hg diff -r 2:.
43 $ hg diff -r 2:
44 $ hg diff -r :0
45 $ hg diff -r '2:first(2:2)'
46 $ hg diff -r 'first(2:2)' --nodates
47 diff -r bf5ff72eb7e0 file.txt
48 --- a/file.txt
49 +++ b/file.txt
50 @@ -1,1 +1,1 @@
51 -third
52 +wdir
53 $ hg diff -r 2::2 --nodates
54 diff -r bf5ff72eb7e0 file.txt
55 --- a/file.txt
56 +++ b/file.txt
57 @@ -1,1 +1,1 @@
58 -third
59 +wdir
41 $ hg diff -r "2 and 1"
60 $ hg diff -r "2 and 1"
42 abort: empty revision range
61 abort: empty revision range
43 [255]
62 [255]
44
63
45 $ cd ..
64 $ cd ..
46
65
66 $ hg clone -qr0 a dumbspec-rev0
67 $ cd dumbspec-rev0
68 $ echo "wdir" > file.txt
69
70 $ hg diff -r :
71 $ hg diff -r 'first(:)' --nodates
72 diff -r 4bb65dda5db4 file.txt
73 --- a/file.txt
74 +++ b/file.txt
75 @@ -1,1 +1,1 @@
76 -first
77 +wdir
78
79 $ cd ..
80
47 Testing diff --change when merge:
81 Testing diff --change when merge:
48
82
49 $ cd a
83 $ cd a
50
84
51 $ for i in 1 2 3 4 5 6 7 8 9 10; do
85 $ for i in 1 2 3 4 5 6 7 8 9 10; do
52 > echo $i >> file.txt
86 > echo $i >> file.txt
53 > done
87 > done
54 $ hg commit -m "lots of text" # 3
88 $ hg commit -m "lots of text" # 3
55
89
56 $ sed -e 's,^2$,x,' file.txt > file.txt.tmp
90 $ sed -e 's,^2$,x,' file.txt > file.txt.tmp
57 $ mv file.txt.tmp file.txt
91 $ mv file.txt.tmp file.txt
58 $ hg commit -m "change 2 to x" # 4
92 $ hg commit -m "change 2 to x" # 4
59
93
60 $ hg up -r 3
94 $ hg up -r 3
61 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
62 $ sed -e 's,^8$,y,' file.txt > file.txt.tmp
96 $ sed -e 's,^8$,y,' file.txt > file.txt.tmp
63 $ mv file.txt.tmp file.txt
97 $ mv file.txt.tmp file.txt
64 $ hg commit -m "change 8 to y"
98 $ hg commit -m "change 8 to y"
65 created new head
99 created new head
66
100
67 $ hg up -C -r 4
101 $ hg up -C -r 4
68 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
102 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
69 $ hg merge -r 5
103 $ hg merge -r 5
70 merging file.txt
104 merging file.txt
71 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
105 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
72 (branch merge, don't forget to commit)
106 (branch merge, don't forget to commit)
73 $ hg commit -m "merge 8 to y" # 6
107 $ hg commit -m "merge 8 to y" # 6
74
108
75 $ hg diff --change 5
109 $ hg diff --change 5
76 diff -r ae119d680c82 -r 9085c5c02e52 file.txt
110 diff -r ae119d680c82 -r 9085c5c02e52 file.txt
77 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
111 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
78 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
112 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
79 @@ -6,6 +6,6 @@
113 @@ -6,6 +6,6 @@
80 5
114 5
81 6
115 6
82 7
116 7
83 -8
117 -8
84 +y
118 +y
85 9
119 9
86 10
120 10
87
121
88 must be similar to 'hg diff --change 5':
122 must be similar to 'hg diff --change 5':
89
123
90 $ hg diff -c 6
124 $ hg diff -c 6
91 diff -r 273b50f17c6d -r 979ca961fd2e file.txt
125 diff -r 273b50f17c6d -r 979ca961fd2e file.txt
92 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
126 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
93 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
127 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
94 @@ -6,6 +6,6 @@
128 @@ -6,6 +6,6 @@
95 5
129 5
96 6
130 6
97 7
131 7
98 -8
132 -8
99 +y
133 +y
100 9
134 9
101 10
135 10
102
136
103 $ cd ..
137 $ cd ..
@@ -1,2163 +1,2169 b''
1 $ HGENCODING=utf-8
1 $ HGENCODING=utf-8
2 $ export HGENCODING
2 $ export HGENCODING
3 $ cat > testrevset.py << EOF
3 $ cat > testrevset.py << EOF
4 > import mercurial.revset
4 > import mercurial.revset
5 >
5 >
6 > baseset = mercurial.revset.baseset
6 > baseset = mercurial.revset.baseset
7 >
7 >
8 > def r3232(repo, subset, x):
8 > def r3232(repo, subset, x):
9 > """"simple revset that return [3,2,3,2]
9 > """"simple revset that return [3,2,3,2]
10 >
10 >
11 > revisions duplicated on purpose.
11 > revisions duplicated on purpose.
12 > """
12 > """
13 > if 3 not in subset:
13 > if 3 not in subset:
14 > if 2 in subset:
14 > if 2 in subset:
15 > return baseset([2,2])
15 > return baseset([2,2])
16 > return baseset()
16 > return baseset()
17 > return baseset([3,3,2,2])
17 > return baseset([3,3,2,2])
18 >
18 >
19 > mercurial.revset.symbols['r3232'] = r3232
19 > mercurial.revset.symbols['r3232'] = r3232
20 > EOF
20 > EOF
21 $ cat >> $HGRCPATH << EOF
21 $ cat >> $HGRCPATH << EOF
22 > [extensions]
22 > [extensions]
23 > testrevset=$TESTTMP/testrevset.py
23 > testrevset=$TESTTMP/testrevset.py
24 > EOF
24 > EOF
25
25
26 $ try() {
26 $ try() {
27 > hg debugrevspec --debug "$@"
27 > hg debugrevspec --debug "$@"
28 > }
28 > }
29
29
30 $ log() {
30 $ log() {
31 > hg log --template '{rev}\n' -r "$1"
31 > hg log --template '{rev}\n' -r "$1"
32 > }
32 > }
33
33
34 $ hg init repo
34 $ hg init repo
35 $ cd repo
35 $ cd repo
36
36
37 $ echo a > a
37 $ echo a > a
38 $ hg branch a
38 $ hg branch a
39 marked working directory as branch a
39 marked working directory as branch a
40 (branches are permanent and global, did you want a bookmark?)
40 (branches are permanent and global, did you want a bookmark?)
41 $ hg ci -Aqm0
41 $ hg ci -Aqm0
42
42
43 $ echo b > b
43 $ echo b > b
44 $ hg branch b
44 $ hg branch b
45 marked working directory as branch b
45 marked working directory as branch b
46 $ hg ci -Aqm1
46 $ hg ci -Aqm1
47
47
48 $ rm a
48 $ rm a
49 $ hg branch a-b-c-
49 $ hg branch a-b-c-
50 marked working directory as branch a-b-c-
50 marked working directory as branch a-b-c-
51 $ hg ci -Aqm2 -u Bob
51 $ hg ci -Aqm2 -u Bob
52
52
53 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
53 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
54 2
54 2
55 $ hg log -r "extra('branch')" --template '{rev}\n'
55 $ hg log -r "extra('branch')" --template '{rev}\n'
56 0
56 0
57 1
57 1
58 2
58 2
59 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
59 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
60 0 a
60 0 a
61 2 a-b-c-
61 2 a-b-c-
62
62
63 $ hg co 1
63 $ hg co 1
64 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
64 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
65 $ hg branch +a+b+c+
65 $ hg branch +a+b+c+
66 marked working directory as branch +a+b+c+
66 marked working directory as branch +a+b+c+
67 $ hg ci -Aqm3
67 $ hg ci -Aqm3
68
68
69 $ hg co 2 # interleave
69 $ hg co 2 # interleave
70 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
70 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
71 $ echo bb > b
71 $ echo bb > b
72 $ hg branch -- -a-b-c-
72 $ hg branch -- -a-b-c-
73 marked working directory as branch -a-b-c-
73 marked working directory as branch -a-b-c-
74 $ hg ci -Aqm4 -d "May 12 2005"
74 $ hg ci -Aqm4 -d "May 12 2005"
75
75
76 $ hg co 3
76 $ hg co 3
77 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
77 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
78 $ hg branch !a/b/c/
78 $ hg branch !a/b/c/
79 marked working directory as branch !a/b/c/
79 marked working directory as branch !a/b/c/
80 $ hg ci -Aqm"5 bug"
80 $ hg ci -Aqm"5 bug"
81
81
82 $ hg merge 4
82 $ hg merge 4
83 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
83 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
84 (branch merge, don't forget to commit)
84 (branch merge, don't forget to commit)
85 $ hg branch _a_b_c_
85 $ hg branch _a_b_c_
86 marked working directory as branch _a_b_c_
86 marked working directory as branch _a_b_c_
87 $ hg ci -Aqm"6 issue619"
87 $ hg ci -Aqm"6 issue619"
88
88
89 $ hg branch .a.b.c.
89 $ hg branch .a.b.c.
90 marked working directory as branch .a.b.c.
90 marked working directory as branch .a.b.c.
91 $ hg ci -Aqm7
91 $ hg ci -Aqm7
92
92
93 $ hg branch all
93 $ hg branch all
94 marked working directory as branch all
94 marked working directory as branch all
95
95
96 $ hg co 4
96 $ hg co 4
97 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
97 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
98 $ hg branch Γ©
98 $ hg branch Γ©
99 marked working directory as branch \xc3\xa9 (esc)
99 marked working directory as branch \xc3\xa9 (esc)
100 $ hg ci -Aqm9
100 $ hg ci -Aqm9
101
101
102 $ hg tag -r6 1.0
102 $ hg tag -r6 1.0
103 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
103 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
104
104
105 $ hg clone --quiet -U -r 7 . ../remote1
105 $ hg clone --quiet -U -r 7 . ../remote1
106 $ hg clone --quiet -U -r 8 . ../remote2
106 $ hg clone --quiet -U -r 8 . ../remote2
107 $ echo "[paths]" >> .hg/hgrc
107 $ echo "[paths]" >> .hg/hgrc
108 $ echo "default = ../remote1" >> .hg/hgrc
108 $ echo "default = ../remote1" >> .hg/hgrc
109
109
110 trivial
110 trivial
111
111
112 $ try 0:1
112 $ try 0:1
113 (range
113 (range
114 ('symbol', '0')
114 ('symbol', '0')
115 ('symbol', '1'))
115 ('symbol', '1'))
116 * set:
116 * set:
117 <spanset+ 0:1>
117 <spanset+ 0:1>
118 0
118 0
119 1
119 1
120 $ try --optimize :
120 $ try --optimize :
121 (rangeall
121 (rangeall
122 None)
122 None)
123 * optimized:
123 * optimized:
124 (range
124 (range
125 ('string', '0')
125 ('string', '0')
126 ('string', 'tip'))
126 ('string', 'tip'))
127 * set:
127 * set:
128 <spanset+ 0:9>
128 <spanset+ 0:9>
129 0
129 0
130 1
130 1
131 2
131 2
132 3
132 3
133 4
133 4
134 5
134 5
135 6
135 6
136 7
136 7
137 8
137 8
138 9
138 9
139 $ try 3::6
139 $ try 3::6
140 (dagrange
140 (dagrange
141 ('symbol', '3')
141 ('symbol', '3')
142 ('symbol', '6'))
142 ('symbol', '6'))
143 * set:
143 * set:
144 <baseset [3, 5, 6]>
144 <baseset [3, 5, 6]>
145 3
145 3
146 5
146 5
147 6
147 6
148 $ try '0|1|2'
148 $ try '0|1|2'
149 (or
149 (or
150 ('symbol', '0')
150 ('symbol', '0')
151 ('symbol', '1')
151 ('symbol', '1')
152 ('symbol', '2'))
152 ('symbol', '2'))
153 * set:
153 * set:
154 <baseset [0, 1, 2]>
154 <baseset [0, 1, 2]>
155 0
155 0
156 1
156 1
157 2
157 2
158
158
159 names that should work without quoting
159 names that should work without quoting
160
160
161 $ try a
161 $ try a
162 ('symbol', 'a')
162 ('symbol', 'a')
163 * set:
163 * set:
164 <baseset [0]>
164 <baseset [0]>
165 0
165 0
166 $ try b-a
166 $ try b-a
167 (minus
167 (minus
168 ('symbol', 'b')
168 ('symbol', 'b')
169 ('symbol', 'a'))
169 ('symbol', 'a'))
170 * set:
170 * set:
171 <filteredset
171 <filteredset
172 <baseset [1]>>
172 <baseset [1]>>
173 1
173 1
174 $ try _a_b_c_
174 $ try _a_b_c_
175 ('symbol', '_a_b_c_')
175 ('symbol', '_a_b_c_')
176 * set:
176 * set:
177 <baseset [6]>
177 <baseset [6]>
178 6
178 6
179 $ try _a_b_c_-a
179 $ try _a_b_c_-a
180 (minus
180 (minus
181 ('symbol', '_a_b_c_')
181 ('symbol', '_a_b_c_')
182 ('symbol', 'a'))
182 ('symbol', 'a'))
183 * set:
183 * set:
184 <filteredset
184 <filteredset
185 <baseset [6]>>
185 <baseset [6]>>
186 6
186 6
187 $ try .a.b.c.
187 $ try .a.b.c.
188 ('symbol', '.a.b.c.')
188 ('symbol', '.a.b.c.')
189 * set:
189 * set:
190 <baseset [7]>
190 <baseset [7]>
191 7
191 7
192 $ try .a.b.c.-a
192 $ try .a.b.c.-a
193 (minus
193 (minus
194 ('symbol', '.a.b.c.')
194 ('symbol', '.a.b.c.')
195 ('symbol', 'a'))
195 ('symbol', 'a'))
196 * set:
196 * set:
197 <filteredset
197 <filteredset
198 <baseset [7]>>
198 <baseset [7]>>
199 7
199 7
200
200
201 names that should be caught by fallback mechanism
201 names that should be caught by fallback mechanism
202
202
203 $ try -- '-a-b-c-'
203 $ try -- '-a-b-c-'
204 ('symbol', '-a-b-c-')
204 ('symbol', '-a-b-c-')
205 * set:
205 * set:
206 <baseset [4]>
206 <baseset [4]>
207 4
207 4
208 $ log -a-b-c-
208 $ log -a-b-c-
209 4
209 4
210 $ try '+a+b+c+'
210 $ try '+a+b+c+'
211 ('symbol', '+a+b+c+')
211 ('symbol', '+a+b+c+')
212 * set:
212 * set:
213 <baseset [3]>
213 <baseset [3]>
214 3
214 3
215 $ try '+a+b+c+:'
215 $ try '+a+b+c+:'
216 (rangepost
216 (rangepost
217 ('symbol', '+a+b+c+'))
217 ('symbol', '+a+b+c+'))
218 * set:
218 * set:
219 <spanset+ 3:9>
219 <spanset+ 3:9>
220 3
220 3
221 4
221 4
222 5
222 5
223 6
223 6
224 7
224 7
225 8
225 8
226 9
226 9
227 $ try ':+a+b+c+'
227 $ try ':+a+b+c+'
228 (rangepre
228 (rangepre
229 ('symbol', '+a+b+c+'))
229 ('symbol', '+a+b+c+'))
230 * set:
230 * set:
231 <spanset+ 0:3>
231 <spanset+ 0:3>
232 0
232 0
233 1
233 1
234 2
234 2
235 3
235 3
236 $ try -- '-a-b-c-:+a+b+c+'
236 $ try -- '-a-b-c-:+a+b+c+'
237 (range
237 (range
238 ('symbol', '-a-b-c-')
238 ('symbol', '-a-b-c-')
239 ('symbol', '+a+b+c+'))
239 ('symbol', '+a+b+c+'))
240 * set:
240 * set:
241 <spanset- 3:4>
241 <spanset- 3:4>
242 4
242 4
243 3
243 3
244 $ log '-a-b-c-:+a+b+c+'
244 $ log '-a-b-c-:+a+b+c+'
245 4
245 4
246 3
246 3
247
247
248 $ try -- -a-b-c--a # complains
248 $ try -- -a-b-c--a # complains
249 (minus
249 (minus
250 (minus
250 (minus
251 (minus
251 (minus
252 (negate
252 (negate
253 ('symbol', 'a'))
253 ('symbol', 'a'))
254 ('symbol', 'b'))
254 ('symbol', 'b'))
255 ('symbol', 'c'))
255 ('symbol', 'c'))
256 (negate
256 (negate
257 ('symbol', 'a')))
257 ('symbol', 'a')))
258 abort: unknown revision '-a'!
258 abort: unknown revision '-a'!
259 [255]
259 [255]
260 $ try Γ©
260 $ try Γ©
261 ('symbol', '\xc3\xa9')
261 ('symbol', '\xc3\xa9')
262 * set:
262 * set:
263 <baseset [9]>
263 <baseset [9]>
264 9
264 9
265
265
266 no quoting needed
266 no quoting needed
267
267
268 $ log ::a-b-c-
268 $ log ::a-b-c-
269 0
269 0
270 1
270 1
271 2
271 2
272
272
273 quoting needed
273 quoting needed
274
274
275 $ try '"-a-b-c-"-a'
275 $ try '"-a-b-c-"-a'
276 (minus
276 (minus
277 ('string', '-a-b-c-')
277 ('string', '-a-b-c-')
278 ('symbol', 'a'))
278 ('symbol', 'a'))
279 * set:
279 * set:
280 <filteredset
280 <filteredset
281 <baseset [4]>>
281 <baseset [4]>>
282 4
282 4
283
283
284 $ log '1 or 2'
284 $ log '1 or 2'
285 1
285 1
286 2
286 2
287 $ log '1|2'
287 $ log '1|2'
288 1
288 1
289 2
289 2
290 $ log '1 and 2'
290 $ log '1 and 2'
291 $ log '1&2'
291 $ log '1&2'
292 $ try '1&2|3' # precedence - and is higher
292 $ try '1&2|3' # precedence - and is higher
293 (or
293 (or
294 (and
294 (and
295 ('symbol', '1')
295 ('symbol', '1')
296 ('symbol', '2'))
296 ('symbol', '2'))
297 ('symbol', '3'))
297 ('symbol', '3'))
298 * set:
298 * set:
299 <addset
299 <addset
300 <baseset []>,
300 <baseset []>,
301 <baseset [3]>>
301 <baseset [3]>>
302 3
302 3
303 $ try '1|2&3'
303 $ try '1|2&3'
304 (or
304 (or
305 ('symbol', '1')
305 ('symbol', '1')
306 (and
306 (and
307 ('symbol', '2')
307 ('symbol', '2')
308 ('symbol', '3')))
308 ('symbol', '3')))
309 * set:
309 * set:
310 <addset
310 <addset
311 <baseset [1]>,
311 <baseset [1]>,
312 <baseset []>>
312 <baseset []>>
313 1
313 1
314 $ try '1&2&3' # associativity
314 $ try '1&2&3' # associativity
315 (and
315 (and
316 (and
316 (and
317 ('symbol', '1')
317 ('symbol', '1')
318 ('symbol', '2'))
318 ('symbol', '2'))
319 ('symbol', '3'))
319 ('symbol', '3'))
320 * set:
320 * set:
321 <baseset []>
321 <baseset []>
322 $ try '1|(2|3)'
322 $ try '1|(2|3)'
323 (or
323 (or
324 ('symbol', '1')
324 ('symbol', '1')
325 (group
325 (group
326 (or
326 (or
327 ('symbol', '2')
327 ('symbol', '2')
328 ('symbol', '3'))))
328 ('symbol', '3'))))
329 * set:
329 * set:
330 <addset
330 <addset
331 <baseset [1]>,
331 <baseset [1]>,
332 <baseset [2, 3]>>
332 <baseset [2, 3]>>
333 1
333 1
334 2
334 2
335 3
335 3
336 $ log '1.0' # tag
336 $ log '1.0' # tag
337 6
337 6
338 $ log 'a' # branch
338 $ log 'a' # branch
339 0
339 0
340 $ log '2785f51ee'
340 $ log '2785f51ee'
341 0
341 0
342 $ log 'date(2005)'
342 $ log 'date(2005)'
343 4
343 4
344 $ log 'date(this is a test)'
344 $ log 'date(this is a test)'
345 hg: parse error at 10: unexpected token: symbol
345 hg: parse error at 10: unexpected token: symbol
346 [255]
346 [255]
347 $ log 'date()'
347 $ log 'date()'
348 hg: parse error: date requires a string
348 hg: parse error: date requires a string
349 [255]
349 [255]
350 $ log 'date'
350 $ log 'date'
351 abort: unknown revision 'date'!
351 abort: unknown revision 'date'!
352 [255]
352 [255]
353 $ log 'date('
353 $ log 'date('
354 hg: parse error at 5: not a prefix: end
354 hg: parse error at 5: not a prefix: end
355 [255]
355 [255]
356 $ log 'date(tip)'
356 $ log 'date(tip)'
357 abort: invalid date: 'tip'
357 abort: invalid date: 'tip'
358 [255]
358 [255]
359 $ log '0:date'
359 $ log '0:date'
360 abort: unknown revision 'date'!
360 abort: unknown revision 'date'!
361 [255]
361 [255]
362 $ log '::"date"'
362 $ log '::"date"'
363 abort: unknown revision 'date'!
363 abort: unknown revision 'date'!
364 [255]
364 [255]
365 $ hg book date -r 4
365 $ hg book date -r 4
366 $ log '0:date'
366 $ log '0:date'
367 0
367 0
368 1
368 1
369 2
369 2
370 3
370 3
371 4
371 4
372 $ log '::date'
372 $ log '::date'
373 0
373 0
374 1
374 1
375 2
375 2
376 4
376 4
377 $ log '::"date"'
377 $ log '::"date"'
378 0
378 0
379 1
379 1
380 2
380 2
381 4
381 4
382 $ log 'date(2005) and 1::'
382 $ log 'date(2005) and 1::'
383 4
383 4
384 $ hg book -d date
384 $ hg book -d date
385
385
386 keyword arguments
386 keyword arguments
387
387
388 $ log 'extra(branch, value=a)'
388 $ log 'extra(branch, value=a)'
389 0
389 0
390
390
391 $ log 'extra(branch, a, b)'
391 $ log 'extra(branch, a, b)'
392 hg: parse error: extra takes at most 2 arguments
392 hg: parse error: extra takes at most 2 arguments
393 [255]
393 [255]
394 $ log 'extra(a, label=b)'
394 $ log 'extra(a, label=b)'
395 hg: parse error: extra got multiple values for keyword argument 'label'
395 hg: parse error: extra got multiple values for keyword argument 'label'
396 [255]
396 [255]
397 $ log 'extra(label=branch, default)'
397 $ log 'extra(label=branch, default)'
398 hg: parse error: extra got an invalid argument
398 hg: parse error: extra got an invalid argument
399 [255]
399 [255]
400 $ log 'extra(branch, foo+bar=baz)'
400 $ log 'extra(branch, foo+bar=baz)'
401 hg: parse error: extra got an invalid argument
401 hg: parse error: extra got an invalid argument
402 [255]
402 [255]
403 $ log 'extra(unknown=branch)'
403 $ log 'extra(unknown=branch)'
404 hg: parse error: extra got an unexpected keyword argument 'unknown'
404 hg: parse error: extra got an unexpected keyword argument 'unknown'
405 [255]
405 [255]
406
406
407 $ try 'foo=bar|baz'
407 $ try 'foo=bar|baz'
408 (keyvalue
408 (keyvalue
409 ('symbol', 'foo')
409 ('symbol', 'foo')
410 (or
410 (or
411 ('symbol', 'bar')
411 ('symbol', 'bar')
412 ('symbol', 'baz')))
412 ('symbol', 'baz')))
413 hg: parse error: can't use a key-value pair in this context
413 hg: parse error: can't use a key-value pair in this context
414 [255]
414 [255]
415
415
416 Test that symbols only get parsed as functions if there's an opening
416 Test that symbols only get parsed as functions if there's an opening
417 parenthesis.
417 parenthesis.
418
418
419 $ hg book only -r 9
419 $ hg book only -r 9
420 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
420 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
421 8
421 8
422 9
422 9
423
423
424 ancestor can accept 0 or more arguments
424 ancestor can accept 0 or more arguments
425
425
426 $ log 'ancestor()'
426 $ log 'ancestor()'
427 $ log 'ancestor(1)'
427 $ log 'ancestor(1)'
428 1
428 1
429 $ log 'ancestor(4,5)'
429 $ log 'ancestor(4,5)'
430 1
430 1
431 $ log 'ancestor(4,5) and 4'
431 $ log 'ancestor(4,5) and 4'
432 $ log 'ancestor(0,0,1,3)'
432 $ log 'ancestor(0,0,1,3)'
433 0
433 0
434 $ log 'ancestor(3,1,5,3,5,1)'
434 $ log 'ancestor(3,1,5,3,5,1)'
435 1
435 1
436 $ log 'ancestor(0,1,3,5)'
436 $ log 'ancestor(0,1,3,5)'
437 0
437 0
438 $ log 'ancestor(1,2,3,4,5)'
438 $ log 'ancestor(1,2,3,4,5)'
439 1
439 1
440
440
441 test ancestors
441 test ancestors
442
442
443 $ log 'ancestors(5)'
443 $ log 'ancestors(5)'
444 0
444 0
445 1
445 1
446 3
446 3
447 5
447 5
448 $ log 'ancestor(ancestors(5))'
448 $ log 'ancestor(ancestors(5))'
449 0
449 0
450 $ log '::r3232()'
450 $ log '::r3232()'
451 0
451 0
452 1
452 1
453 2
453 2
454 3
454 3
455
455
456 $ log 'author(bob)'
456 $ log 'author(bob)'
457 2
457 2
458 $ log 'author("re:bob|test")'
458 $ log 'author("re:bob|test")'
459 0
459 0
460 1
460 1
461 2
461 2
462 3
462 3
463 4
463 4
464 5
464 5
465 6
465 6
466 7
466 7
467 8
467 8
468 9
468 9
469 $ log 'branch(Γ©)'
469 $ log 'branch(Γ©)'
470 8
470 8
471 9
471 9
472 $ log 'branch(a)'
472 $ log 'branch(a)'
473 0
473 0
474 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
474 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
475 0 a
475 0 a
476 2 a-b-c-
476 2 a-b-c-
477 3 +a+b+c+
477 3 +a+b+c+
478 4 -a-b-c-
478 4 -a-b-c-
479 5 !a/b/c/
479 5 !a/b/c/
480 6 _a_b_c_
480 6 _a_b_c_
481 7 .a.b.c.
481 7 .a.b.c.
482 $ log 'children(ancestor(4,5))'
482 $ log 'children(ancestor(4,5))'
483 2
483 2
484 3
484 3
485 $ log 'closed()'
485 $ log 'closed()'
486 $ log 'contains(a)'
486 $ log 'contains(a)'
487 0
487 0
488 1
488 1
489 3
489 3
490 5
490 5
491 $ log 'contains("../repo/a")'
491 $ log 'contains("../repo/a")'
492 0
492 0
493 1
493 1
494 3
494 3
495 5
495 5
496 $ log 'desc(B)'
496 $ log 'desc(B)'
497 5
497 5
498 $ log 'descendants(2 or 3)'
498 $ log 'descendants(2 or 3)'
499 2
499 2
500 3
500 3
501 4
501 4
502 5
502 5
503 6
503 6
504 7
504 7
505 8
505 8
506 9
506 9
507 $ log 'file("b*")'
507 $ log 'file("b*")'
508 1
508 1
509 4
509 4
510 $ log 'filelog("b")'
510 $ log 'filelog("b")'
511 1
511 1
512 4
512 4
513 $ log 'filelog("../repo/b")'
513 $ log 'filelog("../repo/b")'
514 1
514 1
515 4
515 4
516 $ log 'follow()'
516 $ log 'follow()'
517 0
517 0
518 1
518 1
519 2
519 2
520 4
520 4
521 8
521 8
522 9
522 9
523 $ log 'grep("issue\d+")'
523 $ log 'grep("issue\d+")'
524 6
524 6
525 $ try 'grep("(")' # invalid regular expression
525 $ try 'grep("(")' # invalid regular expression
526 (func
526 (func
527 ('symbol', 'grep')
527 ('symbol', 'grep')
528 ('string', '('))
528 ('string', '('))
529 hg: parse error: invalid match pattern: unbalanced parenthesis
529 hg: parse error: invalid match pattern: unbalanced parenthesis
530 [255]
530 [255]
531 $ try 'grep("\bissue\d+")'
531 $ try 'grep("\bissue\d+")'
532 (func
532 (func
533 ('symbol', 'grep')
533 ('symbol', 'grep')
534 ('string', '\x08issue\\d+'))
534 ('string', '\x08issue\\d+'))
535 * set:
535 * set:
536 <filteredset
536 <filteredset
537 <fullreposet+ 0:9>>
537 <fullreposet+ 0:9>>
538 $ try 'grep(r"\bissue\d+")'
538 $ try 'grep(r"\bissue\d+")'
539 (func
539 (func
540 ('symbol', 'grep')
540 ('symbol', 'grep')
541 ('string', '\\bissue\\d+'))
541 ('string', '\\bissue\\d+'))
542 * set:
542 * set:
543 <filteredset
543 <filteredset
544 <fullreposet+ 0:9>>
544 <fullreposet+ 0:9>>
545 6
545 6
546 $ try 'grep(r"\")'
546 $ try 'grep(r"\")'
547 hg: parse error at 7: unterminated string
547 hg: parse error at 7: unterminated string
548 [255]
548 [255]
549 $ log 'head()'
549 $ log 'head()'
550 0
550 0
551 1
551 1
552 2
552 2
553 3
553 3
554 4
554 4
555 5
555 5
556 6
556 6
557 7
557 7
558 9
558 9
559 $ log 'heads(6::)'
559 $ log 'heads(6::)'
560 7
560 7
561 $ log 'keyword(issue)'
561 $ log 'keyword(issue)'
562 6
562 6
563 $ log 'keyword("test a")'
563 $ log 'keyword("test a")'
564 $ log 'limit(head(), 1)'
564 $ log 'limit(head(), 1)'
565 0
565 0
566 $ log 'matching(6)'
566 $ log 'matching(6)'
567 6
567 6
568 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
568 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
569 6
569 6
570 7
570 7
571
571
572 Testing min and max
572 Testing min and max
573
573
574 max: simple
574 max: simple
575
575
576 $ log 'max(contains(a))'
576 $ log 'max(contains(a))'
577 5
577 5
578
578
579 max: simple on unordered set)
579 max: simple on unordered set)
580
580
581 $ log 'max((4+0+2+5+7) and contains(a))'
581 $ log 'max((4+0+2+5+7) and contains(a))'
582 5
582 5
583
583
584 max: no result
584 max: no result
585
585
586 $ log 'max(contains(stringthatdoesnotappearanywhere))'
586 $ log 'max(contains(stringthatdoesnotappearanywhere))'
587
587
588 max: no result on unordered set
588 max: no result on unordered set
589
589
590 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
590 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
591
591
592 min: simple
592 min: simple
593
593
594 $ log 'min(contains(a))'
594 $ log 'min(contains(a))'
595 0
595 0
596
596
597 min: simple on unordered set
597 min: simple on unordered set
598
598
599 $ log 'min((4+0+2+5+7) and contains(a))'
599 $ log 'min((4+0+2+5+7) and contains(a))'
600 0
600 0
601
601
602 min: empty
602 min: empty
603
603
604 $ log 'min(contains(stringthatdoesnotappearanywhere))'
604 $ log 'min(contains(stringthatdoesnotappearanywhere))'
605
605
606 min: empty on unordered set
606 min: empty on unordered set
607
607
608 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
608 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
609
609
610
610
611 $ log 'merge()'
611 $ log 'merge()'
612 6
612 6
613 $ log 'branchpoint()'
613 $ log 'branchpoint()'
614 1
614 1
615 4
615 4
616 $ log 'modifies(b)'
616 $ log 'modifies(b)'
617 4
617 4
618 $ log 'modifies("path:b")'
618 $ log 'modifies("path:b")'
619 4
619 4
620 $ log 'modifies("*")'
620 $ log 'modifies("*")'
621 4
621 4
622 6
622 6
623 $ log 'modifies("set:modified()")'
623 $ log 'modifies("set:modified()")'
624 4
624 4
625 $ log 'id(5)'
625 $ log 'id(5)'
626 2
626 2
627 $ log 'only(9)'
627 $ log 'only(9)'
628 8
628 8
629 9
629 9
630 $ log 'only(8)'
630 $ log 'only(8)'
631 8
631 8
632 $ log 'only(9, 5)'
632 $ log 'only(9, 5)'
633 2
633 2
634 4
634 4
635 8
635 8
636 9
636 9
637 $ log 'only(7 + 9, 5 + 2)'
637 $ log 'only(7 + 9, 5 + 2)'
638 4
638 4
639 6
639 6
640 7
640 7
641 8
641 8
642 9
642 9
643
643
644 Test empty set input
644 Test empty set input
645 $ log 'only(p2())'
645 $ log 'only(p2())'
646 $ log 'only(p1(), p2())'
646 $ log 'only(p1(), p2())'
647 0
647 0
648 1
648 1
649 2
649 2
650 4
650 4
651 8
651 8
652 9
652 9
653
653
654 Test '%' operator
654 Test '%' operator
655
655
656 $ log '9%'
656 $ log '9%'
657 8
657 8
658 9
658 9
659 $ log '9%5'
659 $ log '9%5'
660 2
660 2
661 4
661 4
662 8
662 8
663 9
663 9
664 $ log '(7 + 9)%(5 + 2)'
664 $ log '(7 + 9)%(5 + 2)'
665 4
665 4
666 6
666 6
667 7
667 7
668 8
668 8
669 9
669 9
670
670
671 Test opreand of '%' is optimized recursively (issue4670)
671 Test opreand of '%' is optimized recursively (issue4670)
672
672
673 $ try --optimize '8:9-8%'
673 $ try --optimize '8:9-8%'
674 (onlypost
674 (onlypost
675 (minus
675 (minus
676 (range
676 (range
677 ('symbol', '8')
677 ('symbol', '8')
678 ('symbol', '9'))
678 ('symbol', '9'))
679 ('symbol', '8')))
679 ('symbol', '8')))
680 * optimized:
680 * optimized:
681 (func
681 (func
682 ('symbol', 'only')
682 ('symbol', 'only')
683 (and
683 (and
684 (range
684 (range
685 ('symbol', '8')
685 ('symbol', '8')
686 ('symbol', '9'))
686 ('symbol', '9'))
687 (not
687 (not
688 ('symbol', '8'))))
688 ('symbol', '8'))))
689 * set:
689 * set:
690 <baseset+ [8, 9]>
690 <baseset+ [8, 9]>
691 8
691 8
692 9
692 9
693 $ try --optimize '(9)%(5)'
693 $ try --optimize '(9)%(5)'
694 (only
694 (only
695 (group
695 (group
696 ('symbol', '9'))
696 ('symbol', '9'))
697 (group
697 (group
698 ('symbol', '5')))
698 ('symbol', '5')))
699 * optimized:
699 * optimized:
700 (func
700 (func
701 ('symbol', 'only')
701 ('symbol', 'only')
702 (list
702 (list
703 ('symbol', '9')
703 ('symbol', '9')
704 ('symbol', '5')))
704 ('symbol', '5')))
705 * set:
705 * set:
706 <baseset+ [8, 9, 2, 4]>
706 <baseset+ [8, 9, 2, 4]>
707 2
707 2
708 4
708 4
709 8
709 8
710 9
710 9
711
711
712 Test the order of operations
712 Test the order of operations
713
713
714 $ log '7 + 9%5 + 2'
714 $ log '7 + 9%5 + 2'
715 7
715 7
716 2
716 2
717 4
717 4
718 8
718 8
719 9
719 9
720
720
721 Test explicit numeric revision
721 Test explicit numeric revision
722 $ log 'rev(-2)'
722 $ log 'rev(-2)'
723 $ log 'rev(-1)'
723 $ log 'rev(-1)'
724 -1
724 -1
725 $ log 'rev(0)'
725 $ log 'rev(0)'
726 0
726 0
727 $ log 'rev(9)'
727 $ log 'rev(9)'
728 9
728 9
729 $ log 'rev(10)'
729 $ log 'rev(10)'
730 $ log 'rev(tip)'
730 $ log 'rev(tip)'
731 hg: parse error: rev expects a number
731 hg: parse error: rev expects a number
732 [255]
732 [255]
733
733
734 Test hexadecimal revision
734 Test hexadecimal revision
735 $ log 'id(2)'
735 $ log 'id(2)'
736 abort: 00changelog.i@2: ambiguous identifier!
736 abort: 00changelog.i@2: ambiguous identifier!
737 [255]
737 [255]
738 $ log 'id(23268)'
738 $ log 'id(23268)'
739 4
739 4
740 $ log 'id(2785f51eece)'
740 $ log 'id(2785f51eece)'
741 0
741 0
742 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
742 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
743 8
743 8
744 $ log 'id(d5d0dcbdc4a)'
744 $ log 'id(d5d0dcbdc4a)'
745 $ log 'id(d5d0dcbdc4w)'
745 $ log 'id(d5d0dcbdc4w)'
746 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
746 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
747 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
747 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
748 $ log 'id(1.0)'
748 $ log 'id(1.0)'
749 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
749 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
750
750
751 Test null revision
751 Test null revision
752 $ log '(null)'
752 $ log '(null)'
753 -1
753 -1
754 $ log '(null:0)'
754 $ log '(null:0)'
755 -1
755 -1
756 0
756 0
757 $ log '(0:null)'
757 $ log '(0:null)'
758 0
758 0
759 -1
759 -1
760 $ log 'null::0'
760 $ log 'null::0'
761 -1
761 -1
762 0
762 0
763 $ log 'null:tip - 0:'
763 $ log 'null:tip - 0:'
764 -1
764 -1
765 $ log 'null: and null::' | head -1
765 $ log 'null: and null::' | head -1
766 -1
766 -1
767 $ log 'null: or 0:' | head -2
767 $ log 'null: or 0:' | head -2
768 -1
768 -1
769 0
769 0
770 $ log 'ancestors(null)'
770 $ log 'ancestors(null)'
771 -1
771 -1
772 $ log 'reverse(null:)' | tail -2
772 $ log 'reverse(null:)' | tail -2
773 0
773 0
774 -1
774 -1
775 BROKEN: should be '-1'
775 BROKEN: should be '-1'
776 $ log 'first(null:)'
776 $ log 'first(null:)'
777 BROKEN: should be '-1'
777 BROKEN: should be '-1'
778 $ log 'min(null:)'
778 $ log 'min(null:)'
779 $ log 'tip:null and all()' | tail -2
779 $ log 'tip:null and all()' | tail -2
780 1
780 1
781 0
781 0
782
782
783 Test working-directory revision
783 Test working-directory revision
784 $ hg debugrevspec 'wdir()'
784 $ hg debugrevspec 'wdir()'
785 2147483647
785 2147483647
786 $ hg debugrevspec 'tip or wdir()'
786 $ hg debugrevspec 'tip or wdir()'
787 9
787 9
788 2147483647
788 2147483647
789 $ hg debugrevspec '0:tip and wdir()'
789 $ hg debugrevspec '0:tip and wdir()'
790 $ log '0:wdir()' | tail -3
790 $ log '0:wdir()' | tail -3
791 8
791 8
792 9
792 9
793 2147483647
793 2147483647
794 $ log 'wdir():0' | head -3
794 $ log 'wdir():0' | head -3
795 2147483647
795 2147483647
796 9
796 9
797 8
797 8
798 $ log 'wdir():wdir()'
798 $ log 'wdir():wdir()'
799 2147483647
799 2147483647
800 $ log '(all() + wdir()) & min(. + wdir())'
800 $ log '(all() + wdir()) & min(. + wdir())'
801 9
801 9
802 $ log '(all() + wdir()) & max(. + wdir())'
802 $ log '(all() + wdir()) & max(. + wdir())'
803 2147483647
803 2147483647
804 $ log '(all() + wdir()) & first(wdir() + .)'
804 $ log '(all() + wdir()) & first(wdir() + .)'
805 2147483647
805 2147483647
806 $ log '(all() + wdir()) & last(. + wdir())'
806 $ log '(all() + wdir()) & last(. + wdir())'
807 2147483647
807 2147483647
808
808
809 $ log 'outgoing()'
809 $ log 'outgoing()'
810 8
810 8
811 9
811 9
812 $ log 'outgoing("../remote1")'
812 $ log 'outgoing("../remote1")'
813 8
813 8
814 9
814 9
815 $ log 'outgoing("../remote2")'
815 $ log 'outgoing("../remote2")'
816 3
816 3
817 5
817 5
818 6
818 6
819 7
819 7
820 9
820 9
821 $ log 'p1(merge())'
821 $ log 'p1(merge())'
822 5
822 5
823 $ log 'p2(merge())'
823 $ log 'p2(merge())'
824 4
824 4
825 $ log 'parents(merge())'
825 $ log 'parents(merge())'
826 4
826 4
827 5
827 5
828 $ log 'p1(branchpoint())'
828 $ log 'p1(branchpoint())'
829 0
829 0
830 2
830 2
831 $ log 'p2(branchpoint())'
831 $ log 'p2(branchpoint())'
832 $ log 'parents(branchpoint())'
832 $ log 'parents(branchpoint())'
833 0
833 0
834 2
834 2
835 $ log 'removes(a)'
835 $ log 'removes(a)'
836 2
836 2
837 6
837 6
838 $ log 'roots(all())'
838 $ log 'roots(all())'
839 0
839 0
840 $ log 'reverse(2 or 3 or 4 or 5)'
840 $ log 'reverse(2 or 3 or 4 or 5)'
841 5
841 5
842 4
842 4
843 3
843 3
844 2
844 2
845 $ log 'reverse(all())'
845 $ log 'reverse(all())'
846 9
846 9
847 8
847 8
848 7
848 7
849 6
849 6
850 5
850 5
851 4
851 4
852 3
852 3
853 2
853 2
854 1
854 1
855 0
855 0
856 $ log 'reverse(all()) & filelog(b)'
856 $ log 'reverse(all()) & filelog(b)'
857 4
857 4
858 1
858 1
859 $ log 'rev(5)'
859 $ log 'rev(5)'
860 5
860 5
861 $ log 'sort(limit(reverse(all()), 3))'
861 $ log 'sort(limit(reverse(all()), 3))'
862 7
862 7
863 8
863 8
864 9
864 9
865 $ log 'sort(2 or 3 or 4 or 5, date)'
865 $ log 'sort(2 or 3 or 4 or 5, date)'
866 2
866 2
867 3
867 3
868 5
868 5
869 4
869 4
870 $ log 'tagged()'
870 $ log 'tagged()'
871 6
871 6
872 $ log 'tag()'
872 $ log 'tag()'
873 6
873 6
874 $ log 'tag(1.0)'
874 $ log 'tag(1.0)'
875 6
875 6
876 $ log 'tag(tip)'
876 $ log 'tag(tip)'
877 9
877 9
878
878
879 test sort revset
879 test sort revset
880 --------------------------------------------
880 --------------------------------------------
881
881
882 test when adding two unordered revsets
882 test when adding two unordered revsets
883
883
884 $ log 'sort(keyword(issue) or modifies(b))'
884 $ log 'sort(keyword(issue) or modifies(b))'
885 4
885 4
886 6
886 6
887
887
888 test when sorting a reversed collection in the same way it is
888 test when sorting a reversed collection in the same way it is
889
889
890 $ log 'sort(reverse(all()), -rev)'
890 $ log 'sort(reverse(all()), -rev)'
891 9
891 9
892 8
892 8
893 7
893 7
894 6
894 6
895 5
895 5
896 4
896 4
897 3
897 3
898 2
898 2
899 1
899 1
900 0
900 0
901
901
902 test when sorting a reversed collection
902 test when sorting a reversed collection
903
903
904 $ log 'sort(reverse(all()), rev)'
904 $ log 'sort(reverse(all()), rev)'
905 0
905 0
906 1
906 1
907 2
907 2
908 3
908 3
909 4
909 4
910 5
910 5
911 6
911 6
912 7
912 7
913 8
913 8
914 9
914 9
915
915
916
916
917 test sorting two sorted collections in different orders
917 test sorting two sorted collections in different orders
918
918
919 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
919 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
920 2
920 2
921 6
921 6
922 8
922 8
923 9
923 9
924
924
925 test sorting two sorted collections in different orders backwards
925 test sorting two sorted collections in different orders backwards
926
926
927 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
927 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
928 9
928 9
929 8
929 8
930 6
930 6
931 2
931 2
932
932
933 test subtracting something from an addset
933 test subtracting something from an addset
934
934
935 $ log '(outgoing() or removes(a)) - removes(a)'
935 $ log '(outgoing() or removes(a)) - removes(a)'
936 8
936 8
937 9
937 9
938
938
939 test intersecting something with an addset
939 test intersecting something with an addset
940
940
941 $ log 'parents(outgoing() or removes(a))'
941 $ log 'parents(outgoing() or removes(a))'
942 1
942 1
943 4
943 4
944 5
944 5
945 8
945 8
946
946
947 test that `or` operation combines elements in the right order:
947 test that `or` operation combines elements in the right order:
948
948
949 $ log '3:4 or 2:5'
949 $ log '3:4 or 2:5'
950 3
950 3
951 4
951 4
952 2
952 2
953 5
953 5
954 $ log '3:4 or 5:2'
954 $ log '3:4 or 5:2'
955 3
955 3
956 4
956 4
957 5
957 5
958 2
958 2
959 $ log 'sort(3:4 or 2:5)'
959 $ log 'sort(3:4 or 2:5)'
960 2
960 2
961 3
961 3
962 4
962 4
963 5
963 5
964 $ log 'sort(3:4 or 5:2)'
964 $ log 'sort(3:4 or 5:2)'
965 2
965 2
966 3
966 3
967 4
967 4
968 5
968 5
969
969
970 test that more than one `-r`s are combined in the right order and deduplicated:
970 test that more than one `-r`s are combined in the right order and deduplicated:
971
971
972 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
972 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
973 3
973 3
974 4
974 4
975 5
975 5
976 2
976 2
977 0
977 0
978 1
978 1
979
979
980 test that `or` operation skips duplicated revisions from right-hand side
980 test that `or` operation skips duplicated revisions from right-hand side
981
981
982 $ try 'reverse(1::5) or ancestors(4)'
982 $ try 'reverse(1::5) or ancestors(4)'
983 (or
983 (or
984 (func
984 (func
985 ('symbol', 'reverse')
985 ('symbol', 'reverse')
986 (dagrange
986 (dagrange
987 ('symbol', '1')
987 ('symbol', '1')
988 ('symbol', '5')))
988 ('symbol', '5')))
989 (func
989 (func
990 ('symbol', 'ancestors')
990 ('symbol', 'ancestors')
991 ('symbol', '4')))
991 ('symbol', '4')))
992 * set:
992 * set:
993 <addset
993 <addset
994 <baseset [5, 3, 1]>,
994 <baseset [5, 3, 1]>,
995 <generatorset+>>
995 <generatorset+>>
996 5
996 5
997 3
997 3
998 1
998 1
999 0
999 0
1000 2
1000 2
1001 4
1001 4
1002 $ try 'sort(ancestors(4) or reverse(1::5))'
1002 $ try 'sort(ancestors(4) or reverse(1::5))'
1003 (func
1003 (func
1004 ('symbol', 'sort')
1004 ('symbol', 'sort')
1005 (or
1005 (or
1006 (func
1006 (func
1007 ('symbol', 'ancestors')
1007 ('symbol', 'ancestors')
1008 ('symbol', '4'))
1008 ('symbol', '4'))
1009 (func
1009 (func
1010 ('symbol', 'reverse')
1010 ('symbol', 'reverse')
1011 (dagrange
1011 (dagrange
1012 ('symbol', '1')
1012 ('symbol', '1')
1013 ('symbol', '5')))))
1013 ('symbol', '5')))))
1014 * set:
1014 * set:
1015 <addset+
1015 <addset+
1016 <generatorset+>,
1016 <generatorset+>,
1017 <baseset [5, 3, 1]>>
1017 <baseset [5, 3, 1]>>
1018 0
1018 0
1019 1
1019 1
1020 2
1020 2
1021 3
1021 3
1022 4
1022 4
1023 5
1023 5
1024
1024
1025 test optimization of trivial `or` operation
1025 test optimization of trivial `or` operation
1026
1026
1027 $ try --optimize '0|(1)|"2"|-2|tip|null'
1027 $ try --optimize '0|(1)|"2"|-2|tip|null'
1028 (or
1028 (or
1029 ('symbol', '0')
1029 ('symbol', '0')
1030 (group
1030 (group
1031 ('symbol', '1'))
1031 ('symbol', '1'))
1032 ('string', '2')
1032 ('string', '2')
1033 (negate
1033 (negate
1034 ('symbol', '2'))
1034 ('symbol', '2'))
1035 ('symbol', 'tip')
1035 ('symbol', 'tip')
1036 ('symbol', 'null'))
1036 ('symbol', 'null'))
1037 * optimized:
1037 * optimized:
1038 (func
1038 (func
1039 ('symbol', '_list')
1039 ('symbol', '_list')
1040 ('string', '0\x001\x002\x00-2\x00tip\x00null'))
1040 ('string', '0\x001\x002\x00-2\x00tip\x00null'))
1041 * set:
1041 * set:
1042 <baseset [0, 1, 2, 8, 9, -1]>
1042 <baseset [0, 1, 2, 8, 9, -1]>
1043 0
1043 0
1044 1
1044 1
1045 2
1045 2
1046 8
1046 8
1047 9
1047 9
1048 -1
1048 -1
1049
1049
1050 $ try --optimize '0|1|2:3'
1050 $ try --optimize '0|1|2:3'
1051 (or
1051 (or
1052 ('symbol', '0')
1052 ('symbol', '0')
1053 ('symbol', '1')
1053 ('symbol', '1')
1054 (range
1054 (range
1055 ('symbol', '2')
1055 ('symbol', '2')
1056 ('symbol', '3')))
1056 ('symbol', '3')))
1057 * optimized:
1057 * optimized:
1058 (or
1058 (or
1059 (func
1059 (func
1060 ('symbol', '_list')
1060 ('symbol', '_list')
1061 ('string', '0\x001'))
1061 ('string', '0\x001'))
1062 (range
1062 (range
1063 ('symbol', '2')
1063 ('symbol', '2')
1064 ('symbol', '3')))
1064 ('symbol', '3')))
1065 * set:
1065 * set:
1066 <addset
1066 <addset
1067 <baseset [0, 1]>,
1067 <baseset [0, 1]>,
1068 <spanset+ 2:3>>
1068 <spanset+ 2:3>>
1069 0
1069 0
1070 1
1070 1
1071 2
1071 2
1072 3
1072 3
1073
1073
1074 $ try --optimize '0:1|2|3:4|5|6'
1074 $ try --optimize '0:1|2|3:4|5|6'
1075 (or
1075 (or
1076 (range
1076 (range
1077 ('symbol', '0')
1077 ('symbol', '0')
1078 ('symbol', '1'))
1078 ('symbol', '1'))
1079 ('symbol', '2')
1079 ('symbol', '2')
1080 (range
1080 (range
1081 ('symbol', '3')
1081 ('symbol', '3')
1082 ('symbol', '4'))
1082 ('symbol', '4'))
1083 ('symbol', '5')
1083 ('symbol', '5')
1084 ('symbol', '6'))
1084 ('symbol', '6'))
1085 * optimized:
1085 * optimized:
1086 (or
1086 (or
1087 (range
1087 (range
1088 ('symbol', '0')
1088 ('symbol', '0')
1089 ('symbol', '1'))
1089 ('symbol', '1'))
1090 ('symbol', '2')
1090 ('symbol', '2')
1091 (range
1091 (range
1092 ('symbol', '3')
1092 ('symbol', '3')
1093 ('symbol', '4'))
1093 ('symbol', '4'))
1094 (func
1094 (func
1095 ('symbol', '_list')
1095 ('symbol', '_list')
1096 ('string', '5\x006')))
1096 ('string', '5\x006')))
1097 * set:
1097 * set:
1098 <addset
1098 <addset
1099 <addset
1099 <addset
1100 <spanset+ 0:1>,
1100 <spanset+ 0:1>,
1101 <baseset [2]>>,
1101 <baseset [2]>>,
1102 <addset
1102 <addset
1103 <spanset+ 3:4>,
1103 <spanset+ 3:4>,
1104 <baseset [5, 6]>>>
1104 <baseset [5, 6]>>>
1105 0
1105 0
1106 1
1106 1
1107 2
1107 2
1108 3
1108 3
1109 4
1109 4
1110 5
1110 5
1111 6
1111 6
1112
1112
1113 test that `_list` should be narrowed by provided `subset`
1113 test that `_list` should be narrowed by provided `subset`
1114
1114
1115 $ log '0:2 and (null|1|2|3)'
1115 $ log '0:2 and (null|1|2|3)'
1116 1
1116 1
1117 2
1117 2
1118
1118
1119 test that `_list` should remove duplicates
1119 test that `_list` should remove duplicates
1120
1120
1121 $ log '0|1|2|1|2|-1|tip'
1121 $ log '0|1|2|1|2|-1|tip'
1122 0
1122 0
1123 1
1123 1
1124 2
1124 2
1125 9
1125 9
1126
1126
1127 test unknown revision in `_list`
1127 test unknown revision in `_list`
1128
1128
1129 $ log '0|unknown'
1129 $ log '0|unknown'
1130 abort: unknown revision 'unknown'!
1130 abort: unknown revision 'unknown'!
1131 [255]
1131 [255]
1132
1132
1133 test integer range in `_list`
1133 test integer range in `_list`
1134
1134
1135 $ log '-1|-10'
1135 $ log '-1|-10'
1136 9
1136 9
1137 0
1137 0
1138
1138
1139 $ log '-10|-11'
1139 $ log '-10|-11'
1140 abort: unknown revision '-11'!
1140 abort: unknown revision '-11'!
1141 [255]
1141 [255]
1142
1142
1143 $ log '9|10'
1143 $ log '9|10'
1144 abort: unknown revision '10'!
1144 abort: unknown revision '10'!
1145 [255]
1145 [255]
1146
1146
1147 test '0000' != '0' in `_list`
1147 test '0000' != '0' in `_list`
1148
1148
1149 $ log '0|0000'
1149 $ log '0|0000'
1150 0
1150 0
1151 -1
1151 -1
1152
1152
1153 test that chained `or` operations make balanced addsets
1153 test that chained `or` operations make balanced addsets
1154
1154
1155 $ try '0:1|1:2|2:3|3:4|4:5'
1155 $ try '0:1|1:2|2:3|3:4|4:5'
1156 (or
1156 (or
1157 (range
1157 (range
1158 ('symbol', '0')
1158 ('symbol', '0')
1159 ('symbol', '1'))
1159 ('symbol', '1'))
1160 (range
1160 (range
1161 ('symbol', '1')
1161 ('symbol', '1')
1162 ('symbol', '2'))
1162 ('symbol', '2'))
1163 (range
1163 (range
1164 ('symbol', '2')
1164 ('symbol', '2')
1165 ('symbol', '3'))
1165 ('symbol', '3'))
1166 (range
1166 (range
1167 ('symbol', '3')
1167 ('symbol', '3')
1168 ('symbol', '4'))
1168 ('symbol', '4'))
1169 (range
1169 (range
1170 ('symbol', '4')
1170 ('symbol', '4')
1171 ('symbol', '5')))
1171 ('symbol', '5')))
1172 * set:
1172 * set:
1173 <addset
1173 <addset
1174 <addset
1174 <addset
1175 <spanset+ 0:1>,
1175 <spanset+ 0:1>,
1176 <spanset+ 1:2>>,
1176 <spanset+ 1:2>>,
1177 <addset
1177 <addset
1178 <spanset+ 2:3>,
1178 <spanset+ 2:3>,
1179 <addset
1179 <addset
1180 <spanset+ 3:4>,
1180 <spanset+ 3:4>,
1181 <spanset+ 4:5>>>>
1181 <spanset+ 4:5>>>>
1182 0
1182 0
1183 1
1183 1
1184 2
1184 2
1185 3
1185 3
1186 4
1186 4
1187 5
1187 5
1188
1188
1189 no crash by empty group "()" while optimizing `or` operations
1189 no crash by empty group "()" while optimizing `or` operations
1190
1190
1191 $ try --optimize '0|()'
1191 $ try --optimize '0|()'
1192 (or
1192 (or
1193 ('symbol', '0')
1193 ('symbol', '0')
1194 (group
1194 (group
1195 None))
1195 None))
1196 * optimized:
1196 * optimized:
1197 (or
1197 (or
1198 ('symbol', '0')
1198 ('symbol', '0')
1199 None)
1199 None)
1200 hg: parse error: missing argument
1200 hg: parse error: missing argument
1201 [255]
1201 [255]
1202
1202
1203 test that chained `or` operations never eat up stack (issue4624)
1203 test that chained `or` operations never eat up stack (issue4624)
1204 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
1204 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
1205
1205
1206 $ hg log -T '{rev}\n' -r "`python -c "print '|'.join(['0:1'] * 500)"`"
1206 $ hg log -T '{rev}\n' -r "`python -c "print '|'.join(['0:1'] * 500)"`"
1207 0
1207 0
1208 1
1208 1
1209
1209
1210 test that repeated `-r` options never eat up stack (issue4565)
1210 test that repeated `-r` options never eat up stack (issue4565)
1211 (uses `-r 0::1` to avoid possible optimization at old-style parser)
1211 (uses `-r 0::1` to avoid possible optimization at old-style parser)
1212
1212
1213 $ hg log -T '{rev}\n' `python -c "for i in xrange(500): print '-r 0::1 ',"`
1213 $ hg log -T '{rev}\n' `python -c "for i in xrange(500): print '-r 0::1 ',"`
1214 0
1214 0
1215 1
1215 1
1216
1216
1217 check that conversion to only works
1217 check that conversion to only works
1218 $ try --optimize '::3 - ::1'
1218 $ try --optimize '::3 - ::1'
1219 (minus
1219 (minus
1220 (dagrangepre
1220 (dagrangepre
1221 ('symbol', '3'))
1221 ('symbol', '3'))
1222 (dagrangepre
1222 (dagrangepre
1223 ('symbol', '1')))
1223 ('symbol', '1')))
1224 * optimized:
1224 * optimized:
1225 (func
1225 (func
1226 ('symbol', 'only')
1226 ('symbol', 'only')
1227 (list
1227 (list
1228 ('symbol', '3')
1228 ('symbol', '3')
1229 ('symbol', '1')))
1229 ('symbol', '1')))
1230 * set:
1230 * set:
1231 <baseset+ [3]>
1231 <baseset+ [3]>
1232 3
1232 3
1233 $ try --optimize 'ancestors(1) - ancestors(3)'
1233 $ try --optimize 'ancestors(1) - ancestors(3)'
1234 (minus
1234 (minus
1235 (func
1235 (func
1236 ('symbol', 'ancestors')
1236 ('symbol', 'ancestors')
1237 ('symbol', '1'))
1237 ('symbol', '1'))
1238 (func
1238 (func
1239 ('symbol', 'ancestors')
1239 ('symbol', 'ancestors')
1240 ('symbol', '3')))
1240 ('symbol', '3')))
1241 * optimized:
1241 * optimized:
1242 (func
1242 (func
1243 ('symbol', 'only')
1243 ('symbol', 'only')
1244 (list
1244 (list
1245 ('symbol', '1')
1245 ('symbol', '1')
1246 ('symbol', '3')))
1246 ('symbol', '3')))
1247 * set:
1247 * set:
1248 <baseset+ []>
1248 <baseset+ []>
1249 $ try --optimize 'not ::2 and ::6'
1249 $ try --optimize 'not ::2 and ::6'
1250 (and
1250 (and
1251 (not
1251 (not
1252 (dagrangepre
1252 (dagrangepre
1253 ('symbol', '2')))
1253 ('symbol', '2')))
1254 (dagrangepre
1254 (dagrangepre
1255 ('symbol', '6')))
1255 ('symbol', '6')))
1256 * optimized:
1256 * optimized:
1257 (func
1257 (func
1258 ('symbol', 'only')
1258 ('symbol', 'only')
1259 (list
1259 (list
1260 ('symbol', '6')
1260 ('symbol', '6')
1261 ('symbol', '2')))
1261 ('symbol', '2')))
1262 * set:
1262 * set:
1263 <baseset+ [3, 4, 5, 6]>
1263 <baseset+ [3, 4, 5, 6]>
1264 3
1264 3
1265 4
1265 4
1266 5
1266 5
1267 6
1267 6
1268 $ try --optimize 'ancestors(6) and not ancestors(4)'
1268 $ try --optimize 'ancestors(6) and not ancestors(4)'
1269 (and
1269 (and
1270 (func
1270 (func
1271 ('symbol', 'ancestors')
1271 ('symbol', 'ancestors')
1272 ('symbol', '6'))
1272 ('symbol', '6'))
1273 (not
1273 (not
1274 (func
1274 (func
1275 ('symbol', 'ancestors')
1275 ('symbol', 'ancestors')
1276 ('symbol', '4'))))
1276 ('symbol', '4'))))
1277 * optimized:
1277 * optimized:
1278 (func
1278 (func
1279 ('symbol', 'only')
1279 ('symbol', 'only')
1280 (list
1280 (list
1281 ('symbol', '6')
1281 ('symbol', '6')
1282 ('symbol', '4')))
1282 ('symbol', '4')))
1283 * set:
1283 * set:
1284 <baseset+ [3, 5, 6]>
1284 <baseset+ [3, 5, 6]>
1285 3
1285 3
1286 5
1286 5
1287 6
1287 6
1288
1288
1289 no crash by empty group "()" while optimizing to "only()"
1289 no crash by empty group "()" while optimizing to "only()"
1290
1290
1291 $ try --optimize '::1 and ()'
1291 $ try --optimize '::1 and ()'
1292 (and
1292 (and
1293 (dagrangepre
1293 (dagrangepre
1294 ('symbol', '1'))
1294 ('symbol', '1'))
1295 (group
1295 (group
1296 None))
1296 None))
1297 * optimized:
1297 * optimized:
1298 (and
1298 (and
1299 None
1299 None
1300 (func
1300 (func
1301 ('symbol', 'ancestors')
1301 ('symbol', 'ancestors')
1302 ('symbol', '1')))
1302 ('symbol', '1')))
1303 hg: parse error: missing argument
1303 hg: parse error: missing argument
1304 [255]
1304 [255]
1305
1305
1306 we can use patterns when searching for tags
1306 we can use patterns when searching for tags
1307
1307
1308 $ log 'tag("1..*")'
1308 $ log 'tag("1..*")'
1309 abort: tag '1..*' does not exist!
1309 abort: tag '1..*' does not exist!
1310 [255]
1310 [255]
1311 $ log 'tag("re:1..*")'
1311 $ log 'tag("re:1..*")'
1312 6
1312 6
1313 $ log 'tag("re:[0-9].[0-9]")'
1313 $ log 'tag("re:[0-9].[0-9]")'
1314 6
1314 6
1315 $ log 'tag("literal:1.0")'
1315 $ log 'tag("literal:1.0")'
1316 6
1316 6
1317 $ log 'tag("re:0..*")'
1317 $ log 'tag("re:0..*")'
1318
1318
1319 $ log 'tag(unknown)'
1319 $ log 'tag(unknown)'
1320 abort: tag 'unknown' does not exist!
1320 abort: tag 'unknown' does not exist!
1321 [255]
1321 [255]
1322 $ log 'tag("re:unknown")'
1322 $ log 'tag("re:unknown")'
1323 $ log 'present(tag("unknown"))'
1323 $ log 'present(tag("unknown"))'
1324 $ log 'present(tag("re:unknown"))'
1324 $ log 'present(tag("re:unknown"))'
1325 $ log 'branch(unknown)'
1325 $ log 'branch(unknown)'
1326 abort: unknown revision 'unknown'!
1326 abort: unknown revision 'unknown'!
1327 [255]
1327 [255]
1328 $ log 'branch("re:unknown")'
1328 $ log 'branch("re:unknown")'
1329 $ log 'present(branch("unknown"))'
1329 $ log 'present(branch("unknown"))'
1330 $ log 'present(branch("re:unknown"))'
1330 $ log 'present(branch("re:unknown"))'
1331 $ log 'user(bob)'
1331 $ log 'user(bob)'
1332 2
1332 2
1333
1333
1334 $ log '4::8'
1334 $ log '4::8'
1335 4
1335 4
1336 8
1336 8
1337 $ log '4:8'
1337 $ log '4:8'
1338 4
1338 4
1339 5
1339 5
1340 6
1340 6
1341 7
1341 7
1342 8
1342 8
1343
1343
1344 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
1344 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
1345 4
1345 4
1346 2
1346 2
1347 5
1347 5
1348
1348
1349 $ log 'not 0 and 0:2'
1349 $ log 'not 0 and 0:2'
1350 1
1350 1
1351 2
1351 2
1352 $ log 'not 1 and 0:2'
1352 $ log 'not 1 and 0:2'
1353 0
1353 0
1354 2
1354 2
1355 $ log 'not 2 and 0:2'
1355 $ log 'not 2 and 0:2'
1356 0
1356 0
1357 1
1357 1
1358 $ log '(1 and 2)::'
1358 $ log '(1 and 2)::'
1359 $ log '(1 and 2):'
1359 $ log '(1 and 2):'
1360 $ log '(1 and 2):3'
1360 $ log '(1 and 2):3'
1361 $ log 'sort(head(), -rev)'
1361 $ log 'sort(head(), -rev)'
1362 9
1362 9
1363 7
1363 7
1364 6
1364 6
1365 5
1365 5
1366 4
1366 4
1367 3
1367 3
1368 2
1368 2
1369 1
1369 1
1370 0
1370 0
1371 $ log '4::8 - 8'
1371 $ log '4::8 - 8'
1372 4
1372 4
1373 $ log 'matching(1 or 2 or 3) and (2 or 3 or 1)'
1373 $ log 'matching(1 or 2 or 3) and (2 or 3 or 1)'
1374 2
1374 2
1375 3
1375 3
1376 1
1376 1
1377
1377
1378 $ log 'named("unknown")'
1378 $ log 'named("unknown")'
1379 abort: namespace 'unknown' does not exist!
1379 abort: namespace 'unknown' does not exist!
1380 [255]
1380 [255]
1381 $ log 'named("re:unknown")'
1381 $ log 'named("re:unknown")'
1382 abort: no namespace exists that match 'unknown'!
1382 abort: no namespace exists that match 'unknown'!
1383 [255]
1383 [255]
1384 $ log 'present(named("unknown"))'
1384 $ log 'present(named("unknown"))'
1385 $ log 'present(named("re:unknown"))'
1385 $ log 'present(named("re:unknown"))'
1386
1386
1387 $ log 'tag()'
1387 $ log 'tag()'
1388 6
1388 6
1389 $ log 'named("tags")'
1389 $ log 'named("tags")'
1390 6
1390 6
1391
1391
1392 issue2437
1392 issue2437
1393
1393
1394 $ log '3 and p1(5)'
1394 $ log '3 and p1(5)'
1395 3
1395 3
1396 $ log '4 and p2(6)'
1396 $ log '4 and p2(6)'
1397 4
1397 4
1398 $ log '1 and parents(:2)'
1398 $ log '1 and parents(:2)'
1399 1
1399 1
1400 $ log '2 and children(1:)'
1400 $ log '2 and children(1:)'
1401 2
1401 2
1402 $ log 'roots(all()) or roots(all())'
1402 $ log 'roots(all()) or roots(all())'
1403 0
1403 0
1404 $ hg debugrevspec 'roots(all()) or roots(all())'
1404 $ hg debugrevspec 'roots(all()) or roots(all())'
1405 0
1405 0
1406 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
1406 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
1407 9
1407 9
1408 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
1408 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
1409 4
1409 4
1410
1410
1411 issue2654: report a parse error if the revset was not completely parsed
1411 issue2654: report a parse error if the revset was not completely parsed
1412
1412
1413 $ log '1 OR 2'
1413 $ log '1 OR 2'
1414 hg: parse error at 2: invalid token
1414 hg: parse error at 2: invalid token
1415 [255]
1415 [255]
1416
1416
1417 or operator should preserve ordering:
1417 or operator should preserve ordering:
1418 $ log 'reverse(2::4) or tip'
1418 $ log 'reverse(2::4) or tip'
1419 4
1419 4
1420 2
1420 2
1421 9
1421 9
1422
1422
1423 parentrevspec
1423 parentrevspec
1424
1424
1425 $ log 'merge()^0'
1425 $ log 'merge()^0'
1426 6
1426 6
1427 $ log 'merge()^'
1427 $ log 'merge()^'
1428 5
1428 5
1429 $ log 'merge()^1'
1429 $ log 'merge()^1'
1430 5
1430 5
1431 $ log 'merge()^2'
1431 $ log 'merge()^2'
1432 4
1432 4
1433 $ log 'merge()^^'
1433 $ log 'merge()^^'
1434 3
1434 3
1435 $ log 'merge()^1^'
1435 $ log 'merge()^1^'
1436 3
1436 3
1437 $ log 'merge()^^^'
1437 $ log 'merge()^^^'
1438 1
1438 1
1439
1439
1440 $ log 'merge()~0'
1440 $ log 'merge()~0'
1441 6
1441 6
1442 $ log 'merge()~1'
1442 $ log 'merge()~1'
1443 5
1443 5
1444 $ log 'merge()~2'
1444 $ log 'merge()~2'
1445 3
1445 3
1446 $ log 'merge()~2^1'
1446 $ log 'merge()~2^1'
1447 1
1447 1
1448 $ log 'merge()~3'
1448 $ log 'merge()~3'
1449 1
1449 1
1450
1450
1451 $ log '(-3:tip)^'
1451 $ log '(-3:tip)^'
1452 4
1452 4
1453 6
1453 6
1454 8
1454 8
1455
1455
1456 $ log 'tip^foo'
1456 $ log 'tip^foo'
1457 hg: parse error: ^ expects a number 0, 1, or 2
1457 hg: parse error: ^ expects a number 0, 1, or 2
1458 [255]
1458 [255]
1459
1459
1460 Bogus function gets suggestions
1460 Bogus function gets suggestions
1461 $ log 'add()'
1461 $ log 'add()'
1462 hg: parse error: unknown identifier: add
1462 hg: parse error: unknown identifier: add
1463 (did you mean 'adds'?)
1463 (did you mean 'adds'?)
1464 [255]
1464 [255]
1465 $ log 'added()'
1465 $ log 'added()'
1466 hg: parse error: unknown identifier: added
1466 hg: parse error: unknown identifier: added
1467 (did you mean 'adds'?)
1467 (did you mean 'adds'?)
1468 [255]
1468 [255]
1469 $ log 'remo()'
1469 $ log 'remo()'
1470 hg: parse error: unknown identifier: remo
1470 hg: parse error: unknown identifier: remo
1471 (did you mean one of remote, removes?)
1471 (did you mean one of remote, removes?)
1472 [255]
1472 [255]
1473 $ log 'babar()'
1473 $ log 'babar()'
1474 hg: parse error: unknown identifier: babar
1474 hg: parse error: unknown identifier: babar
1475 [255]
1475 [255]
1476
1476
1477 Bogus function with a similar internal name doesn't suggest the internal name
1477 Bogus function with a similar internal name doesn't suggest the internal name
1478 $ log 'matches()'
1478 $ log 'matches()'
1479 hg: parse error: unknown identifier: matches
1479 hg: parse error: unknown identifier: matches
1480 (did you mean 'matching'?)
1480 (did you mean 'matching'?)
1481 [255]
1481 [255]
1482
1482
1483 Undocumented functions aren't suggested as similar either
1483 Undocumented functions aren't suggested as similar either
1484 $ log 'wdir2()'
1484 $ log 'wdir2()'
1485 hg: parse error: unknown identifier: wdir2
1485 hg: parse error: unknown identifier: wdir2
1486 [255]
1486 [255]
1487
1487
1488 multiple revspecs
1488 multiple revspecs
1489
1489
1490 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
1490 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
1491 8
1491 8
1492 9
1492 9
1493 4
1493 4
1494 5
1494 5
1495 6
1495 6
1496 7
1496 7
1497
1497
1498 test usage in revpair (with "+")
1498 test usage in revpair (with "+")
1499
1499
1500 (real pair)
1500 (real pair)
1501
1501
1502 $ hg diff -r 'tip^^' -r 'tip'
1502 $ hg diff -r 'tip^^' -r 'tip'
1503 diff -r 2326846efdab -r 24286f4ae135 .hgtags
1503 diff -r 2326846efdab -r 24286f4ae135 .hgtags
1504 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1504 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1505 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
1505 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
1506 @@ -0,0 +1,1 @@
1506 @@ -0,0 +1,1 @@
1507 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1507 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1508 $ hg diff -r 'tip^^::tip'
1508 $ hg diff -r 'tip^^::tip'
1509 diff -r 2326846efdab -r 24286f4ae135 .hgtags
1509 diff -r 2326846efdab -r 24286f4ae135 .hgtags
1510 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1510 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1511 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
1511 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
1512 @@ -0,0 +1,1 @@
1512 @@ -0,0 +1,1 @@
1513 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1513 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1514
1514
1515 (single rev)
1515 (single rev)
1516
1516
1517 $ hg diff -r 'tip^' -r 'tip^'
1517 $ hg diff -r 'tip^' -r 'tip^'
1518 $ hg diff -r 'tip^::tip^ or tip^'
1518 $ hg diff -r 'tip^:tip^'
1519
1519
1520 (single rev that does not looks like a range)
1520 (single rev that does not looks like a range)
1521
1521
1522 $ hg diff -r 'tip^::tip^ or tip^'
1523 diff -r d5d0dcbdc4d9 .hgtags
1524 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1525 +++ b/.hgtags * (glob)
1526 @@ -0,0 +1,1 @@
1527 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1522 $ hg diff -r 'tip^ or tip^'
1528 $ hg diff -r 'tip^ or tip^'
1523 diff -r d5d0dcbdc4d9 .hgtags
1529 diff -r d5d0dcbdc4d9 .hgtags
1524 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1530 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1525 +++ b/.hgtags * (glob)
1531 +++ b/.hgtags * (glob)
1526 @@ -0,0 +1,1 @@
1532 @@ -0,0 +1,1 @@
1527 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1533 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1528
1534
1529 (no rev)
1535 (no rev)
1530
1536
1531 $ hg diff -r 'author("babar") or author("celeste")'
1537 $ hg diff -r 'author("babar") or author("celeste")'
1532 abort: empty revision range
1538 abort: empty revision range
1533 [255]
1539 [255]
1534
1540
1535 aliases:
1541 aliases:
1536
1542
1537 $ echo '[revsetalias]' >> .hg/hgrc
1543 $ echo '[revsetalias]' >> .hg/hgrc
1538 $ echo 'm = merge()' >> .hg/hgrc
1544 $ echo 'm = merge()' >> .hg/hgrc
1539 (revset aliases can override builtin revsets)
1545 (revset aliases can override builtin revsets)
1540 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
1546 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
1541 $ echo 'sincem = descendants(m)' >> .hg/hgrc
1547 $ echo 'sincem = descendants(m)' >> .hg/hgrc
1542 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
1548 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
1543 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
1549 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
1544 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
1550 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
1545
1551
1546 $ try m
1552 $ try m
1547 ('symbol', 'm')
1553 ('symbol', 'm')
1548 (func
1554 (func
1549 ('symbol', 'merge')
1555 ('symbol', 'merge')
1550 None)
1556 None)
1551 * set:
1557 * set:
1552 <filteredset
1558 <filteredset
1553 <fullreposet+ 0:9>>
1559 <fullreposet+ 0:9>>
1554 6
1560 6
1555
1561
1556 $ HGPLAIN=1
1562 $ HGPLAIN=1
1557 $ export HGPLAIN
1563 $ export HGPLAIN
1558 $ try m
1564 $ try m
1559 ('symbol', 'm')
1565 ('symbol', 'm')
1560 abort: unknown revision 'm'!
1566 abort: unknown revision 'm'!
1561 [255]
1567 [255]
1562
1568
1563 $ HGPLAINEXCEPT=revsetalias
1569 $ HGPLAINEXCEPT=revsetalias
1564 $ export HGPLAINEXCEPT
1570 $ export HGPLAINEXCEPT
1565 $ try m
1571 $ try m
1566 ('symbol', 'm')
1572 ('symbol', 'm')
1567 (func
1573 (func
1568 ('symbol', 'merge')
1574 ('symbol', 'merge')
1569 None)
1575 None)
1570 * set:
1576 * set:
1571 <filteredset
1577 <filteredset
1572 <fullreposet+ 0:9>>
1578 <fullreposet+ 0:9>>
1573 6
1579 6
1574
1580
1575 $ unset HGPLAIN
1581 $ unset HGPLAIN
1576 $ unset HGPLAINEXCEPT
1582 $ unset HGPLAINEXCEPT
1577
1583
1578 $ try 'p2(.)'
1584 $ try 'p2(.)'
1579 (func
1585 (func
1580 ('symbol', 'p2')
1586 ('symbol', 'p2')
1581 ('symbol', '.'))
1587 ('symbol', '.'))
1582 (func
1588 (func
1583 ('symbol', 'p1')
1589 ('symbol', 'p1')
1584 ('symbol', '.'))
1590 ('symbol', '.'))
1585 * set:
1591 * set:
1586 <baseset+ [8]>
1592 <baseset+ [8]>
1587 8
1593 8
1588
1594
1589 $ HGPLAIN=1
1595 $ HGPLAIN=1
1590 $ export HGPLAIN
1596 $ export HGPLAIN
1591 $ try 'p2(.)'
1597 $ try 'p2(.)'
1592 (func
1598 (func
1593 ('symbol', 'p2')
1599 ('symbol', 'p2')
1594 ('symbol', '.'))
1600 ('symbol', '.'))
1595 * set:
1601 * set:
1596 <baseset+ []>
1602 <baseset+ []>
1597
1603
1598 $ HGPLAINEXCEPT=revsetalias
1604 $ HGPLAINEXCEPT=revsetalias
1599 $ export HGPLAINEXCEPT
1605 $ export HGPLAINEXCEPT
1600 $ try 'p2(.)'
1606 $ try 'p2(.)'
1601 (func
1607 (func
1602 ('symbol', 'p2')
1608 ('symbol', 'p2')
1603 ('symbol', '.'))
1609 ('symbol', '.'))
1604 (func
1610 (func
1605 ('symbol', 'p1')
1611 ('symbol', 'p1')
1606 ('symbol', '.'))
1612 ('symbol', '.'))
1607 * set:
1613 * set:
1608 <baseset+ [8]>
1614 <baseset+ [8]>
1609 8
1615 8
1610
1616
1611 $ unset HGPLAIN
1617 $ unset HGPLAIN
1612 $ unset HGPLAINEXCEPT
1618 $ unset HGPLAINEXCEPT
1613
1619
1614 test alias recursion
1620 test alias recursion
1615
1621
1616 $ try sincem
1622 $ try sincem
1617 ('symbol', 'sincem')
1623 ('symbol', 'sincem')
1618 (func
1624 (func
1619 ('symbol', 'descendants')
1625 ('symbol', 'descendants')
1620 (func
1626 (func
1621 ('symbol', 'merge')
1627 ('symbol', 'merge')
1622 None))
1628 None))
1623 * set:
1629 * set:
1624 <addset+
1630 <addset+
1625 <filteredset
1631 <filteredset
1626 <fullreposet+ 0:9>>,
1632 <fullreposet+ 0:9>>,
1627 <generatorset+>>
1633 <generatorset+>>
1628 6
1634 6
1629 7
1635 7
1630
1636
1631 test infinite recursion
1637 test infinite recursion
1632
1638
1633 $ echo 'recurse1 = recurse2' >> .hg/hgrc
1639 $ echo 'recurse1 = recurse2' >> .hg/hgrc
1634 $ echo 'recurse2 = recurse1' >> .hg/hgrc
1640 $ echo 'recurse2 = recurse1' >> .hg/hgrc
1635 $ try recurse1
1641 $ try recurse1
1636 ('symbol', 'recurse1')
1642 ('symbol', 'recurse1')
1637 hg: parse error: infinite expansion of revset alias "recurse1" detected
1643 hg: parse error: infinite expansion of revset alias "recurse1" detected
1638 [255]
1644 [255]
1639
1645
1640 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
1646 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
1641 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
1647 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
1642 $ try "level2(level1(1, 2), 3)"
1648 $ try "level2(level1(1, 2), 3)"
1643 (func
1649 (func
1644 ('symbol', 'level2')
1650 ('symbol', 'level2')
1645 (list
1651 (list
1646 (func
1652 (func
1647 ('symbol', 'level1')
1653 ('symbol', 'level1')
1648 (list
1654 (list
1649 ('symbol', '1')
1655 ('symbol', '1')
1650 ('symbol', '2')))
1656 ('symbol', '2')))
1651 ('symbol', '3')))
1657 ('symbol', '3')))
1652 (or
1658 (or
1653 ('symbol', '3')
1659 ('symbol', '3')
1654 (or
1660 (or
1655 ('symbol', '1')
1661 ('symbol', '1')
1656 ('symbol', '2')))
1662 ('symbol', '2')))
1657 * set:
1663 * set:
1658 <addset
1664 <addset
1659 <baseset [3]>,
1665 <baseset [3]>,
1660 <baseset [1, 2]>>
1666 <baseset [1, 2]>>
1661 3
1667 3
1662 1
1668 1
1663 2
1669 2
1664
1670
1665 test nesting and variable passing
1671 test nesting and variable passing
1666
1672
1667 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
1673 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
1668 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
1674 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
1669 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
1675 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
1670 $ try 'nested(2:5)'
1676 $ try 'nested(2:5)'
1671 (func
1677 (func
1672 ('symbol', 'nested')
1678 ('symbol', 'nested')
1673 (range
1679 (range
1674 ('symbol', '2')
1680 ('symbol', '2')
1675 ('symbol', '5')))
1681 ('symbol', '5')))
1676 (func
1682 (func
1677 ('symbol', 'max')
1683 ('symbol', 'max')
1678 (range
1684 (range
1679 ('symbol', '2')
1685 ('symbol', '2')
1680 ('symbol', '5')))
1686 ('symbol', '5')))
1681 * set:
1687 * set:
1682 <baseset [5]>
1688 <baseset [5]>
1683 5
1689 5
1684
1690
1685 test chained `or` operations are flattened at parsing phase
1691 test chained `or` operations are flattened at parsing phase
1686
1692
1687 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
1693 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
1688 $ try 'chainedorops(0:1, 1:2, 2:3)'
1694 $ try 'chainedorops(0:1, 1:2, 2:3)'
1689 (func
1695 (func
1690 ('symbol', 'chainedorops')
1696 ('symbol', 'chainedorops')
1691 (list
1697 (list
1692 (list
1698 (list
1693 (range
1699 (range
1694 ('symbol', '0')
1700 ('symbol', '0')
1695 ('symbol', '1'))
1701 ('symbol', '1'))
1696 (range
1702 (range
1697 ('symbol', '1')
1703 ('symbol', '1')
1698 ('symbol', '2')))
1704 ('symbol', '2')))
1699 (range
1705 (range
1700 ('symbol', '2')
1706 ('symbol', '2')
1701 ('symbol', '3'))))
1707 ('symbol', '3'))))
1702 (or
1708 (or
1703 (range
1709 (range
1704 ('symbol', '0')
1710 ('symbol', '0')
1705 ('symbol', '1'))
1711 ('symbol', '1'))
1706 (range
1712 (range
1707 ('symbol', '1')
1713 ('symbol', '1')
1708 ('symbol', '2'))
1714 ('symbol', '2'))
1709 (range
1715 (range
1710 ('symbol', '2')
1716 ('symbol', '2')
1711 ('symbol', '3')))
1717 ('symbol', '3')))
1712 * set:
1718 * set:
1713 <addset
1719 <addset
1714 <spanset+ 0:1>,
1720 <spanset+ 0:1>,
1715 <addset
1721 <addset
1716 <spanset+ 1:2>,
1722 <spanset+ 1:2>,
1717 <spanset+ 2:3>>>
1723 <spanset+ 2:3>>>
1718 0
1724 0
1719 1
1725 1
1720 2
1726 2
1721 3
1727 3
1722
1728
1723 test variable isolation, variable placeholders are rewritten as string
1729 test variable isolation, variable placeholders are rewritten as string
1724 then parsed and matched again as string. Check they do not leak too
1730 then parsed and matched again as string. Check they do not leak too
1725 far away.
1731 far away.
1726
1732
1727 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
1733 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
1728 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
1734 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
1729 $ try 'callinjection(2:5)'
1735 $ try 'callinjection(2:5)'
1730 (func
1736 (func
1731 ('symbol', 'callinjection')
1737 ('symbol', 'callinjection')
1732 (range
1738 (range
1733 ('symbol', '2')
1739 ('symbol', '2')
1734 ('symbol', '5')))
1740 ('symbol', '5')))
1735 (func
1741 (func
1736 ('symbol', 'descendants')
1742 ('symbol', 'descendants')
1737 (func
1743 (func
1738 ('symbol', 'max')
1744 ('symbol', 'max')
1739 ('string', '$1')))
1745 ('string', '$1')))
1740 abort: unknown revision '$1'!
1746 abort: unknown revision '$1'!
1741 [255]
1747 [255]
1742
1748
1743 $ echo 'injectparamasstring2 = max(_aliasarg("$1"))' >> .hg/hgrc
1749 $ echo 'injectparamasstring2 = max(_aliasarg("$1"))' >> .hg/hgrc
1744 $ echo 'callinjection2($1) = descendants(injectparamasstring2)' >> .hg/hgrc
1750 $ echo 'callinjection2($1) = descendants(injectparamasstring2)' >> .hg/hgrc
1745 $ try 'callinjection2(2:5)'
1751 $ try 'callinjection2(2:5)'
1746 (func
1752 (func
1747 ('symbol', 'callinjection2')
1753 ('symbol', 'callinjection2')
1748 (range
1754 (range
1749 ('symbol', '2')
1755 ('symbol', '2')
1750 ('symbol', '5')))
1756 ('symbol', '5')))
1751 abort: failed to parse the definition of revset alias "injectparamasstring2": unknown identifier: _aliasarg
1757 abort: failed to parse the definition of revset alias "injectparamasstring2": unknown identifier: _aliasarg
1752 [255]
1758 [255]
1753 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
1759 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
1754 ('symbol', 'tip')
1760 ('symbol', 'tip')
1755 warning: failed to parse the definition of revset alias "anotherbadone": at 7: not a prefix: end
1761 warning: failed to parse the definition of revset alias "anotherbadone": at 7: not a prefix: end
1756 warning: failed to parse the definition of revset alias "injectparamasstring2": unknown identifier: _aliasarg
1762 warning: failed to parse the definition of revset alias "injectparamasstring2": unknown identifier: _aliasarg
1757 * set:
1763 * set:
1758 <baseset [9]>
1764 <baseset [9]>
1759 9
1765 9
1760 >>> data = file('.hg/hgrc', 'rb').read()
1766 >>> data = file('.hg/hgrc', 'rb').read()
1761 >>> file('.hg/hgrc', 'wb').write(data.replace('_aliasarg', ''))
1767 >>> file('.hg/hgrc', 'wb').write(data.replace('_aliasarg', ''))
1762
1768
1763 $ try 'tip'
1769 $ try 'tip'
1764 ('symbol', 'tip')
1770 ('symbol', 'tip')
1765 * set:
1771 * set:
1766 <baseset [9]>
1772 <baseset [9]>
1767 9
1773 9
1768
1774
1769 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
1775 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
1770 ('symbol', 'tip')
1776 ('symbol', 'tip')
1771 warning: failed to parse the declaration of revset alias "bad name": at 4: invalid token
1777 warning: failed to parse the declaration of revset alias "bad name": at 4: invalid token
1772 * set:
1778 * set:
1773 <baseset [9]>
1779 <baseset [9]>
1774 9
1780 9
1775 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
1781 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
1776 $ try 'strictreplacing("foo", tip)'
1782 $ try 'strictreplacing("foo", tip)'
1777 (func
1783 (func
1778 ('symbol', 'strictreplacing')
1784 ('symbol', 'strictreplacing')
1779 (list
1785 (list
1780 ('string', 'foo')
1786 ('string', 'foo')
1781 ('symbol', 'tip')))
1787 ('symbol', 'tip')))
1782 (or
1788 (or
1783 ('symbol', 'tip')
1789 ('symbol', 'tip')
1784 (func
1790 (func
1785 ('symbol', 'desc')
1791 ('symbol', 'desc')
1786 ('string', '$1')))
1792 ('string', '$1')))
1787 * set:
1793 * set:
1788 <addset
1794 <addset
1789 <baseset [9]>,
1795 <baseset [9]>,
1790 <filteredset
1796 <filteredset
1791 <fullreposet+ 0:9>>>
1797 <fullreposet+ 0:9>>>
1792 9
1798 9
1793
1799
1794 $ try 'd(2:5)'
1800 $ try 'd(2:5)'
1795 (func
1801 (func
1796 ('symbol', 'd')
1802 ('symbol', 'd')
1797 (range
1803 (range
1798 ('symbol', '2')
1804 ('symbol', '2')
1799 ('symbol', '5')))
1805 ('symbol', '5')))
1800 (func
1806 (func
1801 ('symbol', 'reverse')
1807 ('symbol', 'reverse')
1802 (func
1808 (func
1803 ('symbol', 'sort')
1809 ('symbol', 'sort')
1804 (list
1810 (list
1805 (range
1811 (range
1806 ('symbol', '2')
1812 ('symbol', '2')
1807 ('symbol', '5'))
1813 ('symbol', '5'))
1808 ('symbol', 'date'))))
1814 ('symbol', 'date'))))
1809 * set:
1815 * set:
1810 <baseset [4, 5, 3, 2]>
1816 <baseset [4, 5, 3, 2]>
1811 4
1817 4
1812 5
1818 5
1813 3
1819 3
1814 2
1820 2
1815 $ try 'rs(2 or 3, date)'
1821 $ try 'rs(2 or 3, date)'
1816 (func
1822 (func
1817 ('symbol', 'rs')
1823 ('symbol', 'rs')
1818 (list
1824 (list
1819 (or
1825 (or
1820 ('symbol', '2')
1826 ('symbol', '2')
1821 ('symbol', '3'))
1827 ('symbol', '3'))
1822 ('symbol', 'date')))
1828 ('symbol', 'date')))
1823 (func
1829 (func
1824 ('symbol', 'reverse')
1830 ('symbol', 'reverse')
1825 (func
1831 (func
1826 ('symbol', 'sort')
1832 ('symbol', 'sort')
1827 (list
1833 (list
1828 (or
1834 (or
1829 ('symbol', '2')
1835 ('symbol', '2')
1830 ('symbol', '3'))
1836 ('symbol', '3'))
1831 ('symbol', 'date'))))
1837 ('symbol', 'date'))))
1832 * set:
1838 * set:
1833 <baseset [3, 2]>
1839 <baseset [3, 2]>
1834 3
1840 3
1835 2
1841 2
1836 $ try 'rs()'
1842 $ try 'rs()'
1837 (func
1843 (func
1838 ('symbol', 'rs')
1844 ('symbol', 'rs')
1839 None)
1845 None)
1840 hg: parse error: invalid number of arguments: 0
1846 hg: parse error: invalid number of arguments: 0
1841 [255]
1847 [255]
1842 $ try 'rs(2)'
1848 $ try 'rs(2)'
1843 (func
1849 (func
1844 ('symbol', 'rs')
1850 ('symbol', 'rs')
1845 ('symbol', '2'))
1851 ('symbol', '2'))
1846 hg: parse error: invalid number of arguments: 1
1852 hg: parse error: invalid number of arguments: 1
1847 [255]
1853 [255]
1848 $ try 'rs(2, data, 7)'
1854 $ try 'rs(2, data, 7)'
1849 (func
1855 (func
1850 ('symbol', 'rs')
1856 ('symbol', 'rs')
1851 (list
1857 (list
1852 (list
1858 (list
1853 ('symbol', '2')
1859 ('symbol', '2')
1854 ('symbol', 'data'))
1860 ('symbol', 'data'))
1855 ('symbol', '7')))
1861 ('symbol', '7')))
1856 hg: parse error: invalid number of arguments: 3
1862 hg: parse error: invalid number of arguments: 3
1857 [255]
1863 [255]
1858 $ try 'rs4(2 or 3, x, x, date)'
1864 $ try 'rs4(2 or 3, x, x, date)'
1859 (func
1865 (func
1860 ('symbol', 'rs4')
1866 ('symbol', 'rs4')
1861 (list
1867 (list
1862 (list
1868 (list
1863 (list
1869 (list
1864 (or
1870 (or
1865 ('symbol', '2')
1871 ('symbol', '2')
1866 ('symbol', '3'))
1872 ('symbol', '3'))
1867 ('symbol', 'x'))
1873 ('symbol', 'x'))
1868 ('symbol', 'x'))
1874 ('symbol', 'x'))
1869 ('symbol', 'date')))
1875 ('symbol', 'date')))
1870 (func
1876 (func
1871 ('symbol', 'reverse')
1877 ('symbol', 'reverse')
1872 (func
1878 (func
1873 ('symbol', 'sort')
1879 ('symbol', 'sort')
1874 (list
1880 (list
1875 (or
1881 (or
1876 ('symbol', '2')
1882 ('symbol', '2')
1877 ('symbol', '3'))
1883 ('symbol', '3'))
1878 ('symbol', 'date'))))
1884 ('symbol', 'date'))))
1879 * set:
1885 * set:
1880 <baseset [3, 2]>
1886 <baseset [3, 2]>
1881 3
1887 3
1882 2
1888 2
1883
1889
1884 issue4553: check that revset aliases override existing hash prefix
1890 issue4553: check that revset aliases override existing hash prefix
1885
1891
1886 $ hg log -qr e
1892 $ hg log -qr e
1887 6:e0cc66ef77e8
1893 6:e0cc66ef77e8
1888
1894
1889 $ hg log -qr e --config revsetalias.e="all()"
1895 $ hg log -qr e --config revsetalias.e="all()"
1890 0:2785f51eece5
1896 0:2785f51eece5
1891 1:d75937da8da0
1897 1:d75937da8da0
1892 2:5ed5505e9f1c
1898 2:5ed5505e9f1c
1893 3:8528aa5637f2
1899 3:8528aa5637f2
1894 4:2326846efdab
1900 4:2326846efdab
1895 5:904fa392b941
1901 5:904fa392b941
1896 6:e0cc66ef77e8
1902 6:e0cc66ef77e8
1897 7:013af1973af4
1903 7:013af1973af4
1898 8:d5d0dcbdc4d9
1904 8:d5d0dcbdc4d9
1899 9:24286f4ae135
1905 9:24286f4ae135
1900
1906
1901 $ hg log -qr e: --config revsetalias.e="0"
1907 $ hg log -qr e: --config revsetalias.e="0"
1902 0:2785f51eece5
1908 0:2785f51eece5
1903 1:d75937da8da0
1909 1:d75937da8da0
1904 2:5ed5505e9f1c
1910 2:5ed5505e9f1c
1905 3:8528aa5637f2
1911 3:8528aa5637f2
1906 4:2326846efdab
1912 4:2326846efdab
1907 5:904fa392b941
1913 5:904fa392b941
1908 6:e0cc66ef77e8
1914 6:e0cc66ef77e8
1909 7:013af1973af4
1915 7:013af1973af4
1910 8:d5d0dcbdc4d9
1916 8:d5d0dcbdc4d9
1911 9:24286f4ae135
1917 9:24286f4ae135
1912
1918
1913 $ hg log -qr :e --config revsetalias.e="9"
1919 $ hg log -qr :e --config revsetalias.e="9"
1914 0:2785f51eece5
1920 0:2785f51eece5
1915 1:d75937da8da0
1921 1:d75937da8da0
1916 2:5ed5505e9f1c
1922 2:5ed5505e9f1c
1917 3:8528aa5637f2
1923 3:8528aa5637f2
1918 4:2326846efdab
1924 4:2326846efdab
1919 5:904fa392b941
1925 5:904fa392b941
1920 6:e0cc66ef77e8
1926 6:e0cc66ef77e8
1921 7:013af1973af4
1927 7:013af1973af4
1922 8:d5d0dcbdc4d9
1928 8:d5d0dcbdc4d9
1923 9:24286f4ae135
1929 9:24286f4ae135
1924
1930
1925 $ hg log -qr e:
1931 $ hg log -qr e:
1926 6:e0cc66ef77e8
1932 6:e0cc66ef77e8
1927 7:013af1973af4
1933 7:013af1973af4
1928 8:d5d0dcbdc4d9
1934 8:d5d0dcbdc4d9
1929 9:24286f4ae135
1935 9:24286f4ae135
1930
1936
1931 $ hg log -qr :e
1937 $ hg log -qr :e
1932 0:2785f51eece5
1938 0:2785f51eece5
1933 1:d75937da8da0
1939 1:d75937da8da0
1934 2:5ed5505e9f1c
1940 2:5ed5505e9f1c
1935 3:8528aa5637f2
1941 3:8528aa5637f2
1936 4:2326846efdab
1942 4:2326846efdab
1937 5:904fa392b941
1943 5:904fa392b941
1938 6:e0cc66ef77e8
1944 6:e0cc66ef77e8
1939
1945
1940 issue2549 - correct optimizations
1946 issue2549 - correct optimizations
1941
1947
1942 $ log 'limit(1 or 2 or 3, 2) and not 2'
1948 $ log 'limit(1 or 2 or 3, 2) and not 2'
1943 1
1949 1
1944 $ log 'max(1 or 2) and not 2'
1950 $ log 'max(1 or 2) and not 2'
1945 $ log 'min(1 or 2) and not 1'
1951 $ log 'min(1 or 2) and not 1'
1946 $ log 'last(1 or 2, 1) and not 2'
1952 $ log 'last(1 or 2, 1) and not 2'
1947
1953
1948 issue4289 - ordering of built-ins
1954 issue4289 - ordering of built-ins
1949 $ hg log -M -q -r 3:2
1955 $ hg log -M -q -r 3:2
1950 3:8528aa5637f2
1956 3:8528aa5637f2
1951 2:5ed5505e9f1c
1957 2:5ed5505e9f1c
1952
1958
1953 test revsets started with 40-chars hash (issue3669)
1959 test revsets started with 40-chars hash (issue3669)
1954
1960
1955 $ ISSUE3669_TIP=`hg tip --template '{node}'`
1961 $ ISSUE3669_TIP=`hg tip --template '{node}'`
1956 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
1962 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
1957 9
1963 9
1958 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
1964 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
1959 8
1965 8
1960
1966
1961 test or-ed indirect predicates (issue3775)
1967 test or-ed indirect predicates (issue3775)
1962
1968
1963 $ log '6 or 6^1' | sort
1969 $ log '6 or 6^1' | sort
1964 5
1970 5
1965 6
1971 6
1966 $ log '6^1 or 6' | sort
1972 $ log '6^1 or 6' | sort
1967 5
1973 5
1968 6
1974 6
1969 $ log '4 or 4~1' | sort
1975 $ log '4 or 4~1' | sort
1970 2
1976 2
1971 4
1977 4
1972 $ log '4~1 or 4' | sort
1978 $ log '4~1 or 4' | sort
1973 2
1979 2
1974 4
1980 4
1975 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
1981 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
1976 0
1982 0
1977 1
1983 1
1978 2
1984 2
1979 3
1985 3
1980 4
1986 4
1981 5
1987 5
1982 6
1988 6
1983 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
1989 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
1984 0
1990 0
1985 1
1991 1
1986 2
1992 2
1987 3
1993 3
1988 4
1994 4
1989 5
1995 5
1990 6
1996 6
1991
1997
1992 tests for 'remote()' predicate:
1998 tests for 'remote()' predicate:
1993 #. (csets in remote) (id) (remote)
1999 #. (csets in remote) (id) (remote)
1994 1. less than local current branch "default"
2000 1. less than local current branch "default"
1995 2. same with local specified "default"
2001 2. same with local specified "default"
1996 3. more than local specified specified
2002 3. more than local specified specified
1997
2003
1998 $ hg clone --quiet -U . ../remote3
2004 $ hg clone --quiet -U . ../remote3
1999 $ cd ../remote3
2005 $ cd ../remote3
2000 $ hg update -q 7
2006 $ hg update -q 7
2001 $ echo r > r
2007 $ echo r > r
2002 $ hg ci -Aqm 10
2008 $ hg ci -Aqm 10
2003 $ log 'remote()'
2009 $ log 'remote()'
2004 7
2010 7
2005 $ log 'remote("a-b-c-")'
2011 $ log 'remote("a-b-c-")'
2006 2
2012 2
2007 $ cd ../repo
2013 $ cd ../repo
2008 $ log 'remote(".a.b.c.", "../remote3")'
2014 $ log 'remote(".a.b.c.", "../remote3")'
2009
2015
2010 tests for concatenation of strings/symbols by "##"
2016 tests for concatenation of strings/symbols by "##"
2011
2017
2012 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
2018 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
2013 (_concat
2019 (_concat
2014 (_concat
2020 (_concat
2015 (_concat
2021 (_concat
2016 ('symbol', '278')
2022 ('symbol', '278')
2017 ('string', '5f5'))
2023 ('string', '5f5'))
2018 ('symbol', '1ee'))
2024 ('symbol', '1ee'))
2019 ('string', 'ce5'))
2025 ('string', 'ce5'))
2020 ('string', '2785f51eece5')
2026 ('string', '2785f51eece5')
2021 * set:
2027 * set:
2022 <baseset [0]>
2028 <baseset [0]>
2023 0
2029 0
2024
2030
2025 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
2031 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
2026 $ try "cat4(278, '5f5', 1ee, 'ce5')"
2032 $ try "cat4(278, '5f5', 1ee, 'ce5')"
2027 (func
2033 (func
2028 ('symbol', 'cat4')
2034 ('symbol', 'cat4')
2029 (list
2035 (list
2030 (list
2036 (list
2031 (list
2037 (list
2032 ('symbol', '278')
2038 ('symbol', '278')
2033 ('string', '5f5'))
2039 ('string', '5f5'))
2034 ('symbol', '1ee'))
2040 ('symbol', '1ee'))
2035 ('string', 'ce5')))
2041 ('string', 'ce5')))
2036 (_concat
2042 (_concat
2037 (_concat
2043 (_concat
2038 (_concat
2044 (_concat
2039 ('symbol', '278')
2045 ('symbol', '278')
2040 ('string', '5f5'))
2046 ('string', '5f5'))
2041 ('symbol', '1ee'))
2047 ('symbol', '1ee'))
2042 ('string', 'ce5'))
2048 ('string', 'ce5'))
2043 ('string', '2785f51eece5')
2049 ('string', '2785f51eece5')
2044 * set:
2050 * set:
2045 <baseset [0]>
2051 <baseset [0]>
2046 0
2052 0
2047
2053
2048 (check concatenation in alias nesting)
2054 (check concatenation in alias nesting)
2049
2055
2050 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
2056 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
2051 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
2057 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
2052 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
2058 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
2053 0
2059 0
2054
2060
2055 (check operator priority)
2061 (check operator priority)
2056
2062
2057 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
2063 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
2058 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
2064 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
2059 0
2065 0
2060 4
2066 4
2061
2067
2062 $ cd ..
2068 $ cd ..
2063
2069
2064 prepare repository that has "default" branches of multiple roots
2070 prepare repository that has "default" branches of multiple roots
2065
2071
2066 $ hg init namedbranch
2072 $ hg init namedbranch
2067 $ cd namedbranch
2073 $ cd namedbranch
2068
2074
2069 $ echo default0 >> a
2075 $ echo default0 >> a
2070 $ hg ci -Aqm0
2076 $ hg ci -Aqm0
2071 $ echo default1 >> a
2077 $ echo default1 >> a
2072 $ hg ci -m1
2078 $ hg ci -m1
2073
2079
2074 $ hg branch -q stable
2080 $ hg branch -q stable
2075 $ echo stable2 >> a
2081 $ echo stable2 >> a
2076 $ hg ci -m2
2082 $ hg ci -m2
2077 $ echo stable3 >> a
2083 $ echo stable3 >> a
2078 $ hg ci -m3
2084 $ hg ci -m3
2079
2085
2080 $ hg update -q null
2086 $ hg update -q null
2081 $ echo default4 >> a
2087 $ echo default4 >> a
2082 $ hg ci -Aqm4
2088 $ hg ci -Aqm4
2083 $ echo default5 >> a
2089 $ echo default5 >> a
2084 $ hg ci -m5
2090 $ hg ci -m5
2085
2091
2086 "null" revision belongs to "default" branch (issue4683)
2092 "null" revision belongs to "default" branch (issue4683)
2087
2093
2088 $ log 'branch(null)'
2094 $ log 'branch(null)'
2089 0
2095 0
2090 1
2096 1
2091 4
2097 4
2092 5
2098 5
2093
2099
2094 "null" revision belongs to "default" branch, but it shouldn't appear in set
2100 "null" revision belongs to "default" branch, but it shouldn't appear in set
2095 unless explicitly specified (issue4682)
2101 unless explicitly specified (issue4682)
2096
2102
2097 $ log 'children(branch(default))'
2103 $ log 'children(branch(default))'
2098 1
2104 1
2099 2
2105 2
2100 5
2106 5
2101
2107
2102 $ cd ..
2108 $ cd ..
2103
2109
2104 test author/desc/keyword in problematic encoding
2110 test author/desc/keyword in problematic encoding
2105 # unicode: cp932:
2111 # unicode: cp932:
2106 # u30A2 0x83 0x41(= 'A')
2112 # u30A2 0x83 0x41(= 'A')
2107 # u30C2 0x83 0x61(= 'a')
2113 # u30C2 0x83 0x61(= 'a')
2108
2114
2109 $ hg init problematicencoding
2115 $ hg init problematicencoding
2110 $ cd problematicencoding
2116 $ cd problematicencoding
2111
2117
2112 $ python > setup.sh <<EOF
2118 $ python > setup.sh <<EOF
2113 > print u'''
2119 > print u'''
2114 > echo a > text
2120 > echo a > text
2115 > hg add text
2121 > hg add text
2116 > hg --encoding utf-8 commit -u '\u30A2' -m none
2122 > hg --encoding utf-8 commit -u '\u30A2' -m none
2117 > echo b > text
2123 > echo b > text
2118 > hg --encoding utf-8 commit -u '\u30C2' -m none
2124 > hg --encoding utf-8 commit -u '\u30C2' -m none
2119 > echo c > text
2125 > echo c > text
2120 > hg --encoding utf-8 commit -u none -m '\u30A2'
2126 > hg --encoding utf-8 commit -u none -m '\u30A2'
2121 > echo d > text
2127 > echo d > text
2122 > hg --encoding utf-8 commit -u none -m '\u30C2'
2128 > hg --encoding utf-8 commit -u none -m '\u30C2'
2123 > '''.encode('utf-8')
2129 > '''.encode('utf-8')
2124 > EOF
2130 > EOF
2125 $ sh < setup.sh
2131 $ sh < setup.sh
2126
2132
2127 test in problematic encoding
2133 test in problematic encoding
2128 $ python > test.sh <<EOF
2134 $ python > test.sh <<EOF
2129 > print u'''
2135 > print u'''
2130 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
2136 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
2131 > echo ====
2137 > echo ====
2132 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
2138 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
2133 > echo ====
2139 > echo ====
2134 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
2140 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
2135 > echo ====
2141 > echo ====
2136 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
2142 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
2137 > echo ====
2143 > echo ====
2138 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
2144 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
2139 > echo ====
2145 > echo ====
2140 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
2146 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
2141 > '''.encode('cp932')
2147 > '''.encode('cp932')
2142 > EOF
2148 > EOF
2143 $ sh < test.sh
2149 $ sh < test.sh
2144 0
2150 0
2145 ====
2151 ====
2146 1
2152 1
2147 ====
2153 ====
2148 2
2154 2
2149 ====
2155 ====
2150 3
2156 3
2151 ====
2157 ====
2152 0
2158 0
2153 2
2159 2
2154 ====
2160 ====
2155 1
2161 1
2156 3
2162 3
2157
2163
2158 test error message of bad revset
2164 test error message of bad revset
2159 $ hg log -r 'foo\\'
2165 $ hg log -r 'foo\\'
2160 hg: parse error at 3: syntax error in revset 'foo\\'
2166 hg: parse error at 3: syntax error in revset 'foo\\'
2161 [255]
2167 [255]
2162
2168
2163 $ cd ..
2169 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now