##// END OF EJS Templates
revset: lookup descendents for negative arguments to ancestor operator...
David Soria Parra -
r32699:f75d0aa5 default
parent child Browse files
Show More
@@ -1,222 +1,223 b''
1 1 Mercurial supports several ways to specify revisions.
2 2
3 3 Specifying single revisions
4 4 ===========================
5 5
6 6 A plain integer is treated as a revision number. Negative integers are
7 7 treated as sequential offsets from the tip, with -1 denoting the tip,
8 8 -2 denoting the revision prior to the tip, and so forth.
9 9
10 10 A 40-digit hexadecimal string is treated as a unique revision identifier.
11 11 A hexadecimal string less than 40 characters long is treated as a
12 12 unique revision identifier and is referred to as a short-form
13 13 identifier. A short-form identifier is only valid if it is the prefix
14 14 of exactly one full-length identifier.
15 15
16 16 Any other string is treated as a bookmark, tag, or branch name. A
17 17 bookmark is a movable pointer to a revision. A tag is a permanent name
18 18 associated with a revision. A branch name denotes the tipmost open branch head
19 19 of that branch - or if they are all closed, the tipmost closed head of the
20 20 branch. Bookmark, tag, and branch names must not contain the ":" character.
21 21
22 22 The reserved name "tip" always identifies the most recent revision.
23 23
24 24 The reserved name "null" indicates the null revision. This is the
25 25 revision of an empty repository, and the parent of revision 0.
26 26
27 27 The reserved name "." indicates the working directory parent. If no
28 28 working directory is checked out, it is equivalent to null. If an
29 29 uncommitted merge is in progress, "." is the revision of the first
30 30 parent.
31 31
32 32 Finally, commands that expect a single revision (like ``hg update``) also
33 33 accept revsets (see below for details). When given a revset, they use the
34 34 last revision of the revset. A few commands accept two single revisions
35 35 (like ``hg diff``). When given a revset, they use the first and the last
36 36 revisions of the revset.
37 37
38 38 Specifying multiple revisions
39 39 =============================
40 40
41 41 Mercurial supports a functional language for selecting a set of
42 42 revisions. Expressions in this language are called revsets.
43 43
44 44 The language supports a number of predicates which are joined by infix
45 45 operators. Parenthesis can be used for grouping.
46 46
47 47 Identifiers such as branch names may need quoting with single or
48 48 double quotes if they contain characters like ``-`` or if they match
49 49 one of the predefined predicates.
50 50
51 51 Special characters can be used in quoted identifiers by escaping them,
52 52 e.g., ``\n`` is interpreted as a newline. To prevent them from being
53 53 interpreted, strings can be prefixed with ``r``, e.g. ``r'...'``.
54 54
55 55 Operators
56 56 =========
57 57
58 58 There is a single prefix operator:
59 59
60 60 ``not x``
61 61 Changesets not in x. Short form is ``! x``.
62 62
63 63 These are the supported infix operators:
64 64
65 65 ``x::y``
66 66 A DAG range, meaning all changesets that are descendants of x and
67 67 ancestors of y, including x and y themselves. If the first endpoint
68 68 is left out, this is equivalent to ``ancestors(y)``, if the second
69 69 is left out it is equivalent to ``descendants(x)``.
70 70
71 71 An alternative syntax is ``x..y``.
72 72
73 73 ``x:y``
74 74 All changesets with revision numbers between x and y, both
75 75 inclusive. Either endpoint can be left out, they default to 0 and
76 76 tip.
77 77
78 78 ``x and y``
79 79 The intersection of changesets in x and y. Short form is ``x & y``.
80 80
81 81 ``x or y``
82 82 The union of changesets in x and y. There are two alternative short
83 83 forms: ``x | y`` and ``x + y``.
84 84
85 85 ``x - y``
86 86 Changesets in x but not in y.
87 87
88 88 ``x % y``
89 89 Changesets that are ancestors of x but not ancestors of y (i.e. ::x - ::y).
90 90 This is shorthand notation for ``only(x, y)`` (see below). The second
91 91 argument is optional and, if left out, is equivalent to ``only(x)``.
92 92
93 93 ``x^n``
94 94 The nth parent of x, n == 0, 1, or 2.
95 95 For n == 0, x; for n == 1, the first parent of each changeset in x;
96 96 for n == 2, the second parent of changeset in x.
97 97
98 98 ``x~n``
99 99 The nth first ancestor of x; ``x~0`` is x; ``x~3`` is ``x^^^``.
100 For n < 0, the nth unambiguous descendent of x.
100 101
101 102 ``x ## y``
102 103 Concatenate strings and identifiers into one string.
103 104
104 105 All other prefix, infix and postfix operators have lower priority than
105 106 ``##``. For example, ``a1 ## a2~2`` is equivalent to ``(a1 ## a2)~2``.
106 107
107 108 For example::
108 109
109 110 [revsetalias]
110 111 issue(a1) = grep(r'\bissue[ :]?' ## a1 ## r'\b|\bbug\(' ## a1 ## r'\)')
111 112
112 113 ``issue(1234)`` is equivalent to
113 114 ``grep(r'\bissue[ :]?1234\b|\bbug\(1234\)')``
114 115 in this case. This matches against all of "issue 1234", "issue:1234",
115 116 "issue1234" and "bug(1234)".
116 117
117 118 There is a single postfix operator:
118 119
119 120 ``x^``
120 121 Equivalent to ``x^1``, the first parent of each changeset in x.
121 122
122 123 Patterns
123 124 ========
124 125
125 126 Where noted, predicates that perform string matching can accept a pattern
126 127 string. The pattern may be either a literal, or a regular expression. If the
127 128 pattern starts with ``re:``, the remainder of the pattern is treated as a
128 129 regular expression. Otherwise, it is treated as a literal. To match a pattern
129 130 that actually starts with ``re:``, use the prefix ``literal:``.
130 131
131 132 Matching is case-sensitive, unless otherwise noted. To perform a case-
132 133 insensitive match on a case-sensitive predicate, use a regular expression,
133 134 prefixed with ``(?i)``.
134 135
135 136 For example, ``tag(r're:(?i)release')`` matches "release" or "RELEASE"
136 137 or "Release", etc
137 138
138 139 Predicates
139 140 ==========
140 141
141 142 The following predicates are supported:
142 143
143 144 .. predicatesmarker
144 145
145 146 Aliases
146 147 =======
147 148
148 149 New predicates (known as "aliases") can be defined, using any combination of
149 150 existing predicates or other aliases. An alias definition looks like::
150 151
151 152 <alias> = <definition>
152 153
153 154 in the ``revsetalias`` section of a Mercurial configuration file. Arguments
154 155 of the form `a1`, `a2`, etc. are substituted from the alias into the
155 156 definition.
156 157
157 158 For example,
158 159
159 160 ::
160 161
161 162 [revsetalias]
162 163 h = heads()
163 164 d(s) = sort(s, date)
164 165 rs(s, k) = reverse(sort(s, k))
165 166
166 167 defines three aliases, ``h``, ``d``, and ``rs``. ``rs(0:tip, author)`` is
167 168 exactly equivalent to ``reverse(sort(0:tip, author))``.
168 169
169 170 Equivalents
170 171 ===========
171 172
172 173 Command line equivalents for :hg:`log`::
173 174
174 175 -f -> ::.
175 176 -d x -> date(x)
176 177 -k x -> keyword(x)
177 178 -m -> merge()
178 179 -u x -> user(x)
179 180 -b x -> branch(x)
180 181 -P x -> !::x
181 182 -l x -> limit(expr, x)
182 183
183 184 Examples
184 185 ========
185 186
186 187 Some sample queries:
187 188
188 189 - Changesets on the default branch::
189 190
190 191 hg log -r "branch(default)"
191 192
192 193 - Changesets on the default branch since tag 1.5 (excluding merges)::
193 194
194 195 hg log -r "branch(default) and 1.5:: and not merge()"
195 196
196 197 - Open branch heads::
197 198
198 199 hg log -r "head() and not closed()"
199 200
200 201 - Changesets between tags 1.3 and 1.5 mentioning "bug" that affect
201 202 ``hgext/*``::
202 203
203 204 hg log -r "1.3::1.5 and keyword(bug) and file('hgext/*')"
204 205
205 206 - Changesets committed in May 2008, sorted by user::
206 207
207 208 hg log -r "sort(date('May 2008'), user)"
208 209
209 210 - Changesets mentioning "bug" or "issue" that are not in a tagged
210 211 release::
211 212
212 213 hg log -r "(keyword(bug) or keyword(issue)) and not ancestors(tag())"
213 214
214 215 - Update to commit that bookmark @ is pointing too, without activating the
215 216 bookmark (this works because the last revision of the revset is used)::
216 217
217 218 hg update :@
218 219
219 220 - Show diff between tags 1.3 and 1.5 (this works because the first and the
220 221 last revisions of the revset are used)::
221 222
222 223 hg diff -r 1.3::1.5
@@ -1,2328 +1,2349 b''
1 1 # revset.py - revision set queries for mercurial
2 2 #
3 3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import heapq
11 11 import re
12 12
13 13 from .i18n import _
14 14 from . import (
15 15 destutil,
16 16 encoding,
17 17 error,
18 18 hbisect,
19 19 match as matchmod,
20 20 node,
21 21 obsolete as obsmod,
22 22 pathutil,
23 23 phases,
24 24 registrar,
25 25 repoview,
26 26 revsetlang,
27 27 scmutil,
28 28 smartset,
29 29 util,
30 30 )
31 31
32 32 # helpers for processing parsed tree
33 33 getsymbol = revsetlang.getsymbol
34 34 getstring = revsetlang.getstring
35 35 getinteger = revsetlang.getinteger
36 36 getboolean = revsetlang.getboolean
37 37 getlist = revsetlang.getlist
38 38 getrange = revsetlang.getrange
39 39 getargs = revsetlang.getargs
40 40 getargsdict = revsetlang.getargsdict
41 41
42 42 # constants used as an argument of match() and matchany()
43 43 anyorder = revsetlang.anyorder
44 44 defineorder = revsetlang.defineorder
45 45 followorder = revsetlang.followorder
46 46
47 47 baseset = smartset.baseset
48 48 generatorset = smartset.generatorset
49 49 spanset = smartset.spanset
50 50 fullreposet = smartset.fullreposet
51 51
52 52 def _revancestors(repo, revs, followfirst):
53 53 """Like revlog.ancestors(), but supports followfirst."""
54 54 if followfirst:
55 55 cut = 1
56 56 else:
57 57 cut = None
58 58 cl = repo.changelog
59 59
60 60 def iterate():
61 61 revs.sort(reverse=True)
62 62 irevs = iter(revs)
63 63 h = []
64 64
65 65 inputrev = next(irevs, None)
66 66 if inputrev is not None:
67 67 heapq.heappush(h, -inputrev)
68 68
69 69 seen = set()
70 70 while h:
71 71 current = -heapq.heappop(h)
72 72 if current == inputrev:
73 73 inputrev = next(irevs, None)
74 74 if inputrev is not None:
75 75 heapq.heappush(h, -inputrev)
76 76 if current not in seen:
77 77 seen.add(current)
78 78 yield current
79 79 try:
80 80 for parent in cl.parentrevs(current)[:cut]:
81 81 if parent != node.nullrev:
82 82 heapq.heappush(h, -parent)
83 83 except error.WdirUnsupported:
84 84 for parent in repo[current].parents()[:cut]:
85 85 if parent.rev() != node.nullrev:
86 86 heapq.heappush(h, -parent.rev())
87 87
88 88 return generatorset(iterate(), iterasc=False)
89 89
90 90 def _revdescendants(repo, revs, followfirst):
91 91 """Like revlog.descendants() but supports followfirst."""
92 92 if followfirst:
93 93 cut = 1
94 94 else:
95 95 cut = None
96 96
97 97 def iterate():
98 98 cl = repo.changelog
99 99 # XXX this should be 'parentset.min()' assuming 'parentset' is a
100 100 # smartset (and if it is not, it should.)
101 101 first = min(revs)
102 102 nullrev = node.nullrev
103 103 if first == nullrev:
104 104 # Are there nodes with a null first parent and a non-null
105 105 # second one? Maybe. Do we care? Probably not.
106 106 for i in cl:
107 107 yield i
108 108 else:
109 109 seen = set(revs)
110 110 for i in cl.revs(first + 1):
111 111 for x in cl.parentrevs(i)[:cut]:
112 112 if x != nullrev and x in seen:
113 113 seen.add(i)
114 114 yield i
115 115 break
116 116
117 117 return generatorset(iterate(), iterasc=True)
118 118
119 119 def _reachablerootspure(repo, minroot, roots, heads, includepath):
120 120 """return (heads(::<roots> and ::<heads>))
121 121
122 122 If includepath is True, return (<roots>::<heads>)."""
123 123 if not roots:
124 124 return []
125 125 parentrevs = repo.changelog.parentrevs
126 126 roots = set(roots)
127 127 visit = list(heads)
128 128 reachable = set()
129 129 seen = {}
130 130 # prefetch all the things! (because python is slow)
131 131 reached = reachable.add
132 132 dovisit = visit.append
133 133 nextvisit = visit.pop
134 134 # open-code the post-order traversal due to the tiny size of
135 135 # sys.getrecursionlimit()
136 136 while visit:
137 137 rev = nextvisit()
138 138 if rev in roots:
139 139 reached(rev)
140 140 if not includepath:
141 141 continue
142 142 parents = parentrevs(rev)
143 143 seen[rev] = parents
144 144 for parent in parents:
145 145 if parent >= minroot and parent not in seen:
146 146 dovisit(parent)
147 147 if not reachable:
148 148 return baseset()
149 149 if not includepath:
150 150 return reachable
151 151 for rev in sorted(seen):
152 152 for parent in seen[rev]:
153 153 if parent in reachable:
154 154 reached(rev)
155 155 return reachable
156 156
157 157 def reachableroots(repo, roots, heads, includepath=False):
158 158 """return (heads(::<roots> and ::<heads>))
159 159
160 160 If includepath is True, return (<roots>::<heads>)."""
161 161 if not roots:
162 162 return baseset()
163 163 minroot = roots.min()
164 164 roots = list(roots)
165 165 heads = list(heads)
166 166 try:
167 167 revs = repo.changelog.reachableroots(minroot, heads, roots, includepath)
168 168 except AttributeError:
169 169 revs = _reachablerootspure(repo, minroot, roots, heads, includepath)
170 170 revs = baseset(revs)
171 171 revs.sort()
172 172 return revs
173 173
174 174 # helpers
175 175
176 176 def getset(repo, subset, x):
177 177 if not x:
178 178 raise error.ParseError(_("missing argument"))
179 179 return methods[x[0]](repo, subset, *x[1:])
180 180
181 181 def _getrevsource(repo, r):
182 182 extra = repo[r].extra()
183 183 for label in ('source', 'transplant_source', 'rebase_source'):
184 184 if label in extra:
185 185 try:
186 186 return repo[extra[label]].rev()
187 187 except error.RepoLookupError:
188 188 pass
189 189 return None
190 190
191 191 # operator methods
192 192
193 193 def stringset(repo, subset, x):
194 194 x = scmutil.intrev(repo[x])
195 195 if (x in subset
196 196 or x == node.nullrev and isinstance(subset, fullreposet)):
197 197 return baseset([x])
198 198 return baseset()
199 199
200 200 def rangeset(repo, subset, x, y, order):
201 201 m = getset(repo, fullreposet(repo), x)
202 202 n = getset(repo, fullreposet(repo), y)
203 203
204 204 if not m or not n:
205 205 return baseset()
206 206 return _makerangeset(repo, subset, m.first(), n.last(), order)
207 207
208 208 def rangeall(repo, subset, x, order):
209 209 assert x is None
210 210 return _makerangeset(repo, subset, 0, len(repo) - 1, order)
211 211
212 212 def rangepre(repo, subset, y, order):
213 213 # ':y' can't be rewritten to '0:y' since '0' may be hidden
214 214 n = getset(repo, fullreposet(repo), y)
215 215 if not n:
216 216 return baseset()
217 217 return _makerangeset(repo, subset, 0, n.last(), order)
218 218
219 219 def rangepost(repo, subset, x, order):
220 220 m = getset(repo, fullreposet(repo), x)
221 221 if not m:
222 222 return baseset()
223 223 return _makerangeset(repo, subset, m.first(), len(repo) - 1, order)
224 224
225 225 def _makerangeset(repo, subset, m, n, order):
226 226 if m == n:
227 227 r = baseset([m])
228 228 elif n == node.wdirrev:
229 229 r = spanset(repo, m, len(repo)) + baseset([n])
230 230 elif m == node.wdirrev:
231 231 r = baseset([m]) + spanset(repo, len(repo) - 1, n - 1)
232 232 elif m < n:
233 233 r = spanset(repo, m, n + 1)
234 234 else:
235 235 r = spanset(repo, m, n - 1)
236 236
237 237 if order == defineorder:
238 238 return r & subset
239 239 else:
240 240 # carrying the sorting over when possible would be more efficient
241 241 return subset & r
242 242
243 243 def dagrange(repo, subset, x, y, order):
244 244 r = fullreposet(repo)
245 245 xs = reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
246 246 includepath=True)
247 247 return subset & xs
248 248
249 249 def andset(repo, subset, x, y, order):
250 250 return getset(repo, getset(repo, subset, x), y)
251 251
252 252 def differenceset(repo, subset, x, y, order):
253 253 return getset(repo, subset, x) - getset(repo, subset, y)
254 254
255 255 def _orsetlist(repo, subset, xs):
256 256 assert xs
257 257 if len(xs) == 1:
258 258 return getset(repo, subset, xs[0])
259 259 p = len(xs) // 2
260 260 a = _orsetlist(repo, subset, xs[:p])
261 261 b = _orsetlist(repo, subset, xs[p:])
262 262 return a + b
263 263
264 264 def orset(repo, subset, x, order):
265 265 xs = getlist(x)
266 266 if order == followorder:
267 267 # slow path to take the subset order
268 268 return subset & _orsetlist(repo, fullreposet(repo), xs)
269 269 else:
270 270 return _orsetlist(repo, subset, xs)
271 271
272 272 def notset(repo, subset, x, order):
273 273 return subset - getset(repo, subset, x)
274 274
275 275 def listset(repo, subset, *xs):
276 276 raise error.ParseError(_("can't use a list in this context"),
277 277 hint=_('see hg help "revsets.x or y"'))
278 278
279 279 def keyvaluepair(repo, subset, k, v):
280 280 raise error.ParseError(_("can't use a key-value pair in this context"))
281 281
282 282 def func(repo, subset, a, b, order):
283 283 f = getsymbol(a)
284 284 if f in symbols:
285 285 func = symbols[f]
286 286 if getattr(func, '_takeorder', False):
287 287 return func(repo, subset, b, order)
288 288 return func(repo, subset, b)
289 289
290 290 keep = lambda fn: getattr(fn, '__doc__', None) is not None
291 291
292 292 syms = [s for (s, fn) in symbols.items() if keep(fn)]
293 293 raise error.UnknownIdentifier(f, syms)
294 294
295 295 # functions
296 296
297 297 # symbols are callables like:
298 298 # fn(repo, subset, x)
299 299 # with:
300 300 # repo - current repository instance
301 301 # subset - of revisions to be examined
302 302 # x - argument in tree form
303 303 symbols = {}
304 304
305 305 # symbols which can't be used for a DoS attack for any given input
306 306 # (e.g. those which accept regexes as plain strings shouldn't be included)
307 307 # functions that just return a lot of changesets (like all) don't count here
308 308 safesymbols = set()
309 309
310 310 predicate = registrar.revsetpredicate()
311 311
312 312 @predicate('_destupdate')
313 313 def _destupdate(repo, subset, x):
314 314 # experimental revset for update destination
315 315 args = getargsdict(x, 'limit', 'clean')
316 316 return subset & baseset([destutil.destupdate(repo, **args)[0]])
317 317
318 318 @predicate('_destmerge')
319 319 def _destmerge(repo, subset, x):
320 320 # experimental revset for merge destination
321 321 sourceset = None
322 322 if x is not None:
323 323 sourceset = getset(repo, fullreposet(repo), x)
324 324 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
325 325
326 326 @predicate('adds(pattern)', safe=True)
327 327 def adds(repo, subset, x):
328 328 """Changesets that add a file matching pattern.
329 329
330 330 The pattern without explicit kind like ``glob:`` is expected to be
331 331 relative to the current directory and match against a file or a
332 332 directory.
333 333 """
334 334 # i18n: "adds" is a keyword
335 335 pat = getstring(x, _("adds requires a pattern"))
336 336 return checkstatus(repo, subset, pat, 1)
337 337
338 338 @predicate('ancestor(*changeset)', safe=True)
339 339 def ancestor(repo, subset, x):
340 340 """A greatest common ancestor of the changesets.
341 341
342 342 Accepts 0 or more changesets.
343 343 Will return empty list when passed no args.
344 344 Greatest common ancestor of a single changeset is that changeset.
345 345 """
346 346 # i18n: "ancestor" is a keyword
347 347 l = getlist(x)
348 348 rl = fullreposet(repo)
349 349 anc = None
350 350
351 351 # (getset(repo, rl, i) for i in l) generates a list of lists
352 352 for revs in (getset(repo, rl, i) for i in l):
353 353 for r in revs:
354 354 if anc is None:
355 355 anc = repo[r]
356 356 else:
357 357 anc = anc.ancestor(repo[r])
358 358
359 359 if anc is not None and anc.rev() in subset:
360 360 return baseset([anc.rev()])
361 361 return baseset()
362 362
363 363 def _ancestors(repo, subset, x, followfirst=False):
364 364 heads = getset(repo, fullreposet(repo), x)
365 365 if not heads:
366 366 return baseset()
367 367 s = _revancestors(repo, heads, followfirst)
368 368 return subset & s
369 369
370 370 @predicate('ancestors(set)', safe=True)
371 371 def ancestors(repo, subset, x):
372 372 """Changesets that are ancestors of a changeset in set.
373 373 """
374 374 return _ancestors(repo, subset, x)
375 375
376 376 @predicate('_firstancestors', safe=True)
377 377 def _firstancestors(repo, subset, x):
378 378 # ``_firstancestors(set)``
379 379 # Like ``ancestors(set)`` but follows only the first parents.
380 380 return _ancestors(repo, subset, x, followfirst=True)
381 381
382 def _childrenspec(repo, subset, x, n, order):
383 """Changesets that are the Nth child of a changeset
384 in set.
385 """
386 cs = set()
387 for r in getset(repo, fullreposet(repo), x):
388 for i in range(n):
389 c = repo[r].children()
390 if len(c) == 0:
391 break
392 if len(c) > 1:
393 raise error.RepoLookupError(
394 _("revision in set has more than one child"))
395 r = c[0]
396 else:
397 cs.add(r)
398 return subset & cs
399
382 400 def ancestorspec(repo, subset, x, n, order):
383 401 """``set~n``
384 402 Changesets that are the Nth ancestor (first parents only) of a changeset
385 403 in set.
386 404 """
387 405 n = getinteger(n, _("~ expects a number"))
406 if n < 0:
407 # children lookup
408 return _childrenspec(repo, subset, x, -n, order)
388 409 ps = set()
389 410 cl = repo.changelog
390 411 for r in getset(repo, fullreposet(repo), x):
391 412 for i in range(n):
392 413 try:
393 414 r = cl.parentrevs(r)[0]
394 415 except error.WdirUnsupported:
395 416 r = repo[r].parents()[0].rev()
396 417 ps.add(r)
397 418 return subset & ps
398 419
399 420 @predicate('author(string)', safe=True)
400 421 def author(repo, subset, x):
401 422 """Alias for ``user(string)``.
402 423 """
403 424 # i18n: "author" is a keyword
404 425 n = getstring(x, _("author requires a string"))
405 426 kind, pattern, matcher = _substringmatcher(n, casesensitive=False)
406 427 return subset.filter(lambda x: matcher(repo[x].user()),
407 428 condrepr=('<user %r>', n))
408 429
409 430 @predicate('bisect(string)', safe=True)
410 431 def bisect(repo, subset, x):
411 432 """Changesets marked in the specified bisect status:
412 433
413 434 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
414 435 - ``goods``, ``bads`` : csets topologically good/bad
415 436 - ``range`` : csets taking part in the bisection
416 437 - ``pruned`` : csets that are goods, bads or skipped
417 438 - ``untested`` : csets whose fate is yet unknown
418 439 - ``ignored`` : csets ignored due to DAG topology
419 440 - ``current`` : the cset currently being bisected
420 441 """
421 442 # i18n: "bisect" is a keyword
422 443 status = getstring(x, _("bisect requires a string")).lower()
423 444 state = set(hbisect.get(repo, status))
424 445 return subset & state
425 446
426 447 # Backward-compatibility
427 448 # - no help entry so that we do not advertise it any more
428 449 @predicate('bisected', safe=True)
429 450 def bisected(repo, subset, x):
430 451 return bisect(repo, subset, x)
431 452
432 453 @predicate('bookmark([name])', safe=True)
433 454 def bookmark(repo, subset, x):
434 455 """The named bookmark or all bookmarks.
435 456
436 457 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`.
437 458 """
438 459 # i18n: "bookmark" is a keyword
439 460 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
440 461 if args:
441 462 bm = getstring(args[0],
442 463 # i18n: "bookmark" is a keyword
443 464 _('the argument to bookmark must be a string'))
444 465 kind, pattern, matcher = util.stringmatcher(bm)
445 466 bms = set()
446 467 if kind == 'literal':
447 468 bmrev = repo._bookmarks.get(pattern, None)
448 469 if not bmrev:
449 470 raise error.RepoLookupError(_("bookmark '%s' does not exist")
450 471 % pattern)
451 472 bms.add(repo[bmrev].rev())
452 473 else:
453 474 matchrevs = set()
454 475 for name, bmrev in repo._bookmarks.iteritems():
455 476 if matcher(name):
456 477 matchrevs.add(bmrev)
457 478 if not matchrevs:
458 479 raise error.RepoLookupError(_("no bookmarks exist"
459 480 " that match '%s'") % pattern)
460 481 for bmrev in matchrevs:
461 482 bms.add(repo[bmrev].rev())
462 483 else:
463 484 bms = {repo[r].rev() for r in repo._bookmarks.values()}
464 485 bms -= {node.nullrev}
465 486 return subset & bms
466 487
467 488 @predicate('branch(string or set)', safe=True)
468 489 def branch(repo, subset, x):
469 490 """
470 491 All changesets belonging to the given branch or the branches of the given
471 492 changesets.
472 493
473 494 Pattern matching is supported for `string`. See
474 495 :hg:`help revisions.patterns`.
475 496 """
476 497 getbi = repo.revbranchcache().branchinfo
477 498 def getbranch(r):
478 499 try:
479 500 return getbi(r)[0]
480 501 except error.WdirUnsupported:
481 502 return repo[r].branch()
482 503
483 504 try:
484 505 b = getstring(x, '')
485 506 except error.ParseError:
486 507 # not a string, but another revspec, e.g. tip()
487 508 pass
488 509 else:
489 510 kind, pattern, matcher = util.stringmatcher(b)
490 511 if kind == 'literal':
491 512 # note: falls through to the revspec case if no branch with
492 513 # this name exists and pattern kind is not specified explicitly
493 514 if pattern in repo.branchmap():
494 515 return subset.filter(lambda r: matcher(getbranch(r)),
495 516 condrepr=('<branch %r>', b))
496 517 if b.startswith('literal:'):
497 518 raise error.RepoLookupError(_("branch '%s' does not exist")
498 519 % pattern)
499 520 else:
500 521 return subset.filter(lambda r: matcher(getbranch(r)),
501 522 condrepr=('<branch %r>', b))
502 523
503 524 s = getset(repo, fullreposet(repo), x)
504 525 b = set()
505 526 for r in s:
506 527 b.add(getbranch(r))
507 528 c = s.__contains__
508 529 return subset.filter(lambda r: c(r) or getbranch(r) in b,
509 530 condrepr=lambda: '<branch %r>' % sorted(b))
510 531
511 532 @predicate('bumped()', safe=True)
512 533 def bumped(repo, subset, x):
513 534 """Mutable changesets marked as successors of public changesets.
514 535
515 536 Only non-public and non-obsolete changesets can be `bumped`.
516 537 """
517 538 # i18n: "bumped" is a keyword
518 539 getargs(x, 0, 0, _("bumped takes no arguments"))
519 540 bumped = obsmod.getrevs(repo, 'bumped')
520 541 return subset & bumped
521 542
522 543 @predicate('bundle()', safe=True)
523 544 def bundle(repo, subset, x):
524 545 """Changesets in the bundle.
525 546
526 547 Bundle must be specified by the -R option."""
527 548
528 549 try:
529 550 bundlerevs = repo.changelog.bundlerevs
530 551 except AttributeError:
531 552 raise error.Abort(_("no bundle provided - specify with -R"))
532 553 return subset & bundlerevs
533 554
534 555 def checkstatus(repo, subset, pat, field):
535 556 hasset = matchmod.patkind(pat) == 'set'
536 557
537 558 mcache = [None]
538 559 def matches(x):
539 560 c = repo[x]
540 561 if not mcache[0] or hasset:
541 562 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
542 563 m = mcache[0]
543 564 fname = None
544 565 if not m.anypats() and len(m.files()) == 1:
545 566 fname = m.files()[0]
546 567 if fname is not None:
547 568 if fname not in c.files():
548 569 return False
549 570 else:
550 571 for f in c.files():
551 572 if m(f):
552 573 break
553 574 else:
554 575 return False
555 576 files = repo.status(c.p1().node(), c.node())[field]
556 577 if fname is not None:
557 578 if fname in files:
558 579 return True
559 580 else:
560 581 for f in files:
561 582 if m(f):
562 583 return True
563 584
564 585 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
565 586
566 587 def _children(repo, subset, parentset):
567 588 if not parentset:
568 589 return baseset()
569 590 cs = set()
570 591 pr = repo.changelog.parentrevs
571 592 minrev = parentset.min()
572 593 nullrev = node.nullrev
573 594 for r in subset:
574 595 if r <= minrev:
575 596 continue
576 597 p1, p2 = pr(r)
577 598 if p1 in parentset:
578 599 cs.add(r)
579 600 if p2 != nullrev and p2 in parentset:
580 601 cs.add(r)
581 602 return baseset(cs)
582 603
583 604 @predicate('children(set)', safe=True)
584 605 def children(repo, subset, x):
585 606 """Child changesets of changesets in set.
586 607 """
587 608 s = getset(repo, fullreposet(repo), x)
588 609 cs = _children(repo, subset, s)
589 610 return subset & cs
590 611
591 612 @predicate('closed()', safe=True)
592 613 def closed(repo, subset, x):
593 614 """Changeset is closed.
594 615 """
595 616 # i18n: "closed" is a keyword
596 617 getargs(x, 0, 0, _("closed takes no arguments"))
597 618 return subset.filter(lambda r: repo[r].closesbranch(),
598 619 condrepr='<branch closed>')
599 620
600 621 @predicate('contains(pattern)')
601 622 def contains(repo, subset, x):
602 623 """The revision's manifest contains a file matching pattern (but might not
603 624 modify it). See :hg:`help patterns` for information about file patterns.
604 625
605 626 The pattern without explicit kind like ``glob:`` is expected to be
606 627 relative to the current directory and match against a file exactly
607 628 for efficiency.
608 629 """
609 630 # i18n: "contains" is a keyword
610 631 pat = getstring(x, _("contains requires a pattern"))
611 632
612 633 def matches(x):
613 634 if not matchmod.patkind(pat):
614 635 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
615 636 if pats in repo[x]:
616 637 return True
617 638 else:
618 639 c = repo[x]
619 640 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
620 641 for f in c.manifest():
621 642 if m(f):
622 643 return True
623 644 return False
624 645
625 646 return subset.filter(matches, condrepr=('<contains %r>', pat))
626 647
627 648 @predicate('converted([id])', safe=True)
628 649 def converted(repo, subset, x):
629 650 """Changesets converted from the given identifier in the old repository if
630 651 present, or all converted changesets if no identifier is specified.
631 652 """
632 653
633 654 # There is exactly no chance of resolving the revision, so do a simple
634 655 # string compare and hope for the best
635 656
636 657 rev = None
637 658 # i18n: "converted" is a keyword
638 659 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
639 660 if l:
640 661 # i18n: "converted" is a keyword
641 662 rev = getstring(l[0], _('converted requires a revision'))
642 663
643 664 def _matchvalue(r):
644 665 source = repo[r].extra().get('convert_revision', None)
645 666 return source is not None and (rev is None or source.startswith(rev))
646 667
647 668 return subset.filter(lambda r: _matchvalue(r),
648 669 condrepr=('<converted %r>', rev))
649 670
650 671 @predicate('date(interval)', safe=True)
651 672 def date(repo, subset, x):
652 673 """Changesets within the interval, see :hg:`help dates`.
653 674 """
654 675 # i18n: "date" is a keyword
655 676 ds = getstring(x, _("date requires a string"))
656 677 dm = util.matchdate(ds)
657 678 return subset.filter(lambda x: dm(repo[x].date()[0]),
658 679 condrepr=('<date %r>', ds))
659 680
660 681 @predicate('desc(string)', safe=True)
661 682 def desc(repo, subset, x):
662 683 """Search commit message for string. The match is case-insensitive.
663 684
664 685 Pattern matching is supported for `string`. See
665 686 :hg:`help revisions.patterns`.
666 687 """
667 688 # i18n: "desc" is a keyword
668 689 ds = getstring(x, _("desc requires a string"))
669 690
670 691 kind, pattern, matcher = _substringmatcher(ds, casesensitive=False)
671 692
672 693 return subset.filter(lambda r: matcher(repo[r].description()),
673 694 condrepr=('<desc %r>', ds))
674 695
675 696 def _descendants(repo, subset, x, followfirst=False):
676 697 roots = getset(repo, fullreposet(repo), x)
677 698 if not roots:
678 699 return baseset()
679 700 s = _revdescendants(repo, roots, followfirst)
680 701
681 702 # Both sets need to be ascending in order to lazily return the union
682 703 # in the correct order.
683 704 base = subset & roots
684 705 desc = subset & s
685 706 result = base + desc
686 707 if subset.isascending():
687 708 result.sort()
688 709 elif subset.isdescending():
689 710 result.sort(reverse=True)
690 711 else:
691 712 result = subset & result
692 713 return result
693 714
694 715 @predicate('descendants(set)', safe=True)
695 716 def descendants(repo, subset, x):
696 717 """Changesets which are descendants of changesets in set.
697 718 """
698 719 return _descendants(repo, subset, x)
699 720
700 721 @predicate('_firstdescendants', safe=True)
701 722 def _firstdescendants(repo, subset, x):
702 723 # ``_firstdescendants(set)``
703 724 # Like ``descendants(set)`` but follows only the first parents.
704 725 return _descendants(repo, subset, x, followfirst=True)
705 726
706 727 @predicate('destination([set])', safe=True)
707 728 def destination(repo, subset, x):
708 729 """Changesets that were created by a graft, transplant or rebase operation,
709 730 with the given revisions specified as the source. Omitting the optional set
710 731 is the same as passing all().
711 732 """
712 733 if x is not None:
713 734 sources = getset(repo, fullreposet(repo), x)
714 735 else:
715 736 sources = fullreposet(repo)
716 737
717 738 dests = set()
718 739
719 740 # subset contains all of the possible destinations that can be returned, so
720 741 # iterate over them and see if their source(s) were provided in the arg set.
721 742 # Even if the immediate src of r is not in the arg set, src's source (or
722 743 # further back) may be. Scanning back further than the immediate src allows
723 744 # transitive transplants and rebases to yield the same results as transitive
724 745 # grafts.
725 746 for r in subset:
726 747 src = _getrevsource(repo, r)
727 748 lineage = None
728 749
729 750 while src is not None:
730 751 if lineage is None:
731 752 lineage = list()
732 753
733 754 lineage.append(r)
734 755
735 756 # The visited lineage is a match if the current source is in the arg
736 757 # set. Since every candidate dest is visited by way of iterating
737 758 # subset, any dests further back in the lineage will be tested by a
738 759 # different iteration over subset. Likewise, if the src was already
739 760 # selected, the current lineage can be selected without going back
740 761 # further.
741 762 if src in sources or src in dests:
742 763 dests.update(lineage)
743 764 break
744 765
745 766 r = src
746 767 src = _getrevsource(repo, r)
747 768
748 769 return subset.filter(dests.__contains__,
749 770 condrepr=lambda: '<destination %r>' % sorted(dests))
750 771
751 772 @predicate('divergent()', safe=True)
752 773 def divergent(repo, subset, x):
753 774 """
754 775 Final successors of changesets with an alternative set of final successors.
755 776 """
756 777 # i18n: "divergent" is a keyword
757 778 getargs(x, 0, 0, _("divergent takes no arguments"))
758 779 divergent = obsmod.getrevs(repo, 'divergent')
759 780 return subset & divergent
760 781
761 782 @predicate('extinct()', safe=True)
762 783 def extinct(repo, subset, x):
763 784 """Obsolete changesets with obsolete descendants only.
764 785 """
765 786 # i18n: "extinct" is a keyword
766 787 getargs(x, 0, 0, _("extinct takes no arguments"))
767 788 extincts = obsmod.getrevs(repo, 'extinct')
768 789 return subset & extincts
769 790
770 791 @predicate('extra(label, [value])', safe=True)
771 792 def extra(repo, subset, x):
772 793 """Changesets with the given label in the extra metadata, with the given
773 794 optional value.
774 795
775 796 Pattern matching is supported for `value`. See
776 797 :hg:`help revisions.patterns`.
777 798 """
778 799 args = getargsdict(x, 'extra', 'label value')
779 800 if 'label' not in args:
780 801 # i18n: "extra" is a keyword
781 802 raise error.ParseError(_('extra takes at least 1 argument'))
782 803 # i18n: "extra" is a keyword
783 804 label = getstring(args['label'], _('first argument to extra must be '
784 805 'a string'))
785 806 value = None
786 807
787 808 if 'value' in args:
788 809 # i18n: "extra" is a keyword
789 810 value = getstring(args['value'], _('second argument to extra must be '
790 811 'a string'))
791 812 kind, value, matcher = util.stringmatcher(value)
792 813
793 814 def _matchvalue(r):
794 815 extra = repo[r].extra()
795 816 return label in extra and (value is None or matcher(extra[label]))
796 817
797 818 return subset.filter(lambda r: _matchvalue(r),
798 819 condrepr=('<extra[%r] %r>', label, value))
799 820
800 821 @predicate('filelog(pattern)', safe=True)
801 822 def filelog(repo, subset, x):
802 823 """Changesets connected to the specified filelog.
803 824
804 825 For performance reasons, visits only revisions mentioned in the file-level
805 826 filelog, rather than filtering through all changesets (much faster, but
806 827 doesn't include deletes or duplicate changes). For a slower, more accurate
807 828 result, use ``file()``.
808 829
809 830 The pattern without explicit kind like ``glob:`` is expected to be
810 831 relative to the current directory and match against a file exactly
811 832 for efficiency.
812 833
813 834 If some linkrev points to revisions filtered by the current repoview, we'll
814 835 work around it to return a non-filtered value.
815 836 """
816 837
817 838 # i18n: "filelog" is a keyword
818 839 pat = getstring(x, _("filelog requires a pattern"))
819 840 s = set()
820 841 cl = repo.changelog
821 842
822 843 if not matchmod.patkind(pat):
823 844 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
824 845 files = [f]
825 846 else:
826 847 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
827 848 files = (f for f in repo[None] if m(f))
828 849
829 850 for f in files:
830 851 fl = repo.file(f)
831 852 known = {}
832 853 scanpos = 0
833 854 for fr in list(fl):
834 855 fn = fl.node(fr)
835 856 if fn in known:
836 857 s.add(known[fn])
837 858 continue
838 859
839 860 lr = fl.linkrev(fr)
840 861 if lr in cl:
841 862 s.add(lr)
842 863 elif scanpos is not None:
843 864 # lowest matching changeset is filtered, scan further
844 865 # ahead in changelog
845 866 start = max(lr, scanpos) + 1
846 867 scanpos = None
847 868 for r in cl.revs(start):
848 869 # minimize parsing of non-matching entries
849 870 if f in cl.revision(r) and f in cl.readfiles(r):
850 871 try:
851 872 # try to use manifest delta fastpath
852 873 n = repo[r].filenode(f)
853 874 if n not in known:
854 875 if n == fn:
855 876 s.add(r)
856 877 scanpos = r
857 878 break
858 879 else:
859 880 known[n] = r
860 881 except error.ManifestLookupError:
861 882 # deletion in changelog
862 883 continue
863 884
864 885 return subset & s
865 886
866 887 @predicate('first(set, [n])', safe=True)
867 888 def first(repo, subset, x):
868 889 """An alias for limit().
869 890 """
870 891 return limit(repo, subset, x)
871 892
872 893 def _follow(repo, subset, x, name, followfirst=False):
873 894 l = getargs(x, 0, 2, _("%s takes no arguments or a pattern "
874 895 "and an optional revset") % name)
875 896 c = repo['.']
876 897 if l:
877 898 x = getstring(l[0], _("%s expected a pattern") % name)
878 899 rev = None
879 900 if len(l) >= 2:
880 901 revs = getset(repo, fullreposet(repo), l[1])
881 902 if len(revs) != 1:
882 903 raise error.RepoLookupError(
883 904 _("%s expected one starting revision") % name)
884 905 rev = revs.last()
885 906 c = repo[rev]
886 907 matcher = matchmod.match(repo.root, repo.getcwd(), [x],
887 908 ctx=repo[rev], default='path')
888 909
889 910 files = c.manifest().walk(matcher)
890 911
891 912 s = set()
892 913 for fname in files:
893 914 fctx = c[fname]
894 915 s = s.union(set(c.rev() for c in fctx.ancestors(followfirst)))
895 916 # include the revision responsible for the most recent version
896 917 s.add(fctx.introrev())
897 918 else:
898 919 s = _revancestors(repo, baseset([c.rev()]), followfirst)
899 920
900 921 return subset & s
901 922
902 923 @predicate('follow([pattern[, startrev]])', safe=True)
903 924 def follow(repo, subset, x):
904 925 """
905 926 An alias for ``::.`` (ancestors of the working directory's first parent).
906 927 If pattern is specified, the histories of files matching given
907 928 pattern in the revision given by startrev are followed, including copies.
908 929 """
909 930 return _follow(repo, subset, x, 'follow')
910 931
911 932 @predicate('_followfirst', safe=True)
912 933 def _followfirst(repo, subset, x):
913 934 # ``followfirst([pattern[, startrev]])``
914 935 # Like ``follow([pattern[, startrev]])`` but follows only the first parent
915 936 # of every revisions or files revisions.
916 937 return _follow(repo, subset, x, '_followfirst', followfirst=True)
917 938
918 939 @predicate('followlines(file, fromline:toline[, startrev=., descend=False])',
919 940 safe=True)
920 941 def followlines(repo, subset, x):
921 942 """Changesets modifying `file` in line range ('fromline', 'toline').
922 943
923 944 Line range corresponds to 'file' content at 'startrev' and should hence be
924 945 consistent with file size. If startrev is not specified, working directory's
925 946 parent is used.
926 947
927 948 By default, ancestors of 'startrev' are returned. If 'descend' is True,
928 949 descendants of 'startrev' are returned though renames are (currently) not
929 950 followed in this direction.
930 951 """
931 952 from . import context # avoid circular import issues
932 953
933 954 args = getargsdict(x, 'followlines', 'file *lines startrev descend')
934 955 if len(args['lines']) != 1:
935 956 raise error.ParseError(_("followlines requires a line range"))
936 957
937 958 rev = '.'
938 959 if 'startrev' in args:
939 960 revs = getset(repo, fullreposet(repo), args['startrev'])
940 961 if len(revs) != 1:
941 962 raise error.ParseError(
942 963 # i18n: "followlines" is a keyword
943 964 _("followlines expects exactly one revision"))
944 965 rev = revs.last()
945 966
946 967 pat = getstring(args['file'], _("followlines requires a pattern"))
947 968 if not matchmod.patkind(pat):
948 969 fname = pathutil.canonpath(repo.root, repo.getcwd(), pat)
949 970 else:
950 971 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[rev])
951 972 files = [f for f in repo[rev] if m(f)]
952 973 if len(files) != 1:
953 974 # i18n: "followlines" is a keyword
954 975 raise error.ParseError(_("followlines expects exactly one file"))
955 976 fname = files[0]
956 977
957 978 # i18n: "followlines" is a keyword
958 979 lr = getrange(args['lines'][0], _("followlines expects a line range"))
959 980 fromline, toline = [getinteger(a, _("line range bounds must be integers"))
960 981 for a in lr]
961 982 fromline, toline = util.processlinerange(fromline, toline)
962 983
963 984 fctx = repo[rev].filectx(fname)
964 985 descend = False
965 986 if 'descend' in args:
966 987 descend = getboolean(args['descend'],
967 988 # i18n: "descend" is a keyword
968 989 _("descend argument must be a boolean"))
969 990 if descend:
970 991 rs = generatorset(
971 992 (c.rev() for c, _linerange
972 993 in context.blockdescendants(fctx, fromline, toline)),
973 994 iterasc=True)
974 995 else:
975 996 rs = generatorset(
976 997 (c.rev() for c, _linerange
977 998 in context.blockancestors(fctx, fromline, toline)),
978 999 iterasc=False)
979 1000 return subset & rs
980 1001
981 1002 @predicate('all()', safe=True)
982 1003 def getall(repo, subset, x):
983 1004 """All changesets, the same as ``0:tip``.
984 1005 """
985 1006 # i18n: "all" is a keyword
986 1007 getargs(x, 0, 0, _("all takes no arguments"))
987 1008 return subset & spanset(repo) # drop "null" if any
988 1009
989 1010 @predicate('grep(regex)')
990 1011 def grep(repo, subset, x):
991 1012 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
992 1013 to ensure special escape characters are handled correctly. Unlike
993 1014 ``keyword(string)``, the match is case-sensitive.
994 1015 """
995 1016 try:
996 1017 # i18n: "grep" is a keyword
997 1018 gr = re.compile(getstring(x, _("grep requires a string")))
998 1019 except re.error as e:
999 1020 raise error.ParseError(_('invalid match pattern: %s') % e)
1000 1021
1001 1022 def matches(x):
1002 1023 c = repo[x]
1003 1024 for e in c.files() + [c.user(), c.description()]:
1004 1025 if gr.search(e):
1005 1026 return True
1006 1027 return False
1007 1028
1008 1029 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1009 1030
1010 1031 @predicate('_matchfiles', safe=True)
1011 1032 def _matchfiles(repo, subset, x):
1012 1033 # _matchfiles takes a revset list of prefixed arguments:
1013 1034 #
1014 1035 # [p:foo, i:bar, x:baz]
1015 1036 #
1016 1037 # builds a match object from them and filters subset. Allowed
1017 1038 # prefixes are 'p:' for regular patterns, 'i:' for include
1018 1039 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1019 1040 # a revision identifier, or the empty string to reference the
1020 1041 # working directory, from which the match object is
1021 1042 # initialized. Use 'd:' to set the default matching mode, default
1022 1043 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1023 1044
1024 1045 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1025 1046 pats, inc, exc = [], [], []
1026 1047 rev, default = None, None
1027 1048 for arg in l:
1028 1049 s = getstring(arg, "_matchfiles requires string arguments")
1029 1050 prefix, value = s[:2], s[2:]
1030 1051 if prefix == 'p:':
1031 1052 pats.append(value)
1032 1053 elif prefix == 'i:':
1033 1054 inc.append(value)
1034 1055 elif prefix == 'x:':
1035 1056 exc.append(value)
1036 1057 elif prefix == 'r:':
1037 1058 if rev is not None:
1038 1059 raise error.ParseError('_matchfiles expected at most one '
1039 1060 'revision')
1040 1061 if value != '': # empty means working directory; leave rev as None
1041 1062 rev = value
1042 1063 elif prefix == 'd:':
1043 1064 if default is not None:
1044 1065 raise error.ParseError('_matchfiles expected at most one '
1045 1066 'default mode')
1046 1067 default = value
1047 1068 else:
1048 1069 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1049 1070 if not default:
1050 1071 default = 'glob'
1051 1072
1052 1073 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1053 1074 exclude=exc, ctx=repo[rev], default=default)
1054 1075
1055 1076 # This directly read the changelog data as creating changectx for all
1056 1077 # revisions is quite expensive.
1057 1078 getfiles = repo.changelog.readfiles
1058 1079 wdirrev = node.wdirrev
1059 1080 def matches(x):
1060 1081 if x == wdirrev:
1061 1082 files = repo[x].files()
1062 1083 else:
1063 1084 files = getfiles(x)
1064 1085 for f in files:
1065 1086 if m(f):
1066 1087 return True
1067 1088 return False
1068 1089
1069 1090 return subset.filter(matches,
1070 1091 condrepr=('<matchfiles patterns=%r, include=%r '
1071 1092 'exclude=%r, default=%r, rev=%r>',
1072 1093 pats, inc, exc, default, rev))
1073 1094
1074 1095 @predicate('file(pattern)', safe=True)
1075 1096 def hasfile(repo, subset, x):
1076 1097 """Changesets affecting files matched by pattern.
1077 1098
1078 1099 For a faster but less accurate result, consider using ``filelog()``
1079 1100 instead.
1080 1101
1081 1102 This predicate uses ``glob:`` as the default kind of pattern.
1082 1103 """
1083 1104 # i18n: "file" is a keyword
1084 1105 pat = getstring(x, _("file requires a pattern"))
1085 1106 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1086 1107
1087 1108 @predicate('head()', safe=True)
1088 1109 def head(repo, subset, x):
1089 1110 """Changeset is a named branch head.
1090 1111 """
1091 1112 # i18n: "head" is a keyword
1092 1113 getargs(x, 0, 0, _("head takes no arguments"))
1093 1114 hs = set()
1094 1115 cl = repo.changelog
1095 1116 for ls in repo.branchmap().itervalues():
1096 1117 hs.update(cl.rev(h) for h in ls)
1097 1118 return subset & baseset(hs)
1098 1119
1099 1120 @predicate('heads(set)', safe=True)
1100 1121 def heads(repo, subset, x):
1101 1122 """Members of set with no children in set.
1102 1123 """
1103 1124 s = getset(repo, subset, x)
1104 1125 ps = parents(repo, subset, x)
1105 1126 return s - ps
1106 1127
1107 1128 @predicate('hidden()', safe=True)
1108 1129 def hidden(repo, subset, x):
1109 1130 """Hidden changesets.
1110 1131 """
1111 1132 # i18n: "hidden" is a keyword
1112 1133 getargs(x, 0, 0, _("hidden takes no arguments"))
1113 1134 hiddenrevs = repoview.filterrevs(repo, 'visible')
1114 1135 return subset & hiddenrevs
1115 1136
1116 1137 @predicate('keyword(string)', safe=True)
1117 1138 def keyword(repo, subset, x):
1118 1139 """Search commit message, user name, and names of changed files for
1119 1140 string. The match is case-insensitive.
1120 1141
1121 1142 For a regular expression or case sensitive search of these fields, use
1122 1143 ``grep(regex)``.
1123 1144 """
1124 1145 # i18n: "keyword" is a keyword
1125 1146 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1126 1147
1127 1148 def matches(r):
1128 1149 c = repo[r]
1129 1150 return any(kw in encoding.lower(t)
1130 1151 for t in c.files() + [c.user(), c.description()])
1131 1152
1132 1153 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1133 1154
1134 1155 @predicate('limit(set[, n[, offset]])', safe=True)
1135 1156 def limit(repo, subset, x):
1136 1157 """First n members of set, defaulting to 1, starting from offset.
1137 1158 """
1138 1159 args = getargsdict(x, 'limit', 'set n offset')
1139 1160 if 'set' not in args:
1140 1161 # i18n: "limit" is a keyword
1141 1162 raise error.ParseError(_("limit requires one to three arguments"))
1142 1163 # i18n: "limit" is a keyword
1143 1164 lim = getinteger(args.get('n'), _("limit expects a number"), default=1)
1144 1165 # i18n: "limit" is a keyword
1145 1166 ofs = getinteger(args.get('offset'), _("limit expects a number"), default=0)
1146 1167 if ofs < 0:
1147 1168 raise error.ParseError(_("negative offset"))
1148 1169 os = getset(repo, fullreposet(repo), args['set'])
1149 1170 result = []
1150 1171 it = iter(os)
1151 1172 for x in xrange(ofs):
1152 1173 y = next(it, None)
1153 1174 if y is None:
1154 1175 break
1155 1176 for x in xrange(lim):
1156 1177 y = next(it, None)
1157 1178 if y is None:
1158 1179 break
1159 1180 elif y in subset:
1160 1181 result.append(y)
1161 1182 return baseset(result, datarepr=('<limit n=%d, offset=%d, %r, %r>',
1162 1183 lim, ofs, subset, os))
1163 1184
1164 1185 @predicate('last(set, [n])', safe=True)
1165 1186 def last(repo, subset, x):
1166 1187 """Last n members of set, defaulting to 1.
1167 1188 """
1168 1189 # i18n: "last" is a keyword
1169 1190 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1170 1191 lim = 1
1171 1192 if len(l) == 2:
1172 1193 # i18n: "last" is a keyword
1173 1194 lim = getinteger(l[1], _("last expects a number"))
1174 1195 os = getset(repo, fullreposet(repo), l[0])
1175 1196 os.reverse()
1176 1197 result = []
1177 1198 it = iter(os)
1178 1199 for x in xrange(lim):
1179 1200 y = next(it, None)
1180 1201 if y is None:
1181 1202 break
1182 1203 elif y in subset:
1183 1204 result.append(y)
1184 1205 return baseset(result, datarepr=('<last n=%d, %r, %r>', lim, subset, os))
1185 1206
1186 1207 @predicate('max(set)', safe=True)
1187 1208 def maxrev(repo, subset, x):
1188 1209 """Changeset with highest revision number in set.
1189 1210 """
1190 1211 os = getset(repo, fullreposet(repo), x)
1191 1212 try:
1192 1213 m = os.max()
1193 1214 if m in subset:
1194 1215 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1195 1216 except ValueError:
1196 1217 # os.max() throws a ValueError when the collection is empty.
1197 1218 # Same as python's max().
1198 1219 pass
1199 1220 return baseset(datarepr=('<max %r, %r>', subset, os))
1200 1221
1201 1222 @predicate('merge()', safe=True)
1202 1223 def merge(repo, subset, x):
1203 1224 """Changeset is a merge changeset.
1204 1225 """
1205 1226 # i18n: "merge" is a keyword
1206 1227 getargs(x, 0, 0, _("merge takes no arguments"))
1207 1228 cl = repo.changelog
1208 1229 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1209 1230 condrepr='<merge>')
1210 1231
1211 1232 @predicate('branchpoint()', safe=True)
1212 1233 def branchpoint(repo, subset, x):
1213 1234 """Changesets with more than one child.
1214 1235 """
1215 1236 # i18n: "branchpoint" is a keyword
1216 1237 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1217 1238 cl = repo.changelog
1218 1239 if not subset:
1219 1240 return baseset()
1220 1241 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1221 1242 # (and if it is not, it should.)
1222 1243 baserev = min(subset)
1223 1244 parentscount = [0]*(len(repo) - baserev)
1224 1245 for r in cl.revs(start=baserev + 1):
1225 1246 for p in cl.parentrevs(r):
1226 1247 if p >= baserev:
1227 1248 parentscount[p - baserev] += 1
1228 1249 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1229 1250 condrepr='<branchpoint>')
1230 1251
1231 1252 @predicate('min(set)', safe=True)
1232 1253 def minrev(repo, subset, x):
1233 1254 """Changeset with lowest revision number in set.
1234 1255 """
1235 1256 os = getset(repo, fullreposet(repo), x)
1236 1257 try:
1237 1258 m = os.min()
1238 1259 if m in subset:
1239 1260 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1240 1261 except ValueError:
1241 1262 # os.min() throws a ValueError when the collection is empty.
1242 1263 # Same as python's min().
1243 1264 pass
1244 1265 return baseset(datarepr=('<min %r, %r>', subset, os))
1245 1266
1246 1267 @predicate('modifies(pattern)', safe=True)
1247 1268 def modifies(repo, subset, x):
1248 1269 """Changesets modifying files matched by pattern.
1249 1270
1250 1271 The pattern without explicit kind like ``glob:`` is expected to be
1251 1272 relative to the current directory and match against a file or a
1252 1273 directory.
1253 1274 """
1254 1275 # i18n: "modifies" is a keyword
1255 1276 pat = getstring(x, _("modifies requires a pattern"))
1256 1277 return checkstatus(repo, subset, pat, 0)
1257 1278
1258 1279 @predicate('named(namespace)')
1259 1280 def named(repo, subset, x):
1260 1281 """The changesets in a given namespace.
1261 1282
1262 1283 Pattern matching is supported for `namespace`. See
1263 1284 :hg:`help revisions.patterns`.
1264 1285 """
1265 1286 # i18n: "named" is a keyword
1266 1287 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1267 1288
1268 1289 ns = getstring(args[0],
1269 1290 # i18n: "named" is a keyword
1270 1291 _('the argument to named must be a string'))
1271 1292 kind, pattern, matcher = util.stringmatcher(ns)
1272 1293 namespaces = set()
1273 1294 if kind == 'literal':
1274 1295 if pattern not in repo.names:
1275 1296 raise error.RepoLookupError(_("namespace '%s' does not exist")
1276 1297 % ns)
1277 1298 namespaces.add(repo.names[pattern])
1278 1299 else:
1279 1300 for name, ns in repo.names.iteritems():
1280 1301 if matcher(name):
1281 1302 namespaces.add(ns)
1282 1303 if not namespaces:
1283 1304 raise error.RepoLookupError(_("no namespace exists"
1284 1305 " that match '%s'") % pattern)
1285 1306
1286 1307 names = set()
1287 1308 for ns in namespaces:
1288 1309 for name in ns.listnames(repo):
1289 1310 if name not in ns.deprecated:
1290 1311 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1291 1312
1292 1313 names -= {node.nullrev}
1293 1314 return subset & names
1294 1315
1295 1316 @predicate('id(string)', safe=True)
1296 1317 def node_(repo, subset, x):
1297 1318 """Revision non-ambiguously specified by the given hex string prefix.
1298 1319 """
1299 1320 # i18n: "id" is a keyword
1300 1321 l = getargs(x, 1, 1, _("id requires one argument"))
1301 1322 # i18n: "id" is a keyword
1302 1323 n = getstring(l[0], _("id requires a string"))
1303 1324 if len(n) == 40:
1304 1325 try:
1305 1326 rn = repo.changelog.rev(node.bin(n))
1306 1327 except error.WdirUnsupported:
1307 1328 rn = node.wdirrev
1308 1329 except (LookupError, TypeError):
1309 1330 rn = None
1310 1331 else:
1311 1332 rn = None
1312 1333 try:
1313 1334 pm = repo.changelog._partialmatch(n)
1314 1335 if pm is not None:
1315 1336 rn = repo.changelog.rev(pm)
1316 1337 except error.WdirUnsupported:
1317 1338 rn = node.wdirrev
1318 1339
1319 1340 if rn is None:
1320 1341 return baseset()
1321 1342 result = baseset([rn])
1322 1343 return result & subset
1323 1344
1324 1345 @predicate('obsolete()', safe=True)
1325 1346 def obsolete(repo, subset, x):
1326 1347 """Mutable changeset with a newer version."""
1327 1348 # i18n: "obsolete" is a keyword
1328 1349 getargs(x, 0, 0, _("obsolete takes no arguments"))
1329 1350 obsoletes = obsmod.getrevs(repo, 'obsolete')
1330 1351 return subset & obsoletes
1331 1352
1332 1353 @predicate('only(set, [set])', safe=True)
1333 1354 def only(repo, subset, x):
1334 1355 """Changesets that are ancestors of the first set that are not ancestors
1335 1356 of any other head in the repo. If a second set is specified, the result
1336 1357 is ancestors of the first set that are not ancestors of the second set
1337 1358 (i.e. ::<set1> - ::<set2>).
1338 1359 """
1339 1360 cl = repo.changelog
1340 1361 # i18n: "only" is a keyword
1341 1362 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1342 1363 include = getset(repo, fullreposet(repo), args[0])
1343 1364 if len(args) == 1:
1344 1365 if not include:
1345 1366 return baseset()
1346 1367
1347 1368 descendants = set(_revdescendants(repo, include, False))
1348 1369 exclude = [rev for rev in cl.headrevs()
1349 1370 if not rev in descendants and not rev in include]
1350 1371 else:
1351 1372 exclude = getset(repo, fullreposet(repo), args[1])
1352 1373
1353 1374 results = set(cl.findmissingrevs(common=exclude, heads=include))
1354 1375 # XXX we should turn this into a baseset instead of a set, smartset may do
1355 1376 # some optimizations from the fact this is a baseset.
1356 1377 return subset & results
1357 1378
1358 1379 @predicate('origin([set])', safe=True)
1359 1380 def origin(repo, subset, x):
1360 1381 """
1361 1382 Changesets that were specified as a source for the grafts, transplants or
1362 1383 rebases that created the given revisions. Omitting the optional set is the
1363 1384 same as passing all(). If a changeset created by these operations is itself
1364 1385 specified as a source for one of these operations, only the source changeset
1365 1386 for the first operation is selected.
1366 1387 """
1367 1388 if x is not None:
1368 1389 dests = getset(repo, fullreposet(repo), x)
1369 1390 else:
1370 1391 dests = fullreposet(repo)
1371 1392
1372 1393 def _firstsrc(rev):
1373 1394 src = _getrevsource(repo, rev)
1374 1395 if src is None:
1375 1396 return None
1376 1397
1377 1398 while True:
1378 1399 prev = _getrevsource(repo, src)
1379 1400
1380 1401 if prev is None:
1381 1402 return src
1382 1403 src = prev
1383 1404
1384 1405 o = {_firstsrc(r) for r in dests}
1385 1406 o -= {None}
1386 1407 # XXX we should turn this into a baseset instead of a set, smartset may do
1387 1408 # some optimizations from the fact this is a baseset.
1388 1409 return subset & o
1389 1410
1390 1411 @predicate('outgoing([path])', safe=False)
1391 1412 def outgoing(repo, subset, x):
1392 1413 """Changesets not found in the specified destination repository, or the
1393 1414 default push location.
1394 1415 """
1395 1416 # Avoid cycles.
1396 1417 from . import (
1397 1418 discovery,
1398 1419 hg,
1399 1420 )
1400 1421 # i18n: "outgoing" is a keyword
1401 1422 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1402 1423 # i18n: "outgoing" is a keyword
1403 1424 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1404 1425 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1405 1426 dest, branches = hg.parseurl(dest)
1406 1427 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1407 1428 if revs:
1408 1429 revs = [repo.lookup(rev) for rev in revs]
1409 1430 other = hg.peer(repo, {}, dest)
1410 1431 repo.ui.pushbuffer()
1411 1432 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1412 1433 repo.ui.popbuffer()
1413 1434 cl = repo.changelog
1414 1435 o = {cl.rev(r) for r in outgoing.missing}
1415 1436 return subset & o
1416 1437
1417 1438 @predicate('p1([set])', safe=True)
1418 1439 def p1(repo, subset, x):
1419 1440 """First parent of changesets in set, or the working directory.
1420 1441 """
1421 1442 if x is None:
1422 1443 p = repo[x].p1().rev()
1423 1444 if p >= 0:
1424 1445 return subset & baseset([p])
1425 1446 return baseset()
1426 1447
1427 1448 ps = set()
1428 1449 cl = repo.changelog
1429 1450 for r in getset(repo, fullreposet(repo), x):
1430 1451 try:
1431 1452 ps.add(cl.parentrevs(r)[0])
1432 1453 except error.WdirUnsupported:
1433 1454 ps.add(repo[r].parents()[0].rev())
1434 1455 ps -= {node.nullrev}
1435 1456 # XXX we should turn this into a baseset instead of a set, smartset may do
1436 1457 # some optimizations from the fact this is a baseset.
1437 1458 return subset & ps
1438 1459
1439 1460 @predicate('p2([set])', safe=True)
1440 1461 def p2(repo, subset, x):
1441 1462 """Second parent of changesets in set, or the working directory.
1442 1463 """
1443 1464 if x is None:
1444 1465 ps = repo[x].parents()
1445 1466 try:
1446 1467 p = ps[1].rev()
1447 1468 if p >= 0:
1448 1469 return subset & baseset([p])
1449 1470 return baseset()
1450 1471 except IndexError:
1451 1472 return baseset()
1452 1473
1453 1474 ps = set()
1454 1475 cl = repo.changelog
1455 1476 for r in getset(repo, fullreposet(repo), x):
1456 1477 try:
1457 1478 ps.add(cl.parentrevs(r)[1])
1458 1479 except error.WdirUnsupported:
1459 1480 parents = repo[r].parents()
1460 1481 if len(parents) == 2:
1461 1482 ps.add(parents[1])
1462 1483 ps -= {node.nullrev}
1463 1484 # XXX we should turn this into a baseset instead of a set, smartset may do
1464 1485 # some optimizations from the fact this is a baseset.
1465 1486 return subset & ps
1466 1487
1467 1488 def parentpost(repo, subset, x, order):
1468 1489 return p1(repo, subset, x)
1469 1490
1470 1491 @predicate('parents([set])', safe=True)
1471 1492 def parents(repo, subset, x):
1472 1493 """
1473 1494 The set of all parents for all changesets in set, or the working directory.
1474 1495 """
1475 1496 if x is None:
1476 1497 ps = set(p.rev() for p in repo[x].parents())
1477 1498 else:
1478 1499 ps = set()
1479 1500 cl = repo.changelog
1480 1501 up = ps.update
1481 1502 parentrevs = cl.parentrevs
1482 1503 for r in getset(repo, fullreposet(repo), x):
1483 1504 try:
1484 1505 up(parentrevs(r))
1485 1506 except error.WdirUnsupported:
1486 1507 up(p.rev() for p in repo[r].parents())
1487 1508 ps -= {node.nullrev}
1488 1509 return subset & ps
1489 1510
1490 1511 def _phase(repo, subset, *targets):
1491 1512 """helper to select all rev in <targets> phases"""
1492 1513 s = repo._phasecache.getrevset(repo, targets)
1493 1514 return subset & s
1494 1515
1495 1516 @predicate('draft()', safe=True)
1496 1517 def draft(repo, subset, x):
1497 1518 """Changeset in draft phase."""
1498 1519 # i18n: "draft" is a keyword
1499 1520 getargs(x, 0, 0, _("draft takes no arguments"))
1500 1521 target = phases.draft
1501 1522 return _phase(repo, subset, target)
1502 1523
1503 1524 @predicate('secret()', safe=True)
1504 1525 def secret(repo, subset, x):
1505 1526 """Changeset in secret phase."""
1506 1527 # i18n: "secret" is a keyword
1507 1528 getargs(x, 0, 0, _("secret takes no arguments"))
1508 1529 target = phases.secret
1509 1530 return _phase(repo, subset, target)
1510 1531
1511 1532 def parentspec(repo, subset, x, n, order):
1512 1533 """``set^0``
1513 1534 The set.
1514 1535 ``set^1`` (or ``set^``), ``set^2``
1515 1536 First or second parent, respectively, of all changesets in set.
1516 1537 """
1517 1538 try:
1518 1539 n = int(n[1])
1519 1540 if n not in (0, 1, 2):
1520 1541 raise ValueError
1521 1542 except (TypeError, ValueError):
1522 1543 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1523 1544 ps = set()
1524 1545 cl = repo.changelog
1525 1546 for r in getset(repo, fullreposet(repo), x):
1526 1547 if n == 0:
1527 1548 ps.add(r)
1528 1549 elif n == 1:
1529 1550 try:
1530 1551 ps.add(cl.parentrevs(r)[0])
1531 1552 except error.WdirUnsupported:
1532 1553 ps.add(repo[r].parents()[0].rev())
1533 1554 else:
1534 1555 try:
1535 1556 parents = cl.parentrevs(r)
1536 1557 if parents[1] != node.nullrev:
1537 1558 ps.add(parents[1])
1538 1559 except error.WdirUnsupported:
1539 1560 parents = repo[r].parents()
1540 1561 if len(parents) == 2:
1541 1562 ps.add(parents[1].rev())
1542 1563 return subset & ps
1543 1564
1544 1565 @predicate('present(set)', safe=True)
1545 1566 def present(repo, subset, x):
1546 1567 """An empty set, if any revision in set isn't found; otherwise,
1547 1568 all revisions in set.
1548 1569
1549 1570 If any of specified revisions is not present in the local repository,
1550 1571 the query is normally aborted. But this predicate allows the query
1551 1572 to continue even in such cases.
1552 1573 """
1553 1574 try:
1554 1575 return getset(repo, subset, x)
1555 1576 except error.RepoLookupError:
1556 1577 return baseset()
1557 1578
1558 1579 # for internal use
1559 1580 @predicate('_notpublic', safe=True)
1560 1581 def _notpublic(repo, subset, x):
1561 1582 getargs(x, 0, 0, "_notpublic takes no arguments")
1562 1583 return _phase(repo, subset, phases.draft, phases.secret)
1563 1584
1564 1585 @predicate('public()', safe=True)
1565 1586 def public(repo, subset, x):
1566 1587 """Changeset in public phase."""
1567 1588 # i18n: "public" is a keyword
1568 1589 getargs(x, 0, 0, _("public takes no arguments"))
1569 1590 phase = repo._phasecache.phase
1570 1591 target = phases.public
1571 1592 condition = lambda r: phase(repo, r) == target
1572 1593 return subset.filter(condition, condrepr=('<phase %r>', target),
1573 1594 cache=False)
1574 1595
1575 1596 @predicate('remote([id [,path]])', safe=False)
1576 1597 def remote(repo, subset, x):
1577 1598 """Local revision that corresponds to the given identifier in a
1578 1599 remote repository, if present. Here, the '.' identifier is a
1579 1600 synonym for the current local branch.
1580 1601 """
1581 1602
1582 1603 from . import hg # avoid start-up nasties
1583 1604 # i18n: "remote" is a keyword
1584 1605 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1585 1606
1586 1607 q = '.'
1587 1608 if len(l) > 0:
1588 1609 # i18n: "remote" is a keyword
1589 1610 q = getstring(l[0], _("remote requires a string id"))
1590 1611 if q == '.':
1591 1612 q = repo['.'].branch()
1592 1613
1593 1614 dest = ''
1594 1615 if len(l) > 1:
1595 1616 # i18n: "remote" is a keyword
1596 1617 dest = getstring(l[1], _("remote requires a repository path"))
1597 1618 dest = repo.ui.expandpath(dest or 'default')
1598 1619 dest, branches = hg.parseurl(dest)
1599 1620 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1600 1621 if revs:
1601 1622 revs = [repo.lookup(rev) for rev in revs]
1602 1623 other = hg.peer(repo, {}, dest)
1603 1624 n = other.lookup(q)
1604 1625 if n in repo:
1605 1626 r = repo[n].rev()
1606 1627 if r in subset:
1607 1628 return baseset([r])
1608 1629 return baseset()
1609 1630
1610 1631 @predicate('removes(pattern)', safe=True)
1611 1632 def removes(repo, subset, x):
1612 1633 """Changesets which remove files matching pattern.
1613 1634
1614 1635 The pattern without explicit kind like ``glob:`` is expected to be
1615 1636 relative to the current directory and match against a file or a
1616 1637 directory.
1617 1638 """
1618 1639 # i18n: "removes" is a keyword
1619 1640 pat = getstring(x, _("removes requires a pattern"))
1620 1641 return checkstatus(repo, subset, pat, 2)
1621 1642
1622 1643 @predicate('rev(number)', safe=True)
1623 1644 def rev(repo, subset, x):
1624 1645 """Revision with the given numeric identifier.
1625 1646 """
1626 1647 # i18n: "rev" is a keyword
1627 1648 l = getargs(x, 1, 1, _("rev requires one argument"))
1628 1649 try:
1629 1650 # i18n: "rev" is a keyword
1630 1651 l = int(getstring(l[0], _("rev requires a number")))
1631 1652 except (TypeError, ValueError):
1632 1653 # i18n: "rev" is a keyword
1633 1654 raise error.ParseError(_("rev expects a number"))
1634 1655 if l not in repo.changelog and l not in (node.nullrev, node.wdirrev):
1635 1656 return baseset()
1636 1657 return subset & baseset([l])
1637 1658
1638 1659 @predicate('matching(revision [, field])', safe=True)
1639 1660 def matching(repo, subset, x):
1640 1661 """Changesets in which a given set of fields match the set of fields in the
1641 1662 selected revision or set.
1642 1663
1643 1664 To match more than one field pass the list of fields to match separated
1644 1665 by spaces (e.g. ``author description``).
1645 1666
1646 1667 Valid fields are most regular revision fields and some special fields.
1647 1668
1648 1669 Regular revision fields are ``description``, ``author``, ``branch``,
1649 1670 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1650 1671 and ``diff``.
1651 1672 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1652 1673 contents of the revision. Two revisions matching their ``diff`` will
1653 1674 also match their ``files``.
1654 1675
1655 1676 Special fields are ``summary`` and ``metadata``:
1656 1677 ``summary`` matches the first line of the description.
1657 1678 ``metadata`` is equivalent to matching ``description user date``
1658 1679 (i.e. it matches the main metadata fields).
1659 1680
1660 1681 ``metadata`` is the default field which is used when no fields are
1661 1682 specified. You can match more than one field at a time.
1662 1683 """
1663 1684 # i18n: "matching" is a keyword
1664 1685 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1665 1686
1666 1687 revs = getset(repo, fullreposet(repo), l[0])
1667 1688
1668 1689 fieldlist = ['metadata']
1669 1690 if len(l) > 1:
1670 1691 fieldlist = getstring(l[1],
1671 1692 # i18n: "matching" is a keyword
1672 1693 _("matching requires a string "
1673 1694 "as its second argument")).split()
1674 1695
1675 1696 # Make sure that there are no repeated fields,
1676 1697 # expand the 'special' 'metadata' field type
1677 1698 # and check the 'files' whenever we check the 'diff'
1678 1699 fields = []
1679 1700 for field in fieldlist:
1680 1701 if field == 'metadata':
1681 1702 fields += ['user', 'description', 'date']
1682 1703 elif field == 'diff':
1683 1704 # a revision matching the diff must also match the files
1684 1705 # since matching the diff is very costly, make sure to
1685 1706 # also match the files first
1686 1707 fields += ['files', 'diff']
1687 1708 else:
1688 1709 if field == 'author':
1689 1710 field = 'user'
1690 1711 fields.append(field)
1691 1712 fields = set(fields)
1692 1713 if 'summary' in fields and 'description' in fields:
1693 1714 # If a revision matches its description it also matches its summary
1694 1715 fields.discard('summary')
1695 1716
1696 1717 # We may want to match more than one field
1697 1718 # Not all fields take the same amount of time to be matched
1698 1719 # Sort the selected fields in order of increasing matching cost
1699 1720 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1700 1721 'files', 'description', 'substate', 'diff']
1701 1722 def fieldkeyfunc(f):
1702 1723 try:
1703 1724 return fieldorder.index(f)
1704 1725 except ValueError:
1705 1726 # assume an unknown field is very costly
1706 1727 return len(fieldorder)
1707 1728 fields = list(fields)
1708 1729 fields.sort(key=fieldkeyfunc)
1709 1730
1710 1731 # Each field will be matched with its own "getfield" function
1711 1732 # which will be added to the getfieldfuncs array of functions
1712 1733 getfieldfuncs = []
1713 1734 _funcs = {
1714 1735 'user': lambda r: repo[r].user(),
1715 1736 'branch': lambda r: repo[r].branch(),
1716 1737 'date': lambda r: repo[r].date(),
1717 1738 'description': lambda r: repo[r].description(),
1718 1739 'files': lambda r: repo[r].files(),
1719 1740 'parents': lambda r: repo[r].parents(),
1720 1741 'phase': lambda r: repo[r].phase(),
1721 1742 'substate': lambda r: repo[r].substate,
1722 1743 'summary': lambda r: repo[r].description().splitlines()[0],
1723 1744 'diff': lambda r: list(repo[r].diff(git=True),)
1724 1745 }
1725 1746 for info in fields:
1726 1747 getfield = _funcs.get(info, None)
1727 1748 if getfield is None:
1728 1749 raise error.ParseError(
1729 1750 # i18n: "matching" is a keyword
1730 1751 _("unexpected field name passed to matching: %s") % info)
1731 1752 getfieldfuncs.append(getfield)
1732 1753 # convert the getfield array of functions into a "getinfo" function
1733 1754 # which returns an array of field values (or a single value if there
1734 1755 # is only one field to match)
1735 1756 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1736 1757
1737 1758 def matches(x):
1738 1759 for rev in revs:
1739 1760 target = getinfo(rev)
1740 1761 match = True
1741 1762 for n, f in enumerate(getfieldfuncs):
1742 1763 if target[n] != f(x):
1743 1764 match = False
1744 1765 if match:
1745 1766 return True
1746 1767 return False
1747 1768
1748 1769 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1749 1770
1750 1771 @predicate('reverse(set)', safe=True, takeorder=True)
1751 1772 def reverse(repo, subset, x, order):
1752 1773 """Reverse order of set.
1753 1774 """
1754 1775 l = getset(repo, subset, x)
1755 1776 if order == defineorder:
1756 1777 l.reverse()
1757 1778 return l
1758 1779
1759 1780 @predicate('roots(set)', safe=True)
1760 1781 def roots(repo, subset, x):
1761 1782 """Changesets in set with no parent changeset in set.
1762 1783 """
1763 1784 s = getset(repo, fullreposet(repo), x)
1764 1785 parents = repo.changelog.parentrevs
1765 1786 def filter(r):
1766 1787 for p in parents(r):
1767 1788 if 0 <= p and p in s:
1768 1789 return False
1769 1790 return True
1770 1791 return subset & s.filter(filter, condrepr='<roots>')
1771 1792
1772 1793 _sortkeyfuncs = {
1773 1794 'rev': lambda c: c.rev(),
1774 1795 'branch': lambda c: c.branch(),
1775 1796 'desc': lambda c: c.description(),
1776 1797 'user': lambda c: c.user(),
1777 1798 'author': lambda c: c.user(),
1778 1799 'date': lambda c: c.date()[0],
1779 1800 }
1780 1801
1781 1802 def _getsortargs(x):
1782 1803 """Parse sort options into (set, [(key, reverse)], opts)"""
1783 1804 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1784 1805 if 'set' not in args:
1785 1806 # i18n: "sort" is a keyword
1786 1807 raise error.ParseError(_('sort requires one or two arguments'))
1787 1808 keys = "rev"
1788 1809 if 'keys' in args:
1789 1810 # i18n: "sort" is a keyword
1790 1811 keys = getstring(args['keys'], _("sort spec must be a string"))
1791 1812
1792 1813 keyflags = []
1793 1814 for k in keys.split():
1794 1815 fk = k
1795 1816 reverse = (k[0] == '-')
1796 1817 if reverse:
1797 1818 k = k[1:]
1798 1819 if k not in _sortkeyfuncs and k != 'topo':
1799 1820 raise error.ParseError(_("unknown sort key %r") % fk)
1800 1821 keyflags.append((k, reverse))
1801 1822
1802 1823 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1803 1824 # i18n: "topo" is a keyword
1804 1825 raise error.ParseError(_('topo sort order cannot be combined '
1805 1826 'with other sort keys'))
1806 1827
1807 1828 opts = {}
1808 1829 if 'topo.firstbranch' in args:
1809 1830 if any(k == 'topo' for k, reverse in keyflags):
1810 1831 opts['topo.firstbranch'] = args['topo.firstbranch']
1811 1832 else:
1812 1833 # i18n: "topo" and "topo.firstbranch" are keywords
1813 1834 raise error.ParseError(_('topo.firstbranch can only be used '
1814 1835 'when using the topo sort key'))
1815 1836
1816 1837 return args['set'], keyflags, opts
1817 1838
1818 1839 @predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True)
1819 1840 def sort(repo, subset, x, order):
1820 1841 """Sort set by keys. The default sort order is ascending, specify a key
1821 1842 as ``-key`` to sort in descending order.
1822 1843
1823 1844 The keys can be:
1824 1845
1825 1846 - ``rev`` for the revision number,
1826 1847 - ``branch`` for the branch name,
1827 1848 - ``desc`` for the commit message (description),
1828 1849 - ``user`` for user name (``author`` can be used as an alias),
1829 1850 - ``date`` for the commit date
1830 1851 - ``topo`` for a reverse topographical sort
1831 1852
1832 1853 The ``topo`` sort order cannot be combined with other sort keys. This sort
1833 1854 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1834 1855 specifies what topographical branches to prioritize in the sort.
1835 1856
1836 1857 """
1837 1858 s, keyflags, opts = _getsortargs(x)
1838 1859 revs = getset(repo, subset, s)
1839 1860
1840 1861 if not keyflags or order != defineorder:
1841 1862 return revs
1842 1863 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1843 1864 revs.sort(reverse=keyflags[0][1])
1844 1865 return revs
1845 1866 elif keyflags[0][0] == "topo":
1846 1867 firstbranch = ()
1847 1868 if 'topo.firstbranch' in opts:
1848 1869 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1849 1870 revs = baseset(_toposort(revs, repo.changelog.parentrevs, firstbranch),
1850 1871 istopo=True)
1851 1872 if keyflags[0][1]:
1852 1873 revs.reverse()
1853 1874 return revs
1854 1875
1855 1876 # sort() is guaranteed to be stable
1856 1877 ctxs = [repo[r] for r in revs]
1857 1878 for k, reverse in reversed(keyflags):
1858 1879 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1859 1880 return baseset([c.rev() for c in ctxs])
1860 1881
1861 1882 def _toposort(revs, parentsfunc, firstbranch=()):
1862 1883 """Yield revisions from heads to roots one (topo) branch at a time.
1863 1884
1864 1885 This function aims to be used by a graph generator that wishes to minimize
1865 1886 the number of parallel branches and their interleaving.
1866 1887
1867 1888 Example iteration order (numbers show the "true" order in a changelog):
1868 1889
1869 1890 o 4
1870 1891 |
1871 1892 o 1
1872 1893 |
1873 1894 | o 3
1874 1895 | |
1875 1896 | o 2
1876 1897 |/
1877 1898 o 0
1878 1899
1879 1900 Note that the ancestors of merges are understood by the current
1880 1901 algorithm to be on the same branch. This means no reordering will
1881 1902 occur behind a merge.
1882 1903 """
1883 1904
1884 1905 ### Quick summary of the algorithm
1885 1906 #
1886 1907 # This function is based around a "retention" principle. We keep revisions
1887 1908 # in memory until we are ready to emit a whole branch that immediately
1888 1909 # "merges" into an existing one. This reduces the number of parallel
1889 1910 # branches with interleaved revisions.
1890 1911 #
1891 1912 # During iteration revs are split into two groups:
1892 1913 # A) revision already emitted
1893 1914 # B) revision in "retention". They are stored as different subgroups.
1894 1915 #
1895 1916 # for each REV, we do the following logic:
1896 1917 #
1897 1918 # 1) if REV is a parent of (A), we will emit it. If there is a
1898 1919 # retention group ((B) above) that is blocked on REV being
1899 1920 # available, we emit all the revisions out of that retention
1900 1921 # group first.
1901 1922 #
1902 1923 # 2) else, we'll search for a subgroup in (B) awaiting for REV to be
1903 1924 # available, if such subgroup exist, we add REV to it and the subgroup is
1904 1925 # now awaiting for REV.parents() to be available.
1905 1926 #
1906 1927 # 3) finally if no such group existed in (B), we create a new subgroup.
1907 1928 #
1908 1929 #
1909 1930 # To bootstrap the algorithm, we emit the tipmost revision (which
1910 1931 # puts it in group (A) from above).
1911 1932
1912 1933 revs.sort(reverse=True)
1913 1934
1914 1935 # Set of parents of revision that have been emitted. They can be considered
1915 1936 # unblocked as the graph generator is already aware of them so there is no
1916 1937 # need to delay the revisions that reference them.
1917 1938 #
1918 1939 # If someone wants to prioritize a branch over the others, pre-filling this
1919 1940 # set will force all other branches to wait until this branch is ready to be
1920 1941 # emitted.
1921 1942 unblocked = set(firstbranch)
1922 1943
1923 1944 # list of groups waiting to be displayed, each group is defined by:
1924 1945 #
1925 1946 # (revs: lists of revs waiting to be displayed,
1926 1947 # blocked: set of that cannot be displayed before those in 'revs')
1927 1948 #
1928 1949 # The second value ('blocked') correspond to parents of any revision in the
1929 1950 # group ('revs') that is not itself contained in the group. The main idea
1930 1951 # of this algorithm is to delay as much as possible the emission of any
1931 1952 # revision. This means waiting for the moment we are about to display
1932 1953 # these parents to display the revs in a group.
1933 1954 #
1934 1955 # This first implementation is smart until it encounters a merge: it will
1935 1956 # emit revs as soon as any parent is about to be emitted and can grow an
1936 1957 # arbitrary number of revs in 'blocked'. In practice this mean we properly
1937 1958 # retains new branches but gives up on any special ordering for ancestors
1938 1959 # of merges. The implementation can be improved to handle this better.
1939 1960 #
1940 1961 # The first subgroup is special. It corresponds to all the revision that
1941 1962 # were already emitted. The 'revs' lists is expected to be empty and the
1942 1963 # 'blocked' set contains the parents revisions of already emitted revision.
1943 1964 #
1944 1965 # You could pre-seed the <parents> set of groups[0] to a specific
1945 1966 # changesets to select what the first emitted branch should be.
1946 1967 groups = [([], unblocked)]
1947 1968 pendingheap = []
1948 1969 pendingset = set()
1949 1970
1950 1971 heapq.heapify(pendingheap)
1951 1972 heappop = heapq.heappop
1952 1973 heappush = heapq.heappush
1953 1974 for currentrev in revs:
1954 1975 # Heap works with smallest element, we want highest so we invert
1955 1976 if currentrev not in pendingset:
1956 1977 heappush(pendingheap, -currentrev)
1957 1978 pendingset.add(currentrev)
1958 1979 # iterates on pending rev until after the current rev have been
1959 1980 # processed.
1960 1981 rev = None
1961 1982 while rev != currentrev:
1962 1983 rev = -heappop(pendingheap)
1963 1984 pendingset.remove(rev)
1964 1985
1965 1986 # Seek for a subgroup blocked, waiting for the current revision.
1966 1987 matching = [i for i, g in enumerate(groups) if rev in g[1]]
1967 1988
1968 1989 if matching:
1969 1990 # The main idea is to gather together all sets that are blocked
1970 1991 # on the same revision.
1971 1992 #
1972 1993 # Groups are merged when a common blocking ancestor is
1973 1994 # observed. For example, given two groups:
1974 1995 #
1975 1996 # revs [5, 4] waiting for 1
1976 1997 # revs [3, 2] waiting for 1
1977 1998 #
1978 1999 # These two groups will be merged when we process
1979 2000 # 1. In theory, we could have merged the groups when
1980 2001 # we added 2 to the group it is now in (we could have
1981 2002 # noticed the groups were both blocked on 1 then), but
1982 2003 # the way it works now makes the algorithm simpler.
1983 2004 #
1984 2005 # We also always keep the oldest subgroup first. We can
1985 2006 # probably improve the behavior by having the longest set
1986 2007 # first. That way, graph algorithms could minimise the length
1987 2008 # of parallel lines their drawing. This is currently not done.
1988 2009 targetidx = matching.pop(0)
1989 2010 trevs, tparents = groups[targetidx]
1990 2011 for i in matching:
1991 2012 gr = groups[i]
1992 2013 trevs.extend(gr[0])
1993 2014 tparents |= gr[1]
1994 2015 # delete all merged subgroups (except the one we kept)
1995 2016 # (starting from the last subgroup for performance and
1996 2017 # sanity reasons)
1997 2018 for i in reversed(matching):
1998 2019 del groups[i]
1999 2020 else:
2000 2021 # This is a new head. We create a new subgroup for it.
2001 2022 targetidx = len(groups)
2002 2023 groups.append(([], {rev}))
2003 2024
2004 2025 gr = groups[targetidx]
2005 2026
2006 2027 # We now add the current nodes to this subgroups. This is done
2007 2028 # after the subgroup merging because all elements from a subgroup
2008 2029 # that relied on this rev must precede it.
2009 2030 #
2010 2031 # we also update the <parents> set to include the parents of the
2011 2032 # new nodes.
2012 2033 if rev == currentrev: # only display stuff in rev
2013 2034 gr[0].append(rev)
2014 2035 gr[1].remove(rev)
2015 2036 parents = [p for p in parentsfunc(rev) if p > node.nullrev]
2016 2037 gr[1].update(parents)
2017 2038 for p in parents:
2018 2039 if p not in pendingset:
2019 2040 pendingset.add(p)
2020 2041 heappush(pendingheap, -p)
2021 2042
2022 2043 # Look for a subgroup to display
2023 2044 #
2024 2045 # When unblocked is empty (if clause), we were not waiting for any
2025 2046 # revisions during the first iteration (if no priority was given) or
2026 2047 # if we emitted a whole disconnected set of the graph (reached a
2027 2048 # root). In that case we arbitrarily take the oldest known
2028 2049 # subgroup. The heuristic could probably be better.
2029 2050 #
2030 2051 # Otherwise (elif clause) if the subgroup is blocked on
2031 2052 # a revision we just emitted, we can safely emit it as
2032 2053 # well.
2033 2054 if not unblocked:
2034 2055 if len(groups) > 1: # display other subset
2035 2056 targetidx = 1
2036 2057 gr = groups[1]
2037 2058 elif not gr[1] & unblocked:
2038 2059 gr = None
2039 2060
2040 2061 if gr is not None:
2041 2062 # update the set of awaited revisions with the one from the
2042 2063 # subgroup
2043 2064 unblocked |= gr[1]
2044 2065 # output all revisions in the subgroup
2045 2066 for r in gr[0]:
2046 2067 yield r
2047 2068 # delete the subgroup that you just output
2048 2069 # unless it is groups[0] in which case you just empty it.
2049 2070 if targetidx:
2050 2071 del groups[targetidx]
2051 2072 else:
2052 2073 gr[0][:] = []
2053 2074 # Check if we have some subgroup waiting for revisions we are not going to
2054 2075 # iterate over
2055 2076 for g in groups:
2056 2077 for r in g[0]:
2057 2078 yield r
2058 2079
2059 2080 @predicate('subrepo([pattern])')
2060 2081 def subrepo(repo, subset, x):
2061 2082 """Changesets that add, modify or remove the given subrepo. If no subrepo
2062 2083 pattern is named, any subrepo changes are returned.
2063 2084 """
2064 2085 # i18n: "subrepo" is a keyword
2065 2086 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
2066 2087 pat = None
2067 2088 if len(args) != 0:
2068 2089 pat = getstring(args[0], _("subrepo requires a pattern"))
2069 2090
2070 2091 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
2071 2092
2072 2093 def submatches(names):
2073 2094 k, p, m = util.stringmatcher(pat)
2074 2095 for name in names:
2075 2096 if m(name):
2076 2097 yield name
2077 2098
2078 2099 def matches(x):
2079 2100 c = repo[x]
2080 2101 s = repo.status(c.p1().node(), c.node(), match=m)
2081 2102
2082 2103 if pat is None:
2083 2104 return s.added or s.modified or s.removed
2084 2105
2085 2106 if s.added:
2086 2107 return any(submatches(c.substate.keys()))
2087 2108
2088 2109 if s.modified:
2089 2110 subs = set(c.p1().substate.keys())
2090 2111 subs.update(c.substate.keys())
2091 2112
2092 2113 for path in submatches(subs):
2093 2114 if c.p1().substate.get(path) != c.substate.get(path):
2094 2115 return True
2095 2116
2096 2117 if s.removed:
2097 2118 return any(submatches(c.p1().substate.keys()))
2098 2119
2099 2120 return False
2100 2121
2101 2122 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
2102 2123
2103 2124 def _substringmatcher(pattern, casesensitive=True):
2104 2125 kind, pattern, matcher = util.stringmatcher(pattern,
2105 2126 casesensitive=casesensitive)
2106 2127 if kind == 'literal':
2107 2128 if not casesensitive:
2108 2129 pattern = encoding.lower(pattern)
2109 2130 matcher = lambda s: pattern in encoding.lower(s)
2110 2131 else:
2111 2132 matcher = lambda s: pattern in s
2112 2133 return kind, pattern, matcher
2113 2134
2114 2135 @predicate('tag([name])', safe=True)
2115 2136 def tag(repo, subset, x):
2116 2137 """The specified tag by name, or all tagged revisions if no name is given.
2117 2138
2118 2139 Pattern matching is supported for `name`. See
2119 2140 :hg:`help revisions.patterns`.
2120 2141 """
2121 2142 # i18n: "tag" is a keyword
2122 2143 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2123 2144 cl = repo.changelog
2124 2145 if args:
2125 2146 pattern = getstring(args[0],
2126 2147 # i18n: "tag" is a keyword
2127 2148 _('the argument to tag must be a string'))
2128 2149 kind, pattern, matcher = util.stringmatcher(pattern)
2129 2150 if kind == 'literal':
2130 2151 # avoid resolving all tags
2131 2152 tn = repo._tagscache.tags.get(pattern, None)
2132 2153 if tn is None:
2133 2154 raise error.RepoLookupError(_("tag '%s' does not exist")
2134 2155 % pattern)
2135 2156 s = {repo[tn].rev()}
2136 2157 else:
2137 2158 s = {cl.rev(n) for t, n in repo.tagslist() if matcher(t)}
2138 2159 else:
2139 2160 s = {cl.rev(n) for t, n in repo.tagslist() if t != 'tip'}
2140 2161 return subset & s
2141 2162
2142 2163 @predicate('tagged', safe=True)
2143 2164 def tagged(repo, subset, x):
2144 2165 return tag(repo, subset, x)
2145 2166
2146 2167 @predicate('unstable()', safe=True)
2147 2168 def unstable(repo, subset, x):
2148 2169 """Non-obsolete changesets with obsolete ancestors.
2149 2170 """
2150 2171 # i18n: "unstable" is a keyword
2151 2172 getargs(x, 0, 0, _("unstable takes no arguments"))
2152 2173 unstables = obsmod.getrevs(repo, 'unstable')
2153 2174 return subset & unstables
2154 2175
2155 2176
2156 2177 @predicate('user(string)', safe=True)
2157 2178 def user(repo, subset, x):
2158 2179 """User name contains string. The match is case-insensitive.
2159 2180
2160 2181 Pattern matching is supported for `string`. See
2161 2182 :hg:`help revisions.patterns`.
2162 2183 """
2163 2184 return author(repo, subset, x)
2164 2185
2165 2186 @predicate('wdir()', safe=True)
2166 2187 def wdir(repo, subset, x):
2167 2188 """Working directory. (EXPERIMENTAL)"""
2168 2189 # i18n: "wdir" is a keyword
2169 2190 getargs(x, 0, 0, _("wdir takes no arguments"))
2170 2191 if node.wdirrev in subset or isinstance(subset, fullreposet):
2171 2192 return baseset([node.wdirrev])
2172 2193 return baseset()
2173 2194
2174 2195 def _orderedlist(repo, subset, x):
2175 2196 s = getstring(x, "internal error")
2176 2197 if not s:
2177 2198 return baseset()
2178 2199 # remove duplicates here. it's difficult for caller to deduplicate sets
2179 2200 # because different symbols can point to the same rev.
2180 2201 cl = repo.changelog
2181 2202 ls = []
2182 2203 seen = set()
2183 2204 for t in s.split('\0'):
2184 2205 try:
2185 2206 # fast path for integer revision
2186 2207 r = int(t)
2187 2208 if str(r) != t or r not in cl:
2188 2209 raise ValueError
2189 2210 revs = [r]
2190 2211 except ValueError:
2191 2212 revs = stringset(repo, subset, t)
2192 2213
2193 2214 for r in revs:
2194 2215 if r in seen:
2195 2216 continue
2196 2217 if (r in subset
2197 2218 or r == node.nullrev and isinstance(subset, fullreposet)):
2198 2219 ls.append(r)
2199 2220 seen.add(r)
2200 2221 return baseset(ls)
2201 2222
2202 2223 # for internal use
2203 2224 @predicate('_list', safe=True, takeorder=True)
2204 2225 def _list(repo, subset, x, order):
2205 2226 if order == followorder:
2206 2227 # slow path to take the subset order
2207 2228 return subset & _orderedlist(repo, fullreposet(repo), x)
2208 2229 else:
2209 2230 return _orderedlist(repo, subset, x)
2210 2231
2211 2232 def _orderedintlist(repo, subset, x):
2212 2233 s = getstring(x, "internal error")
2213 2234 if not s:
2214 2235 return baseset()
2215 2236 ls = [int(r) for r in s.split('\0')]
2216 2237 s = subset
2217 2238 return baseset([r for r in ls if r in s])
2218 2239
2219 2240 # for internal use
2220 2241 @predicate('_intlist', safe=True, takeorder=True)
2221 2242 def _intlist(repo, subset, x, order):
2222 2243 if order == followorder:
2223 2244 # slow path to take the subset order
2224 2245 return subset & _orderedintlist(repo, fullreposet(repo), x)
2225 2246 else:
2226 2247 return _orderedintlist(repo, subset, x)
2227 2248
2228 2249 def _orderedhexlist(repo, subset, x):
2229 2250 s = getstring(x, "internal error")
2230 2251 if not s:
2231 2252 return baseset()
2232 2253 cl = repo.changelog
2233 2254 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2234 2255 s = subset
2235 2256 return baseset([r for r in ls if r in s])
2236 2257
2237 2258 # for internal use
2238 2259 @predicate('_hexlist', safe=True, takeorder=True)
2239 2260 def _hexlist(repo, subset, x, order):
2240 2261 if order == followorder:
2241 2262 # slow path to take the subset order
2242 2263 return subset & _orderedhexlist(repo, fullreposet(repo), x)
2243 2264 else:
2244 2265 return _orderedhexlist(repo, subset, x)
2245 2266
2246 2267 methods = {
2247 2268 "range": rangeset,
2248 2269 "rangeall": rangeall,
2249 2270 "rangepre": rangepre,
2250 2271 "rangepost": rangepost,
2251 2272 "dagrange": dagrange,
2252 2273 "string": stringset,
2253 2274 "symbol": stringset,
2254 2275 "and": andset,
2255 2276 "or": orset,
2256 2277 "not": notset,
2257 2278 "difference": differenceset,
2258 2279 "list": listset,
2259 2280 "keyvalue": keyvaluepair,
2260 2281 "func": func,
2261 2282 "ancestor": ancestorspec,
2262 2283 "parent": parentspec,
2263 2284 "parentpost": parentpost,
2264 2285 }
2265 2286
2266 2287 def posttreebuilthook(tree, repo):
2267 2288 # hook for extensions to execute code on the optimized tree
2268 2289 pass
2269 2290
2270 2291 def match(ui, spec, repo=None, order=defineorder):
2271 2292 """Create a matcher for a single revision spec
2272 2293
2273 2294 If order=followorder, a matcher takes the ordering specified by the input
2274 2295 set.
2275 2296 """
2276 2297 return matchany(ui, [spec], repo=repo, order=order)
2277 2298
2278 2299 def matchany(ui, specs, repo=None, order=defineorder):
2279 2300 """Create a matcher that will include any revisions matching one of the
2280 2301 given specs
2281 2302
2282 2303 If order=followorder, a matcher takes the ordering specified by the input
2283 2304 set.
2284 2305 """
2285 2306 if not specs:
2286 2307 def mfunc(repo, subset=None):
2287 2308 return baseset()
2288 2309 return mfunc
2289 2310 if not all(specs):
2290 2311 raise error.ParseError(_("empty query"))
2291 2312 lookup = None
2292 2313 if repo:
2293 2314 lookup = repo.__contains__
2294 2315 if len(specs) == 1:
2295 2316 tree = revsetlang.parse(specs[0], lookup)
2296 2317 else:
2297 2318 tree = ('or',
2298 2319 ('list',) + tuple(revsetlang.parse(s, lookup) for s in specs))
2299 2320
2300 2321 if ui:
2301 2322 tree = revsetlang.expandaliases(ui, tree)
2302 2323 tree = revsetlang.foldconcat(tree)
2303 2324 tree = revsetlang.analyze(tree, order)
2304 2325 tree = revsetlang.optimize(tree)
2305 2326 posttreebuilthook(tree, repo)
2306 2327 return makematcher(tree)
2307 2328
2308 2329 def makematcher(tree):
2309 2330 """Create a matcher from an evaluatable tree"""
2310 2331 def mfunc(repo, subset=None):
2311 2332 if subset is None:
2312 2333 subset = fullreposet(repo)
2313 2334 return getset(repo, subset, tree)
2314 2335 return mfunc
2315 2336
2316 2337 def loadpredicate(ui, extname, registrarobj):
2317 2338 """Load revset predicates from specified registrarobj
2318 2339 """
2319 2340 for name, func in registrarobj._table.iteritems():
2320 2341 symbols[name] = func
2321 2342 if func._safe:
2322 2343 safesymbols.add(name)
2323 2344
2324 2345 # load built-in predicates explicitly to setup safesymbols
2325 2346 loadpredicate(None, None, predicate)
2326 2347
2327 2348 # tell hggettext to extract docstrings from these functions:
2328 2349 i18nfunctions = symbols.values()
@@ -1,3850 +1,3862 b''
1 1 $ HGENCODING=utf-8
2 2 $ export HGENCODING
3 3 $ cat > testrevset.py << EOF
4 4 > import mercurial.revset
5 5 >
6 6 > baseset = mercurial.revset.baseset
7 7 >
8 8 > def r3232(repo, subset, x):
9 9 > """"simple revset that return [3,2,3,2]
10 10 >
11 11 > revisions duplicated on purpose.
12 12 > """
13 13 > if 3 not in subset:
14 14 > if 2 in subset:
15 15 > return baseset([2,2])
16 16 > return baseset()
17 17 > return baseset([3,3,2,2])
18 18 >
19 19 > mercurial.revset.symbols['r3232'] = r3232
20 20 > EOF
21 21 $ cat >> $HGRCPATH << EOF
22 22 > [extensions]
23 23 > testrevset=$TESTTMP/testrevset.py
24 24 > EOF
25 25
26 26 $ try() {
27 27 > hg debugrevspec --debug "$@"
28 28 > }
29 29
30 30 $ log() {
31 31 > hg log --template '{rev}\n' -r "$1"
32 32 > }
33 33
34 34 extension to build '_intlist()' and '_hexlist()', which is necessary because
35 35 these predicates use '\0' as a separator:
36 36
37 37 $ cat <<EOF > debugrevlistspec.py
38 38 > from __future__ import absolute_import
39 39 > from mercurial import (
40 40 > node as nodemod,
41 41 > registrar,
42 42 > revset,
43 43 > revsetlang,
44 44 > smartset,
45 45 > )
46 46 > cmdtable = {}
47 47 > command = registrar.command(cmdtable)
48 48 > @command('debugrevlistspec',
49 49 > [('', 'optimize', None, 'print parsed tree after optimizing'),
50 50 > ('', 'bin', None, 'unhexlify arguments')])
51 51 > def debugrevlistspec(ui, repo, fmt, *args, **opts):
52 52 > if opts['bin']:
53 53 > args = map(nodemod.bin, args)
54 54 > expr = revsetlang.formatspec(fmt, list(args))
55 55 > if ui.verbose:
56 56 > tree = revsetlang.parse(expr, lookup=repo.__contains__)
57 57 > ui.note(revsetlang.prettyformat(tree), "\n")
58 58 > if opts["optimize"]:
59 59 > opttree = revsetlang.optimize(revsetlang.analyze(tree))
60 60 > ui.note("* optimized:\n", revsetlang.prettyformat(opttree),
61 61 > "\n")
62 62 > func = revset.match(ui, expr, repo)
63 63 > revs = func(repo)
64 64 > if ui.verbose:
65 65 > ui.note("* set:\n", smartset.prettyformat(revs), "\n")
66 66 > for c in revs:
67 67 > ui.write("%s\n" % c)
68 68 > EOF
69 69 $ cat <<EOF >> $HGRCPATH
70 70 > [extensions]
71 71 > debugrevlistspec = $TESTTMP/debugrevlistspec.py
72 72 > EOF
73 73 $ trylist() {
74 74 > hg debugrevlistspec --debug "$@"
75 75 > }
76 76
77 77 $ hg init repo
78 78 $ cd repo
79 79
80 80 $ echo a > a
81 81 $ hg branch a
82 82 marked working directory as branch a
83 83 (branches are permanent and global, did you want a bookmark?)
84 84 $ hg ci -Aqm0
85 85
86 86 $ echo b > b
87 87 $ hg branch b
88 88 marked working directory as branch b
89 89 $ hg ci -Aqm1
90 90
91 91 $ rm a
92 92 $ hg branch a-b-c-
93 93 marked working directory as branch a-b-c-
94 94 $ hg ci -Aqm2 -u Bob
95 95
96 96 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
97 97 2
98 98 $ hg log -r "extra('branch')" --template '{rev}\n'
99 99 0
100 100 1
101 101 2
102 102 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
103 103 0 a
104 104 2 a-b-c-
105 105
106 106 $ hg co 1
107 107 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
108 108 $ hg branch +a+b+c+
109 109 marked working directory as branch +a+b+c+
110 110 $ hg ci -Aqm3
111 111
112 112 $ hg co 2 # interleave
113 113 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
114 114 $ echo bb > b
115 115 $ hg branch -- -a-b-c-
116 116 marked working directory as branch -a-b-c-
117 117 $ hg ci -Aqm4 -d "May 12 2005"
118 118
119 119 $ hg co 3
120 120 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
121 121 $ hg branch !a/b/c/
122 122 marked working directory as branch !a/b/c/
123 123 $ hg ci -Aqm"5 bug"
124 124
125 125 $ hg merge 4
126 126 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
127 127 (branch merge, don't forget to commit)
128 128 $ hg branch _a_b_c_
129 129 marked working directory as branch _a_b_c_
130 130 $ hg ci -Aqm"6 issue619"
131 131
132 132 $ hg branch .a.b.c.
133 133 marked working directory as branch .a.b.c.
134 134 $ hg ci -Aqm7
135 135
136 136 $ hg branch all
137 137 marked working directory as branch all
138 138
139 139 $ hg co 4
140 140 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
141 141 $ hg branch Γ©
142 142 marked working directory as branch \xc3\xa9 (esc)
143 143 $ hg ci -Aqm9
144 144
145 145 $ hg tag -r6 1.0
146 146 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
147 147
148 148 $ hg clone --quiet -U -r 7 . ../remote1
149 149 $ hg clone --quiet -U -r 8 . ../remote2
150 150 $ echo "[paths]" >> .hg/hgrc
151 151 $ echo "default = ../remote1" >> .hg/hgrc
152 152
153 153 trivial
154 154
155 155 $ try 0:1
156 156 (range
157 157 ('symbol', '0')
158 158 ('symbol', '1'))
159 159 * set:
160 160 <spanset+ 0:1>
161 161 0
162 162 1
163 163 $ try --optimize :
164 164 (rangeall
165 165 None)
166 166 * optimized:
167 167 (rangeall
168 168 None
169 169 define)
170 170 * set:
171 171 <spanset+ 0:9>
172 172 0
173 173 1
174 174 2
175 175 3
176 176 4
177 177 5
178 178 6
179 179 7
180 180 8
181 181 9
182 182 $ try 3::6
183 183 (dagrange
184 184 ('symbol', '3')
185 185 ('symbol', '6'))
186 186 * set:
187 187 <baseset+ [3, 5, 6]>
188 188 3
189 189 5
190 190 6
191 191 $ try '0|1|2'
192 192 (or
193 193 (list
194 194 ('symbol', '0')
195 195 ('symbol', '1')
196 196 ('symbol', '2')))
197 197 * set:
198 198 <baseset [0, 1, 2]>
199 199 0
200 200 1
201 201 2
202 202
203 203 names that should work without quoting
204 204
205 205 $ try a
206 206 ('symbol', 'a')
207 207 * set:
208 208 <baseset [0]>
209 209 0
210 210 $ try b-a
211 211 (minus
212 212 ('symbol', 'b')
213 213 ('symbol', 'a'))
214 214 * set:
215 215 <filteredset
216 216 <baseset [1]>,
217 217 <not
218 218 <baseset [0]>>>
219 219 1
220 220 $ try _a_b_c_
221 221 ('symbol', '_a_b_c_')
222 222 * set:
223 223 <baseset [6]>
224 224 6
225 225 $ try _a_b_c_-a
226 226 (minus
227 227 ('symbol', '_a_b_c_')
228 228 ('symbol', 'a'))
229 229 * set:
230 230 <filteredset
231 231 <baseset [6]>,
232 232 <not
233 233 <baseset [0]>>>
234 234 6
235 235 $ try .a.b.c.
236 236 ('symbol', '.a.b.c.')
237 237 * set:
238 238 <baseset [7]>
239 239 7
240 240 $ try .a.b.c.-a
241 241 (minus
242 242 ('symbol', '.a.b.c.')
243 243 ('symbol', 'a'))
244 244 * set:
245 245 <filteredset
246 246 <baseset [7]>,
247 247 <not
248 248 <baseset [0]>>>
249 249 7
250 250
251 251 names that should be caught by fallback mechanism
252 252
253 253 $ try -- '-a-b-c-'
254 254 ('symbol', '-a-b-c-')
255 255 * set:
256 256 <baseset [4]>
257 257 4
258 258 $ log -a-b-c-
259 259 4
260 260 $ try '+a+b+c+'
261 261 ('symbol', '+a+b+c+')
262 262 * set:
263 263 <baseset [3]>
264 264 3
265 265 $ try '+a+b+c+:'
266 266 (rangepost
267 267 ('symbol', '+a+b+c+'))
268 268 * set:
269 269 <spanset+ 3:9>
270 270 3
271 271 4
272 272 5
273 273 6
274 274 7
275 275 8
276 276 9
277 277 $ try ':+a+b+c+'
278 278 (rangepre
279 279 ('symbol', '+a+b+c+'))
280 280 * set:
281 281 <spanset+ 0:3>
282 282 0
283 283 1
284 284 2
285 285 3
286 286 $ try -- '-a-b-c-:+a+b+c+'
287 287 (range
288 288 ('symbol', '-a-b-c-')
289 289 ('symbol', '+a+b+c+'))
290 290 * set:
291 291 <spanset- 3:4>
292 292 4
293 293 3
294 294 $ log '-a-b-c-:+a+b+c+'
295 295 4
296 296 3
297 297
298 298 $ try -- -a-b-c--a # complains
299 299 (minus
300 300 (minus
301 301 (minus
302 302 (negate
303 303 ('symbol', 'a'))
304 304 ('symbol', 'b'))
305 305 ('symbol', 'c'))
306 306 (negate
307 307 ('symbol', 'a')))
308 308 abort: unknown revision '-a'!
309 309 [255]
310 310 $ try Γ©
311 311 ('symbol', '\xc3\xa9')
312 312 * set:
313 313 <baseset [9]>
314 314 9
315 315
316 316 no quoting needed
317 317
318 318 $ log ::a-b-c-
319 319 0
320 320 1
321 321 2
322 322
323 323 quoting needed
324 324
325 325 $ try '"-a-b-c-"-a'
326 326 (minus
327 327 ('string', '-a-b-c-')
328 328 ('symbol', 'a'))
329 329 * set:
330 330 <filteredset
331 331 <baseset [4]>,
332 332 <not
333 333 <baseset [0]>>>
334 334 4
335 335
336 336 $ log '1 or 2'
337 337 1
338 338 2
339 339 $ log '1|2'
340 340 1
341 341 2
342 342 $ log '1 and 2'
343 343 $ log '1&2'
344 344 $ try '1&2|3' # precedence - and is higher
345 345 (or
346 346 (list
347 347 (and
348 348 ('symbol', '1')
349 349 ('symbol', '2'))
350 350 ('symbol', '3')))
351 351 * set:
352 352 <addset
353 353 <baseset []>,
354 354 <baseset [3]>>
355 355 3
356 356 $ try '1|2&3'
357 357 (or
358 358 (list
359 359 ('symbol', '1')
360 360 (and
361 361 ('symbol', '2')
362 362 ('symbol', '3'))))
363 363 * set:
364 364 <addset
365 365 <baseset [1]>,
366 366 <baseset []>>
367 367 1
368 368 $ try '1&2&3' # associativity
369 369 (and
370 370 (and
371 371 ('symbol', '1')
372 372 ('symbol', '2'))
373 373 ('symbol', '3'))
374 374 * set:
375 375 <baseset []>
376 376 $ try '1|(2|3)'
377 377 (or
378 378 (list
379 379 ('symbol', '1')
380 380 (group
381 381 (or
382 382 (list
383 383 ('symbol', '2')
384 384 ('symbol', '3'))))))
385 385 * set:
386 386 <addset
387 387 <baseset [1]>,
388 388 <baseset [2, 3]>>
389 389 1
390 390 2
391 391 3
392 392 $ log '1.0' # tag
393 393 6
394 394 $ log 'a' # branch
395 395 0
396 396 $ log '2785f51ee'
397 397 0
398 398 $ log 'date(2005)'
399 399 4
400 400 $ log 'date(this is a test)'
401 401 hg: parse error at 10: unexpected token: symbol
402 402 [255]
403 403 $ log 'date()'
404 404 hg: parse error: date requires a string
405 405 [255]
406 406 $ log 'date'
407 407 abort: unknown revision 'date'!
408 408 [255]
409 409 $ log 'date('
410 410 hg: parse error at 5: not a prefix: end
411 411 [255]
412 412 $ log 'date("\xy")'
413 413 hg: parse error: invalid \x escape
414 414 [255]
415 415 $ log 'date(tip)'
416 416 hg: parse error: invalid date: 'tip'
417 417 [255]
418 418 $ log '0:date'
419 419 abort: unknown revision 'date'!
420 420 [255]
421 421 $ log '::"date"'
422 422 abort: unknown revision 'date'!
423 423 [255]
424 424 $ hg book date -r 4
425 425 $ log '0:date'
426 426 0
427 427 1
428 428 2
429 429 3
430 430 4
431 431 $ log '::date'
432 432 0
433 433 1
434 434 2
435 435 4
436 436 $ log '::"date"'
437 437 0
438 438 1
439 439 2
440 440 4
441 441 $ log 'date(2005) and 1::'
442 442 4
443 443 $ hg book -d date
444 444
445 445 function name should be a symbol
446 446
447 447 $ log '"date"(2005)'
448 448 hg: parse error: not a symbol
449 449 [255]
450 450
451 451 keyword arguments
452 452
453 453 $ log 'extra(branch, value=a)'
454 454 0
455 455
456 456 $ log 'extra(branch, a, b)'
457 457 hg: parse error: extra takes at most 2 positional arguments
458 458 [255]
459 459 $ log 'extra(a, label=b)'
460 460 hg: parse error: extra got multiple values for keyword argument 'label'
461 461 [255]
462 462 $ log 'extra(label=branch, default)'
463 463 hg: parse error: extra got an invalid argument
464 464 [255]
465 465 $ log 'extra(branch, foo+bar=baz)'
466 466 hg: parse error: extra got an invalid argument
467 467 [255]
468 468 $ log 'extra(unknown=branch)'
469 469 hg: parse error: extra got an unexpected keyword argument 'unknown'
470 470 [255]
471 471
472 472 $ try 'foo=bar|baz'
473 473 (keyvalue
474 474 ('symbol', 'foo')
475 475 (or
476 476 (list
477 477 ('symbol', 'bar')
478 478 ('symbol', 'baz'))))
479 479 hg: parse error: can't use a key-value pair in this context
480 480 [255]
481 481
482 482 right-hand side should be optimized recursively
483 483
484 484 $ try --optimize 'foo=(not public())'
485 485 (keyvalue
486 486 ('symbol', 'foo')
487 487 (group
488 488 (not
489 489 (func
490 490 ('symbol', 'public')
491 491 None))))
492 492 * optimized:
493 493 (keyvalue
494 494 ('symbol', 'foo')
495 495 (func
496 496 ('symbol', '_notpublic')
497 497 None
498 498 any))
499 499 hg: parse error: can't use a key-value pair in this context
500 500 [255]
501 501
502 502 parsed tree at stages:
503 503
504 504 $ hg debugrevspec -p all '()'
505 505 * parsed:
506 506 (group
507 507 None)
508 508 * expanded:
509 509 (group
510 510 None)
511 511 * concatenated:
512 512 (group
513 513 None)
514 514 * analyzed:
515 515 None
516 516 * optimized:
517 517 None
518 518 hg: parse error: missing argument
519 519 [255]
520 520
521 521 $ hg debugrevspec --no-optimized -p all '()'
522 522 * parsed:
523 523 (group
524 524 None)
525 525 * expanded:
526 526 (group
527 527 None)
528 528 * concatenated:
529 529 (group
530 530 None)
531 531 * analyzed:
532 532 None
533 533 hg: parse error: missing argument
534 534 [255]
535 535
536 536 $ hg debugrevspec -p parsed -p analyzed -p optimized '(0|1)-1'
537 537 * parsed:
538 538 (minus
539 539 (group
540 540 (or
541 541 (list
542 542 ('symbol', '0')
543 543 ('symbol', '1'))))
544 544 ('symbol', '1'))
545 545 * analyzed:
546 546 (and
547 547 (or
548 548 (list
549 549 ('symbol', '0')
550 550 ('symbol', '1'))
551 551 define)
552 552 (not
553 553 ('symbol', '1')
554 554 follow)
555 555 define)
556 556 * optimized:
557 557 (difference
558 558 (func
559 559 ('symbol', '_list')
560 560 ('string', '0\x001')
561 561 define)
562 562 ('symbol', '1')
563 563 define)
564 564 0
565 565
566 566 $ hg debugrevspec -p unknown '0'
567 567 abort: invalid stage name: unknown
568 568 [255]
569 569
570 570 $ hg debugrevspec -p all --optimize '0'
571 571 abort: cannot use --optimize with --show-stage
572 572 [255]
573 573
574 574 verify optimized tree:
575 575
576 576 $ hg debugrevspec --verify '0|1'
577 577
578 578 $ hg debugrevspec --verify -v -p analyzed -p optimized 'r3232() & 2'
579 579 * analyzed:
580 580 (and
581 581 (func
582 582 ('symbol', 'r3232')
583 583 None
584 584 define)
585 585 ('symbol', '2')
586 586 define)
587 587 * optimized:
588 588 (and
589 589 ('symbol', '2')
590 590 (func
591 591 ('symbol', 'r3232')
592 592 None
593 593 define)
594 594 define)
595 595 * analyzed set:
596 596 <baseset [2]>
597 597 * optimized set:
598 598 <baseset [2, 2]>
599 599 --- analyzed
600 600 +++ optimized
601 601 2
602 602 +2
603 603 [1]
604 604
605 605 $ hg debugrevspec --no-optimized --verify-optimized '0'
606 606 abort: cannot use --verify-optimized with --no-optimized
607 607 [255]
608 608
609 609 Test that symbols only get parsed as functions if there's an opening
610 610 parenthesis.
611 611
612 612 $ hg book only -r 9
613 613 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
614 614 8
615 615 9
616 616
617 617 ':y' behaves like '0:y', but can't be rewritten as such since the revision '0'
618 618 may be hidden (issue5385)
619 619
620 620 $ try -p parsed -p analyzed ':'
621 621 * parsed:
622 622 (rangeall
623 623 None)
624 624 * analyzed:
625 625 (rangeall
626 626 None
627 627 define)
628 628 * set:
629 629 <spanset+ 0:9>
630 630 0
631 631 1
632 632 2
633 633 3
634 634 4
635 635 5
636 636 6
637 637 7
638 638 8
639 639 9
640 640 $ try -p analyzed ':1'
641 641 * analyzed:
642 642 (rangepre
643 643 ('symbol', '1')
644 644 define)
645 645 * set:
646 646 <spanset+ 0:1>
647 647 0
648 648 1
649 649 $ try -p analyzed ':(1|2)'
650 650 * analyzed:
651 651 (rangepre
652 652 (or
653 653 (list
654 654 ('symbol', '1')
655 655 ('symbol', '2'))
656 656 define)
657 657 define)
658 658 * set:
659 659 <spanset+ 0:2>
660 660 0
661 661 1
662 662 2
663 663 $ try -p analyzed ':(1&2)'
664 664 * analyzed:
665 665 (rangepre
666 666 (and
667 667 ('symbol', '1')
668 668 ('symbol', '2')
669 669 define)
670 670 define)
671 671 * set:
672 672 <baseset []>
673 673
674 674 infix/suffix resolution of ^ operator (issue2884):
675 675
676 676 x^:y means (x^):y
677 677
678 678 $ try '1^:2'
679 679 (range
680 680 (parentpost
681 681 ('symbol', '1'))
682 682 ('symbol', '2'))
683 683 * set:
684 684 <spanset+ 0:2>
685 685 0
686 686 1
687 687 2
688 688
689 689 $ try '1^::2'
690 690 (dagrange
691 691 (parentpost
692 692 ('symbol', '1'))
693 693 ('symbol', '2'))
694 694 * set:
695 695 <baseset+ [0, 1, 2]>
696 696 0
697 697 1
698 698 2
699 699
700 700 $ try '9^:'
701 701 (rangepost
702 702 (parentpost
703 703 ('symbol', '9')))
704 704 * set:
705 705 <spanset+ 8:9>
706 706 8
707 707 9
708 708
709 709 x^:y should be resolved before omitting group operators
710 710
711 711 $ try '1^(:2)'
712 712 (parent
713 713 ('symbol', '1')
714 714 (group
715 715 (rangepre
716 716 ('symbol', '2'))))
717 717 hg: parse error: ^ expects a number 0, 1, or 2
718 718 [255]
719 719
720 720 x^:y should be resolved recursively
721 721
722 722 $ try 'sort(1^:2)'
723 723 (func
724 724 ('symbol', 'sort')
725 725 (range
726 726 (parentpost
727 727 ('symbol', '1'))
728 728 ('symbol', '2')))
729 729 * set:
730 730 <spanset+ 0:2>
731 731 0
732 732 1
733 733 2
734 734
735 735 $ try '(3^:4)^:2'
736 736 (range
737 737 (parentpost
738 738 (group
739 739 (range
740 740 (parentpost
741 741 ('symbol', '3'))
742 742 ('symbol', '4'))))
743 743 ('symbol', '2'))
744 744 * set:
745 745 <spanset+ 0:2>
746 746 0
747 747 1
748 748 2
749 749
750 750 $ try '(3^::4)^::2'
751 751 (dagrange
752 752 (parentpost
753 753 (group
754 754 (dagrange
755 755 (parentpost
756 756 ('symbol', '3'))
757 757 ('symbol', '4'))))
758 758 ('symbol', '2'))
759 759 * set:
760 760 <baseset+ [0, 1, 2]>
761 761 0
762 762 1
763 763 2
764 764
765 765 $ try '(9^:)^:'
766 766 (rangepost
767 767 (parentpost
768 768 (group
769 769 (rangepost
770 770 (parentpost
771 771 ('symbol', '9'))))))
772 772 * set:
773 773 <spanset+ 4:9>
774 774 4
775 775 5
776 776 6
777 777 7
778 778 8
779 779 9
780 780
781 781 x^ in alias should also be resolved
782 782
783 783 $ try 'A' --config 'revsetalias.A=1^:2'
784 784 ('symbol', 'A')
785 785 * expanded:
786 786 (range
787 787 (parentpost
788 788 ('symbol', '1'))
789 789 ('symbol', '2'))
790 790 * set:
791 791 <spanset+ 0:2>
792 792 0
793 793 1
794 794 2
795 795
796 796 $ try 'A:2' --config 'revsetalias.A=1^'
797 797 (range
798 798 ('symbol', 'A')
799 799 ('symbol', '2'))
800 800 * expanded:
801 801 (range
802 802 (parentpost
803 803 ('symbol', '1'))
804 804 ('symbol', '2'))
805 805 * set:
806 806 <spanset+ 0:2>
807 807 0
808 808 1
809 809 2
810 810
811 811 but not beyond the boundary of alias expansion, because the resolution should
812 812 be made at the parsing stage
813 813
814 814 $ try '1^A' --config 'revsetalias.A=:2'
815 815 (parent
816 816 ('symbol', '1')
817 817 ('symbol', 'A'))
818 818 * expanded:
819 819 (parent
820 820 ('symbol', '1')
821 821 (rangepre
822 822 ('symbol', '2')))
823 823 hg: parse error: ^ expects a number 0, 1, or 2
824 824 [255]
825 825
826 826 ancestor can accept 0 or more arguments
827 827
828 828 $ log 'ancestor()'
829 829 $ log 'ancestor(1)'
830 830 1
831 831 $ log 'ancestor(4,5)'
832 832 1
833 833 $ log 'ancestor(4,5) and 4'
834 834 $ log 'ancestor(0,0,1,3)'
835 835 0
836 836 $ log 'ancestor(3,1,5,3,5,1)'
837 837 1
838 838 $ log 'ancestor(0,1,3,5)'
839 839 0
840 840 $ log 'ancestor(1,2,3,4,5)'
841 841 1
842 842
843 843 test ancestors
844 844
845 845 $ log 'ancestors(5)'
846 846 0
847 847 1
848 848 3
849 849 5
850 850 $ log 'ancestor(ancestors(5))'
851 851 0
852 852 $ log '::r3232()'
853 853 0
854 854 1
855 855 2
856 856 3
857 857
858 858 $ log 'author(bob)'
859 859 2
860 860 $ log 'author("re:bob|test")'
861 861 0
862 862 1
863 863 2
864 864 3
865 865 4
866 866 5
867 867 6
868 868 7
869 869 8
870 870 9
871 871 $ log 'author(r"re:\S")'
872 872 0
873 873 1
874 874 2
875 875 3
876 876 4
877 877 5
878 878 6
879 879 7
880 880 8
881 881 9
882 882 $ log 'branch(Γ©)'
883 883 8
884 884 9
885 885 $ log 'branch(a)'
886 886 0
887 887 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
888 888 0 a
889 889 2 a-b-c-
890 890 3 +a+b+c+
891 891 4 -a-b-c-
892 892 5 !a/b/c/
893 893 6 _a_b_c_
894 894 7 .a.b.c.
895 895 $ log 'children(ancestor(4,5))'
896 896 2
897 897 3
898 898
899 899 $ log 'children(4)'
900 900 6
901 901 8
902 902 $ log 'children(null)'
903 903 0
904 904
905 905 $ log 'closed()'
906 906 $ log 'contains(a)'
907 907 0
908 908 1
909 909 3
910 910 5
911 911 $ log 'contains("../repo/a")'
912 912 0
913 913 1
914 914 3
915 915 5
916 916 $ log 'desc(B)'
917 917 5
918 918 $ hg log -r 'desc(r"re:S?u")' --template "{rev} {desc|firstline}\n"
919 919 5 5 bug
920 920 6 6 issue619
921 921 $ log 'descendants(2 or 3)'
922 922 2
923 923 3
924 924 4
925 925 5
926 926 6
927 927 7
928 928 8
929 929 9
930 930 $ log 'file("b*")'
931 931 1
932 932 4
933 933 $ log 'filelog("b")'
934 934 1
935 935 4
936 936 $ log 'filelog("../repo/b")'
937 937 1
938 938 4
939 939 $ log 'follow()'
940 940 0
941 941 1
942 942 2
943 943 4
944 944 8
945 945 9
946 946 $ log 'grep("issue\d+")'
947 947 6
948 948 $ try 'grep("(")' # invalid regular expression
949 949 (func
950 950 ('symbol', 'grep')
951 951 ('string', '('))
952 952 hg: parse error: invalid match pattern: unbalanced parenthesis
953 953 [255]
954 954 $ try 'grep("\bissue\d+")'
955 955 (func
956 956 ('symbol', 'grep')
957 957 ('string', '\x08issue\\d+'))
958 958 * set:
959 959 <filteredset
960 960 <fullreposet+ 0:9>,
961 961 <grep '\x08issue\\d+'>>
962 962 $ try 'grep(r"\bissue\d+")'
963 963 (func
964 964 ('symbol', 'grep')
965 965 ('string', '\\bissue\\d+'))
966 966 * set:
967 967 <filteredset
968 968 <fullreposet+ 0:9>,
969 969 <grep '\\bissue\\d+'>>
970 970 6
971 971 $ try 'grep(r"\")'
972 972 hg: parse error at 7: unterminated string
973 973 [255]
974 974 $ log 'head()'
975 975 0
976 976 1
977 977 2
978 978 3
979 979 4
980 980 5
981 981 6
982 982 7
983 983 9
984 984 $ log 'heads(6::)'
985 985 7
986 986 $ log 'keyword(issue)'
987 987 6
988 988 $ log 'keyword("test a")'
989 989 $ log 'limit(head(), 1)'
990 990 0
991 991 $ log 'limit(author("re:bob|test"), 3, 5)'
992 992 5
993 993 6
994 994 7
995 995 $ log 'limit(author("re:bob|test"), offset=6)'
996 996 6
997 997 $ log 'limit(author("re:bob|test"), offset=10)'
998 998 $ log 'limit(all(), 1, -1)'
999 999 hg: parse error: negative offset
1000 1000 [255]
1001 1001 $ log 'matching(6)'
1002 1002 6
1003 1003 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
1004 1004 6
1005 1005 7
1006 1006
1007 1007 Testing min and max
1008 1008
1009 1009 max: simple
1010 1010
1011 1011 $ log 'max(contains(a))'
1012 1012 5
1013 1013
1014 1014 max: simple on unordered set)
1015 1015
1016 1016 $ log 'max((4+0+2+5+7) and contains(a))'
1017 1017 5
1018 1018
1019 1019 max: no result
1020 1020
1021 1021 $ log 'max(contains(stringthatdoesnotappearanywhere))'
1022 1022
1023 1023 max: no result on unordered set
1024 1024
1025 1025 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1026 1026
1027 1027 min: simple
1028 1028
1029 1029 $ log 'min(contains(a))'
1030 1030 0
1031 1031
1032 1032 min: simple on unordered set
1033 1033
1034 1034 $ log 'min((4+0+2+5+7) and contains(a))'
1035 1035 0
1036 1036
1037 1037 min: empty
1038 1038
1039 1039 $ log 'min(contains(stringthatdoesnotappearanywhere))'
1040 1040
1041 1041 min: empty on unordered set
1042 1042
1043 1043 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1044 1044
1045 1045
1046 1046 $ log 'merge()'
1047 1047 6
1048 1048 $ log 'branchpoint()'
1049 1049 1
1050 1050 4
1051 1051 $ log 'modifies(b)'
1052 1052 4
1053 1053 $ log 'modifies("path:b")'
1054 1054 4
1055 1055 $ log 'modifies("*")'
1056 1056 4
1057 1057 6
1058 1058 $ log 'modifies("set:modified()")'
1059 1059 4
1060 1060 $ log 'id(5)'
1061 1061 2
1062 1062 $ log 'only(9)'
1063 1063 8
1064 1064 9
1065 1065 $ log 'only(8)'
1066 1066 8
1067 1067 $ log 'only(9, 5)'
1068 1068 2
1069 1069 4
1070 1070 8
1071 1071 9
1072 1072 $ log 'only(7 + 9, 5 + 2)'
1073 1073 4
1074 1074 6
1075 1075 7
1076 1076 8
1077 1077 9
1078 1078
1079 1079 Test empty set input
1080 1080 $ log 'only(p2())'
1081 1081 $ log 'only(p1(), p2())'
1082 1082 0
1083 1083 1
1084 1084 2
1085 1085 4
1086 1086 8
1087 1087 9
1088 1088
1089 1089 Test '%' operator
1090 1090
1091 1091 $ log '9%'
1092 1092 8
1093 1093 9
1094 1094 $ log '9%5'
1095 1095 2
1096 1096 4
1097 1097 8
1098 1098 9
1099 1099 $ log '(7 + 9)%(5 + 2)'
1100 1100 4
1101 1101 6
1102 1102 7
1103 1103 8
1104 1104 9
1105 1105
1106 1106 Test operand of '%' is optimized recursively (issue4670)
1107 1107
1108 1108 $ try --optimize '8:9-8%'
1109 1109 (onlypost
1110 1110 (minus
1111 1111 (range
1112 1112 ('symbol', '8')
1113 1113 ('symbol', '9'))
1114 1114 ('symbol', '8')))
1115 1115 * optimized:
1116 1116 (func
1117 1117 ('symbol', 'only')
1118 1118 (difference
1119 1119 (range
1120 1120 ('symbol', '8')
1121 1121 ('symbol', '9')
1122 1122 define)
1123 1123 ('symbol', '8')
1124 1124 define)
1125 1125 define)
1126 1126 * set:
1127 1127 <baseset+ [8, 9]>
1128 1128 8
1129 1129 9
1130 1130 $ try --optimize '(9)%(5)'
1131 1131 (only
1132 1132 (group
1133 1133 ('symbol', '9'))
1134 1134 (group
1135 1135 ('symbol', '5')))
1136 1136 * optimized:
1137 1137 (func
1138 1138 ('symbol', 'only')
1139 1139 (list
1140 1140 ('symbol', '9')
1141 1141 ('symbol', '5'))
1142 1142 define)
1143 1143 * set:
1144 1144 <baseset+ [2, 4, 8, 9]>
1145 1145 2
1146 1146 4
1147 1147 8
1148 1148 9
1149 1149
1150 1150 Test the order of operations
1151 1151
1152 1152 $ log '7 + 9%5 + 2'
1153 1153 7
1154 1154 2
1155 1155 4
1156 1156 8
1157 1157 9
1158 1158
1159 1159 Test explicit numeric revision
1160 1160 $ log 'rev(-2)'
1161 1161 $ log 'rev(-1)'
1162 1162 -1
1163 1163 $ log 'rev(0)'
1164 1164 0
1165 1165 $ log 'rev(9)'
1166 1166 9
1167 1167 $ log 'rev(10)'
1168 1168 $ log 'rev(tip)'
1169 1169 hg: parse error: rev expects a number
1170 1170 [255]
1171 1171
1172 1172 Test hexadecimal revision
1173 1173 $ log 'id(2)'
1174 1174 abort: 00changelog.i@2: ambiguous identifier!
1175 1175 [255]
1176 1176 $ log 'id(23268)'
1177 1177 4
1178 1178 $ log 'id(2785f51eece)'
1179 1179 0
1180 1180 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
1181 1181 8
1182 1182 $ log 'id(d5d0dcbdc4a)'
1183 1183 $ log 'id(d5d0dcbdc4w)'
1184 1184 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
1185 1185 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
1186 1186 $ log 'id(1.0)'
1187 1187 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
1188 1188
1189 1189 Test null revision
1190 1190 $ log '(null)'
1191 1191 -1
1192 1192 $ log '(null:0)'
1193 1193 -1
1194 1194 0
1195 1195 $ log '(0:null)'
1196 1196 0
1197 1197 -1
1198 1198 $ log 'null::0'
1199 1199 -1
1200 1200 0
1201 1201 $ log 'null:tip - 0:'
1202 1202 -1
1203 1203 $ log 'null: and null::' | head -1
1204 1204 -1
1205 1205 $ log 'null: or 0:' | head -2
1206 1206 -1
1207 1207 0
1208 1208 $ log 'ancestors(null)'
1209 1209 -1
1210 1210 $ log 'reverse(null:)' | tail -2
1211 1211 0
1212 1212 -1
1213 1213 BROKEN: should be '-1'
1214 1214 $ log 'first(null:)'
1215 1215 BROKEN: should be '-1'
1216 1216 $ log 'min(null:)'
1217 1217 $ log 'tip:null and all()' | tail -2
1218 1218 1
1219 1219 0
1220 1220
1221 1221 Test working-directory revision
1222 1222 $ hg debugrevspec 'wdir()'
1223 1223 2147483647
1224 1224 $ hg debugrevspec 'wdir()^'
1225 1225 9
1226 1226 $ hg up 7
1227 1227 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1228 1228 $ hg debugrevspec 'wdir()^'
1229 1229 7
1230 1230 $ hg debugrevspec 'wdir()^0'
1231 1231 2147483647
1232 1232 $ hg debugrevspec 'wdir()~3'
1233 1233 5
1234 1234 $ hg debugrevspec 'ancestors(wdir())'
1235 1235 0
1236 1236 1
1237 1237 2
1238 1238 3
1239 1239 4
1240 1240 5
1241 1241 6
1242 1242 7
1243 1243 2147483647
1244 1244 $ hg debugrevspec 'wdir()~0'
1245 1245 2147483647
1246 1246 $ hg debugrevspec 'p1(wdir())'
1247 1247 7
1248 1248 $ hg debugrevspec 'p2(wdir())'
1249 1249 $ hg debugrevspec 'parents(wdir())'
1250 1250 7
1251 1251 $ hg debugrevspec 'wdir()^1'
1252 1252 7
1253 1253 $ hg debugrevspec 'wdir()^2'
1254 1254 $ hg debugrevspec 'wdir()^3'
1255 1255 hg: parse error: ^ expects a number 0, 1, or 2
1256 1256 [255]
1257 1257 For tests consistency
1258 1258 $ hg up 9
1259 1259 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1260 1260 $ hg debugrevspec 'tip or wdir()'
1261 1261 9
1262 1262 2147483647
1263 1263 $ hg debugrevspec '0:tip and wdir()'
1264 1264 $ log '0:wdir()' | tail -3
1265 1265 8
1266 1266 9
1267 1267 2147483647
1268 1268 $ log 'wdir():0' | head -3
1269 1269 2147483647
1270 1270 9
1271 1271 8
1272 1272 $ log 'wdir():wdir()'
1273 1273 2147483647
1274 1274 $ log '(all() + wdir()) & min(. + wdir())'
1275 1275 9
1276 1276 $ log '(all() + wdir()) & max(. + wdir())'
1277 1277 2147483647
1278 1278 $ log '(all() + wdir()) & first(wdir() + .)'
1279 1279 2147483647
1280 1280 $ log '(all() + wdir()) & last(. + wdir())'
1281 1281 2147483647
1282 1282
1283 1283 Test working-directory integer revision and node id
1284 1284 (BUG: '0:wdir()' is still needed to populate wdir revision)
1285 1285
1286 1286 $ hg debugrevspec '0:wdir() & 2147483647'
1287 1287 2147483647
1288 1288 $ hg debugrevspec '0:wdir() & rev(2147483647)'
1289 1289 2147483647
1290 1290 $ hg debugrevspec '0:wdir() & ffffffffffffffffffffffffffffffffffffffff'
1291 1291 2147483647
1292 1292 $ hg debugrevspec '0:wdir() & ffffffffffff'
1293 1293 2147483647
1294 1294 $ hg debugrevspec '0:wdir() & id(ffffffffffffffffffffffffffffffffffffffff)'
1295 1295 2147483647
1296 1296 $ hg debugrevspec '0:wdir() & id(ffffffffffff)'
1297 1297 2147483647
1298 1298
1299 1299 $ cd ..
1300 1300
1301 1301 Test short 'ff...' hash collision
1302 1302 (BUG: '0:wdir()' is still needed to populate wdir revision)
1303 1303
1304 1304 $ hg init wdir-hashcollision
1305 1305 $ cd wdir-hashcollision
1306 1306 $ cat <<EOF >> .hg/hgrc
1307 1307 > [experimental]
1308 1308 > evolution = createmarkers
1309 1309 > EOF
1310 1310 $ echo 0 > a
1311 1311 $ hg ci -qAm 0
1312 1312 $ for i in 2463 2961 6726 78127; do
1313 1313 > hg up -q 0
1314 1314 > echo $i > a
1315 1315 > hg ci -qm $i
1316 1316 > done
1317 1317 $ hg up -q null
1318 1318 $ hg log -r '0:wdir()' -T '{rev}:{node} {shortest(node, 3)}\n'
1319 1319 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a b4e
1320 1320 1:fffbae3886c8fbb2114296380d276fd37715d571 fffba
1321 1321 2:fffb6093b00943f91034b9bdad069402c834e572 fffb6
1322 1322 3:fff48a9b9de34a4d64120c29548214c67980ade3 fff4
1323 1323 4:ffff85cff0ff78504fcdc3c0bc10de0c65379249 ffff8
1324 1324 2147483647:ffffffffffffffffffffffffffffffffffffffff fffff
1325 1325 $ hg debugobsolete fffbae3886c8fbb2114296380d276fd37715d571
1326 1326
1327 1327 $ hg debugrevspec '0:wdir() & fff'
1328 1328 abort: 00changelog.i@fff: ambiguous identifier!
1329 1329 [255]
1330 1330 $ hg debugrevspec '0:wdir() & ffff'
1331 1331 abort: 00changelog.i@ffff: ambiguous identifier!
1332 1332 [255]
1333 1333 $ hg debugrevspec '0:wdir() & fffb'
1334 1334 abort: 00changelog.i@fffb: ambiguous identifier!
1335 1335 [255]
1336 1336 BROKEN should be '2' (node lookup uses unfiltered repo since dc25ed84bee8)
1337 1337 $ hg debugrevspec '0:wdir() & id(fffb)'
1338 1338 2
1339 1339 $ hg debugrevspec '0:wdir() & ffff8'
1340 1340 4
1341 1341 $ hg debugrevspec '0:wdir() & fffff'
1342 1342 2147483647
1343 1343
1344 1344 $ cd ..
1345 1345
1346 1346 Test branch() with wdir()
1347 1347
1348 1348 $ cd repo
1349 1349
1350 1350 $ log '0:wdir() & branch("literal:Γ©")'
1351 1351 8
1352 1352 9
1353 1353 2147483647
1354 1354 $ log '0:wdir() & branch("re:Γ©")'
1355 1355 8
1356 1356 9
1357 1357 2147483647
1358 1358 $ log '0:wdir() & branch("re:^a")'
1359 1359 0
1360 1360 2
1361 1361 $ log '0:wdir() & branch(8)'
1362 1362 8
1363 1363 9
1364 1364 2147483647
1365 1365
1366 1366 branch(wdir()) returns all revisions belonging to the working branch. The wdir
1367 1367 itself isn't returned unless it is explicitly populated.
1368 1368
1369 1369 $ log 'branch(wdir())'
1370 1370 8
1371 1371 9
1372 1372 $ log '0:wdir() & branch(wdir())'
1373 1373 8
1374 1374 9
1375 1375 2147483647
1376 1376
1377 1377 $ log 'outgoing()'
1378 1378 8
1379 1379 9
1380 1380 $ log 'outgoing("../remote1")'
1381 1381 8
1382 1382 9
1383 1383 $ log 'outgoing("../remote2")'
1384 1384 3
1385 1385 5
1386 1386 6
1387 1387 7
1388 1388 9
1389 1389 $ log 'p1(merge())'
1390 1390 5
1391 1391 $ log 'p2(merge())'
1392 1392 4
1393 1393 $ log 'parents(merge())'
1394 1394 4
1395 1395 5
1396 1396 $ log 'p1(branchpoint())'
1397 1397 0
1398 1398 2
1399 1399 $ log 'p2(branchpoint())'
1400 1400 $ log 'parents(branchpoint())'
1401 1401 0
1402 1402 2
1403 1403 $ log 'removes(a)'
1404 1404 2
1405 1405 6
1406 1406 $ log 'roots(all())'
1407 1407 0
1408 1408 $ log 'reverse(2 or 3 or 4 or 5)'
1409 1409 5
1410 1410 4
1411 1411 3
1412 1412 2
1413 1413 $ log 'reverse(all())'
1414 1414 9
1415 1415 8
1416 1416 7
1417 1417 6
1418 1418 5
1419 1419 4
1420 1420 3
1421 1421 2
1422 1422 1
1423 1423 0
1424 1424 $ log 'reverse(all()) & filelog(b)'
1425 1425 4
1426 1426 1
1427 1427 $ log 'rev(5)'
1428 1428 5
1429 1429 $ log 'sort(limit(reverse(all()), 3))'
1430 1430 7
1431 1431 8
1432 1432 9
1433 1433 $ log 'sort(2 or 3 or 4 or 5, date)'
1434 1434 2
1435 1435 3
1436 1436 5
1437 1437 4
1438 1438 $ log 'tagged()'
1439 1439 6
1440 1440 $ log 'tag()'
1441 1441 6
1442 1442 $ log 'tag(1.0)'
1443 1443 6
1444 1444 $ log 'tag(tip)'
1445 1445 9
1446 1446
1447 1447 Test order of revisions in compound expression
1448 1448 ----------------------------------------------
1449 1449
1450 1450 The general rule is that only the outermost (= leftmost) predicate can
1451 1451 enforce its ordering requirement. The other predicates should take the
1452 1452 ordering defined by it.
1453 1453
1454 1454 'A & B' should follow the order of 'A':
1455 1455
1456 1456 $ log '2:0 & 0::2'
1457 1457 2
1458 1458 1
1459 1459 0
1460 1460
1461 1461 'head()' combines sets in right order:
1462 1462
1463 1463 $ log '2:0 & head()'
1464 1464 2
1465 1465 1
1466 1466 0
1467 1467
1468 1468 'x:y' takes ordering parameter into account:
1469 1469
1470 1470 $ try -p optimized '3:0 & 0:3 & not 2:1'
1471 1471 * optimized:
1472 1472 (difference
1473 1473 (and
1474 1474 (range
1475 1475 ('symbol', '3')
1476 1476 ('symbol', '0')
1477 1477 define)
1478 1478 (range
1479 1479 ('symbol', '0')
1480 1480 ('symbol', '3')
1481 1481 follow)
1482 1482 define)
1483 1483 (range
1484 1484 ('symbol', '2')
1485 1485 ('symbol', '1')
1486 1486 any)
1487 1487 define)
1488 1488 * set:
1489 1489 <filteredset
1490 1490 <filteredset
1491 1491 <spanset- 0:3>,
1492 1492 <spanset+ 0:3>>,
1493 1493 <not
1494 1494 <spanset+ 1:2>>>
1495 1495 3
1496 1496 0
1497 1497
1498 1498 'a + b', which is optimized to '_list(a b)', should take the ordering of
1499 1499 the left expression:
1500 1500
1501 1501 $ try --optimize '2:0 & (0 + 1 + 2)'
1502 1502 (and
1503 1503 (range
1504 1504 ('symbol', '2')
1505 1505 ('symbol', '0'))
1506 1506 (group
1507 1507 (or
1508 1508 (list
1509 1509 ('symbol', '0')
1510 1510 ('symbol', '1')
1511 1511 ('symbol', '2')))))
1512 1512 * optimized:
1513 1513 (and
1514 1514 (range
1515 1515 ('symbol', '2')
1516 1516 ('symbol', '0')
1517 1517 define)
1518 1518 (func
1519 1519 ('symbol', '_list')
1520 1520 ('string', '0\x001\x002')
1521 1521 follow)
1522 1522 define)
1523 1523 * set:
1524 1524 <filteredset
1525 1525 <spanset- 0:2>,
1526 1526 <baseset [0, 1, 2]>>
1527 1527 2
1528 1528 1
1529 1529 0
1530 1530
1531 1531 'A + B' should take the ordering of the left expression:
1532 1532
1533 1533 $ try --optimize '2:0 & (0:1 + 2)'
1534 1534 (and
1535 1535 (range
1536 1536 ('symbol', '2')
1537 1537 ('symbol', '0'))
1538 1538 (group
1539 1539 (or
1540 1540 (list
1541 1541 (range
1542 1542 ('symbol', '0')
1543 1543 ('symbol', '1'))
1544 1544 ('symbol', '2')))))
1545 1545 * optimized:
1546 1546 (and
1547 1547 (range
1548 1548 ('symbol', '2')
1549 1549 ('symbol', '0')
1550 1550 define)
1551 1551 (or
1552 1552 (list
1553 1553 ('symbol', '2')
1554 1554 (range
1555 1555 ('symbol', '0')
1556 1556 ('symbol', '1')
1557 1557 follow))
1558 1558 follow)
1559 1559 define)
1560 1560 * set:
1561 1561 <filteredset
1562 1562 <spanset- 0:2>,
1563 1563 <addset
1564 1564 <baseset [2]>,
1565 1565 <spanset+ 0:1>>>
1566 1566 2
1567 1567 1
1568 1568 0
1569 1569
1570 1570 '_intlist(a b)' should behave like 'a + b':
1571 1571
1572 1572 $ trylist --optimize '2:0 & %ld' 0 1 2
1573 1573 (and
1574 1574 (range
1575 1575 ('symbol', '2')
1576 1576 ('symbol', '0'))
1577 1577 (func
1578 1578 ('symbol', '_intlist')
1579 1579 ('string', '0\x001\x002')))
1580 1580 * optimized:
1581 1581 (and
1582 1582 (func
1583 1583 ('symbol', '_intlist')
1584 1584 ('string', '0\x001\x002')
1585 1585 follow)
1586 1586 (range
1587 1587 ('symbol', '2')
1588 1588 ('symbol', '0')
1589 1589 define)
1590 1590 define)
1591 1591 * set:
1592 1592 <filteredset
1593 1593 <spanset- 0:2>,
1594 1594 <baseset+ [0, 1, 2]>>
1595 1595 2
1596 1596 1
1597 1597 0
1598 1598
1599 1599 $ trylist --optimize '%ld & 2:0' 0 2 1
1600 1600 (and
1601 1601 (func
1602 1602 ('symbol', '_intlist')
1603 1603 ('string', '0\x002\x001'))
1604 1604 (range
1605 1605 ('symbol', '2')
1606 1606 ('symbol', '0')))
1607 1607 * optimized:
1608 1608 (and
1609 1609 (func
1610 1610 ('symbol', '_intlist')
1611 1611 ('string', '0\x002\x001')
1612 1612 define)
1613 1613 (range
1614 1614 ('symbol', '2')
1615 1615 ('symbol', '0')
1616 1616 follow)
1617 1617 define)
1618 1618 * set:
1619 1619 <filteredset
1620 1620 <baseset [0, 2, 1]>,
1621 1621 <spanset- 0:2>>
1622 1622 0
1623 1623 2
1624 1624 1
1625 1625
1626 1626 '_hexlist(a b)' should behave like 'a + b':
1627 1627
1628 1628 $ trylist --optimize --bin '2:0 & %ln' `hg log -T '{node} ' -r0:2`
1629 1629 (and
1630 1630 (range
1631 1631 ('symbol', '2')
1632 1632 ('symbol', '0'))
1633 1633 (func
1634 1634 ('symbol', '_hexlist')
1635 1635 ('string', '*'))) (glob)
1636 1636 * optimized:
1637 1637 (and
1638 1638 (range
1639 1639 ('symbol', '2')
1640 1640 ('symbol', '0')
1641 1641 define)
1642 1642 (func
1643 1643 ('symbol', '_hexlist')
1644 1644 ('string', '*') (glob)
1645 1645 follow)
1646 1646 define)
1647 1647 * set:
1648 1648 <filteredset
1649 1649 <spanset- 0:2>,
1650 1650 <baseset [0, 1, 2]>>
1651 1651 2
1652 1652 1
1653 1653 0
1654 1654
1655 1655 $ trylist --optimize --bin '%ln & 2:0' `hg log -T '{node} ' -r0+2+1`
1656 1656 (and
1657 1657 (func
1658 1658 ('symbol', '_hexlist')
1659 1659 ('string', '*')) (glob)
1660 1660 (range
1661 1661 ('symbol', '2')
1662 1662 ('symbol', '0')))
1663 1663 * optimized:
1664 1664 (and
1665 1665 (range
1666 1666 ('symbol', '2')
1667 1667 ('symbol', '0')
1668 1668 follow)
1669 1669 (func
1670 1670 ('symbol', '_hexlist')
1671 1671 ('string', '*') (glob)
1672 1672 define)
1673 1673 define)
1674 1674 * set:
1675 1675 <baseset [0, 2, 1]>
1676 1676 0
1677 1677 2
1678 1678 1
1679 1679
1680 1680 '_list' should not go through the slow follow-order path if order doesn't
1681 1681 matter:
1682 1682
1683 1683 $ try -p optimized '2:0 & not (0 + 1)'
1684 1684 * optimized:
1685 1685 (difference
1686 1686 (range
1687 1687 ('symbol', '2')
1688 1688 ('symbol', '0')
1689 1689 define)
1690 1690 (func
1691 1691 ('symbol', '_list')
1692 1692 ('string', '0\x001')
1693 1693 any)
1694 1694 define)
1695 1695 * set:
1696 1696 <filteredset
1697 1697 <spanset- 0:2>,
1698 1698 <not
1699 1699 <baseset [0, 1]>>>
1700 1700 2
1701 1701
1702 1702 $ try -p optimized '2:0 & not (0:2 & (0 + 1))'
1703 1703 * optimized:
1704 1704 (difference
1705 1705 (range
1706 1706 ('symbol', '2')
1707 1707 ('symbol', '0')
1708 1708 define)
1709 1709 (and
1710 1710 (range
1711 1711 ('symbol', '0')
1712 1712 ('symbol', '2')
1713 1713 any)
1714 1714 (func
1715 1715 ('symbol', '_list')
1716 1716 ('string', '0\x001')
1717 1717 any)
1718 1718 any)
1719 1719 define)
1720 1720 * set:
1721 1721 <filteredset
1722 1722 <spanset- 0:2>,
1723 1723 <not
1724 1724 <baseset [0, 1]>>>
1725 1725 2
1726 1726
1727 1727 because 'present()' does nothing other than suppressing an error, the
1728 1728 ordering requirement should be forwarded to the nested expression
1729 1729
1730 1730 $ try -p optimized 'present(2 + 0 + 1)'
1731 1731 * optimized:
1732 1732 (func
1733 1733 ('symbol', 'present')
1734 1734 (func
1735 1735 ('symbol', '_list')
1736 1736 ('string', '2\x000\x001')
1737 1737 define)
1738 1738 define)
1739 1739 * set:
1740 1740 <baseset [2, 0, 1]>
1741 1741 2
1742 1742 0
1743 1743 1
1744 1744
1745 1745 $ try --optimize '2:0 & present(0 + 1 + 2)'
1746 1746 (and
1747 1747 (range
1748 1748 ('symbol', '2')
1749 1749 ('symbol', '0'))
1750 1750 (func
1751 1751 ('symbol', 'present')
1752 1752 (or
1753 1753 (list
1754 1754 ('symbol', '0')
1755 1755 ('symbol', '1')
1756 1756 ('symbol', '2')))))
1757 1757 * optimized:
1758 1758 (and
1759 1759 (range
1760 1760 ('symbol', '2')
1761 1761 ('symbol', '0')
1762 1762 define)
1763 1763 (func
1764 1764 ('symbol', 'present')
1765 1765 (func
1766 1766 ('symbol', '_list')
1767 1767 ('string', '0\x001\x002')
1768 1768 follow)
1769 1769 follow)
1770 1770 define)
1771 1771 * set:
1772 1772 <filteredset
1773 1773 <spanset- 0:2>,
1774 1774 <baseset [0, 1, 2]>>
1775 1775 2
1776 1776 1
1777 1777 0
1778 1778
1779 1779 'reverse()' should take effect only if it is the outermost expression:
1780 1780
1781 1781 $ try --optimize '0:2 & reverse(all())'
1782 1782 (and
1783 1783 (range
1784 1784 ('symbol', '0')
1785 1785 ('symbol', '2'))
1786 1786 (func
1787 1787 ('symbol', 'reverse')
1788 1788 (func
1789 1789 ('symbol', 'all')
1790 1790 None)))
1791 1791 * optimized:
1792 1792 (and
1793 1793 (range
1794 1794 ('symbol', '0')
1795 1795 ('symbol', '2')
1796 1796 define)
1797 1797 (func
1798 1798 ('symbol', 'reverse')
1799 1799 (func
1800 1800 ('symbol', 'all')
1801 1801 None
1802 1802 define)
1803 1803 follow)
1804 1804 define)
1805 1805 * set:
1806 1806 <filteredset
1807 1807 <spanset+ 0:2>,
1808 1808 <spanset+ 0:9>>
1809 1809 0
1810 1810 1
1811 1811 2
1812 1812
1813 1813 'sort()' should take effect only if it is the outermost expression:
1814 1814
1815 1815 $ try --optimize '0:2 & sort(all(), -rev)'
1816 1816 (and
1817 1817 (range
1818 1818 ('symbol', '0')
1819 1819 ('symbol', '2'))
1820 1820 (func
1821 1821 ('symbol', 'sort')
1822 1822 (list
1823 1823 (func
1824 1824 ('symbol', 'all')
1825 1825 None)
1826 1826 (negate
1827 1827 ('symbol', 'rev')))))
1828 1828 * optimized:
1829 1829 (and
1830 1830 (range
1831 1831 ('symbol', '0')
1832 1832 ('symbol', '2')
1833 1833 define)
1834 1834 (func
1835 1835 ('symbol', 'sort')
1836 1836 (list
1837 1837 (func
1838 1838 ('symbol', 'all')
1839 1839 None
1840 1840 define)
1841 1841 ('string', '-rev'))
1842 1842 follow)
1843 1843 define)
1844 1844 * set:
1845 1845 <filteredset
1846 1846 <spanset+ 0:2>,
1847 1847 <spanset+ 0:9>>
1848 1848 0
1849 1849 1
1850 1850 2
1851 1851
1852 1852 invalid argument passed to noop sort():
1853 1853
1854 1854 $ log '0:2 & sort()'
1855 1855 hg: parse error: sort requires one or two arguments
1856 1856 [255]
1857 1857 $ log '0:2 & sort(all(), -invalid)'
1858 1858 hg: parse error: unknown sort key '-invalid'
1859 1859 [255]
1860 1860
1861 1861 for 'A & f(B)', 'B' should not be affected by the order of 'A':
1862 1862
1863 1863 $ try --optimize '2:0 & first(1 + 0 + 2)'
1864 1864 (and
1865 1865 (range
1866 1866 ('symbol', '2')
1867 1867 ('symbol', '0'))
1868 1868 (func
1869 1869 ('symbol', 'first')
1870 1870 (or
1871 1871 (list
1872 1872 ('symbol', '1')
1873 1873 ('symbol', '0')
1874 1874 ('symbol', '2')))))
1875 1875 * optimized:
1876 1876 (and
1877 1877 (range
1878 1878 ('symbol', '2')
1879 1879 ('symbol', '0')
1880 1880 define)
1881 1881 (func
1882 1882 ('symbol', 'first')
1883 1883 (func
1884 1884 ('symbol', '_list')
1885 1885 ('string', '1\x000\x002')
1886 1886 define)
1887 1887 follow)
1888 1888 define)
1889 1889 * set:
1890 1890 <baseset
1891 1891 <limit n=1, offset=0,
1892 1892 <spanset- 0:2>,
1893 1893 <baseset [1, 0, 2]>>>
1894 1894 1
1895 1895
1896 1896 $ try --optimize '2:0 & not last(0 + 2 + 1)'
1897 1897 (and
1898 1898 (range
1899 1899 ('symbol', '2')
1900 1900 ('symbol', '0'))
1901 1901 (not
1902 1902 (func
1903 1903 ('symbol', 'last')
1904 1904 (or
1905 1905 (list
1906 1906 ('symbol', '0')
1907 1907 ('symbol', '2')
1908 1908 ('symbol', '1'))))))
1909 1909 * optimized:
1910 1910 (difference
1911 1911 (range
1912 1912 ('symbol', '2')
1913 1913 ('symbol', '0')
1914 1914 define)
1915 1915 (func
1916 1916 ('symbol', 'last')
1917 1917 (func
1918 1918 ('symbol', '_list')
1919 1919 ('string', '0\x002\x001')
1920 1920 define)
1921 1921 any)
1922 1922 define)
1923 1923 * set:
1924 1924 <filteredset
1925 1925 <spanset- 0:2>,
1926 1926 <not
1927 1927 <baseset
1928 1928 <last n=1,
1929 1929 <fullreposet+ 0:9>,
1930 1930 <baseset [1, 2, 0]>>>>>
1931 1931 2
1932 1932 0
1933 1933
1934 1934 for 'A & (op)(B)', 'B' should not be affected by the order of 'A':
1935 1935
1936 1936 $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)'
1937 1937 (and
1938 1938 (range
1939 1939 ('symbol', '2')
1940 1940 ('symbol', '0'))
1941 1941 (range
1942 1942 (group
1943 1943 (or
1944 1944 (list
1945 1945 ('symbol', '1')
1946 1946 ('symbol', '0')
1947 1947 ('symbol', '2'))))
1948 1948 (group
1949 1949 (or
1950 1950 (list
1951 1951 ('symbol', '0')
1952 1952 ('symbol', '2')
1953 1953 ('symbol', '1'))))))
1954 1954 * optimized:
1955 1955 (and
1956 1956 (range
1957 1957 ('symbol', '2')
1958 1958 ('symbol', '0')
1959 1959 define)
1960 1960 (range
1961 1961 (func
1962 1962 ('symbol', '_list')
1963 1963 ('string', '1\x000\x002')
1964 1964 define)
1965 1965 (func
1966 1966 ('symbol', '_list')
1967 1967 ('string', '0\x002\x001')
1968 1968 define)
1969 1969 follow)
1970 1970 define)
1971 1971 * set:
1972 1972 <filteredset
1973 1973 <spanset- 0:2>,
1974 1974 <baseset [1]>>
1975 1975 1
1976 1976
1977 1977 'A & B' can be rewritten as 'B & A' by weight, but that's fine as long as
1978 1978 the ordering rule is determined before the rewrite; in this example,
1979 1979 'B' follows the order of the initial set, which is the same order as 'A'
1980 1980 since 'A' also follows the order:
1981 1981
1982 1982 $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
1983 1983 (and
1984 1984 (func
1985 1985 ('symbol', 'contains')
1986 1986 ('string', 'glob:*'))
1987 1987 (group
1988 1988 (or
1989 1989 (list
1990 1990 ('symbol', '2')
1991 1991 ('symbol', '0')
1992 1992 ('symbol', '1')))))
1993 1993 * optimized:
1994 1994 (and
1995 1995 (func
1996 1996 ('symbol', '_list')
1997 1997 ('string', '2\x000\x001')
1998 1998 follow)
1999 1999 (func
2000 2000 ('symbol', 'contains')
2001 2001 ('string', 'glob:*')
2002 2002 define)
2003 2003 define)
2004 2004 * set:
2005 2005 <filteredset
2006 2006 <baseset+ [0, 1, 2]>,
2007 2007 <contains 'glob:*'>>
2008 2008 0
2009 2009 1
2010 2010 2
2011 2011
2012 2012 and in this example, 'A & B' is rewritten as 'B & A', but 'A' overrides
2013 2013 the order appropriately:
2014 2014
2015 2015 $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)'
2016 2016 (and
2017 2017 (func
2018 2018 ('symbol', 'reverse')
2019 2019 (func
2020 2020 ('symbol', 'contains')
2021 2021 ('string', 'glob:*')))
2022 2022 (group
2023 2023 (or
2024 2024 (list
2025 2025 ('symbol', '0')
2026 2026 ('symbol', '2')
2027 2027 ('symbol', '1')))))
2028 2028 * optimized:
2029 2029 (and
2030 2030 (func
2031 2031 ('symbol', '_list')
2032 2032 ('string', '0\x002\x001')
2033 2033 follow)
2034 2034 (func
2035 2035 ('symbol', 'reverse')
2036 2036 (func
2037 2037 ('symbol', 'contains')
2038 2038 ('string', 'glob:*')
2039 2039 define)
2040 2040 define)
2041 2041 define)
2042 2042 * set:
2043 2043 <filteredset
2044 2044 <baseset- [0, 1, 2]>,
2045 2045 <contains 'glob:*'>>
2046 2046 2
2047 2047 1
2048 2048 0
2049 2049
2050 2050 'A + B' can be rewritten to 'B + A' by weight only when the order doesn't
2051 2051 matter (e.g. 'X & (A + B)' can be 'X & (B + A)', but '(A + B) & X' can't):
2052 2052
2053 2053 $ try -p optimized '0:2 & (reverse(contains("a")) + 2)'
2054 2054 * optimized:
2055 2055 (and
2056 2056 (range
2057 2057 ('symbol', '0')
2058 2058 ('symbol', '2')
2059 2059 define)
2060 2060 (or
2061 2061 (list
2062 2062 ('symbol', '2')
2063 2063 (func
2064 2064 ('symbol', 'reverse')
2065 2065 (func
2066 2066 ('symbol', 'contains')
2067 2067 ('string', 'a')
2068 2068 define)
2069 2069 follow))
2070 2070 follow)
2071 2071 define)
2072 2072 * set:
2073 2073 <filteredset
2074 2074 <spanset+ 0:2>,
2075 2075 <addset
2076 2076 <baseset [2]>,
2077 2077 <filteredset
2078 2078 <fullreposet+ 0:9>,
2079 2079 <contains 'a'>>>>
2080 2080 0
2081 2081 1
2082 2082 2
2083 2083
2084 2084 $ try -p optimized '(reverse(contains("a")) + 2) & 0:2'
2085 2085 * optimized:
2086 2086 (and
2087 2087 (range
2088 2088 ('symbol', '0')
2089 2089 ('symbol', '2')
2090 2090 follow)
2091 2091 (or
2092 2092 (list
2093 2093 (func
2094 2094 ('symbol', 'reverse')
2095 2095 (func
2096 2096 ('symbol', 'contains')
2097 2097 ('string', 'a')
2098 2098 define)
2099 2099 define)
2100 2100 ('symbol', '2'))
2101 2101 define)
2102 2102 define)
2103 2103 * set:
2104 2104 <addset
2105 2105 <filteredset
2106 2106 <spanset- 0:2>,
2107 2107 <contains 'a'>>,
2108 2108 <baseset [2]>>
2109 2109 1
2110 2110 0
2111 2111 2
2112 2112
2113 2113 test sort revset
2114 2114 --------------------------------------------
2115 2115
2116 2116 test when adding two unordered revsets
2117 2117
2118 2118 $ log 'sort(keyword(issue) or modifies(b))'
2119 2119 4
2120 2120 6
2121 2121
2122 2122 test when sorting a reversed collection in the same way it is
2123 2123
2124 2124 $ log 'sort(reverse(all()), -rev)'
2125 2125 9
2126 2126 8
2127 2127 7
2128 2128 6
2129 2129 5
2130 2130 4
2131 2131 3
2132 2132 2
2133 2133 1
2134 2134 0
2135 2135
2136 2136 test when sorting a reversed collection
2137 2137
2138 2138 $ log 'sort(reverse(all()), rev)'
2139 2139 0
2140 2140 1
2141 2141 2
2142 2142 3
2143 2143 4
2144 2144 5
2145 2145 6
2146 2146 7
2147 2147 8
2148 2148 9
2149 2149
2150 2150
2151 2151 test sorting two sorted collections in different orders
2152 2152
2153 2153 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
2154 2154 2
2155 2155 6
2156 2156 8
2157 2157 9
2158 2158
2159 2159 test sorting two sorted collections in different orders backwards
2160 2160
2161 2161 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
2162 2162 9
2163 2163 8
2164 2164 6
2165 2165 2
2166 2166
2167 2167 test empty sort key which is noop
2168 2168
2169 2169 $ log 'sort(0 + 2 + 1, "")'
2170 2170 0
2171 2171 2
2172 2172 1
2173 2173
2174 2174 test invalid sort keys
2175 2175
2176 2176 $ log 'sort(all(), -invalid)'
2177 2177 hg: parse error: unknown sort key '-invalid'
2178 2178 [255]
2179 2179
2180 2180 $ cd ..
2181 2181
2182 2182 test sorting by multiple keys including variable-length strings
2183 2183
2184 2184 $ hg init sorting
2185 2185 $ cd sorting
2186 2186 $ cat <<EOF >> .hg/hgrc
2187 2187 > [ui]
2188 2188 > logtemplate = '{rev} {branch|p5}{desc|p5}{author|p5}{date|hgdate}\n'
2189 2189 > [templatealias]
2190 2190 > p5(s) = pad(s, 5)
2191 2191 > EOF
2192 2192 $ hg branch -qf b12
2193 2193 $ hg ci -m m111 -u u112 -d '111 10800'
2194 2194 $ hg branch -qf b11
2195 2195 $ hg ci -m m12 -u u111 -d '112 7200'
2196 2196 $ hg branch -qf b111
2197 2197 $ hg ci -m m11 -u u12 -d '111 3600'
2198 2198 $ hg branch -qf b112
2199 2199 $ hg ci -m m111 -u u11 -d '120 0'
2200 2200 $ hg branch -qf b111
2201 2201 $ hg ci -m m112 -u u111 -d '110 14400'
2202 2202 created new head
2203 2203
2204 2204 compare revisions (has fast path):
2205 2205
2206 2206 $ hg log -r 'sort(all(), rev)'
2207 2207 0 b12 m111 u112 111 10800
2208 2208 1 b11 m12 u111 112 7200
2209 2209 2 b111 m11 u12 111 3600
2210 2210 3 b112 m111 u11 120 0
2211 2211 4 b111 m112 u111 110 14400
2212 2212
2213 2213 $ hg log -r 'sort(all(), -rev)'
2214 2214 4 b111 m112 u111 110 14400
2215 2215 3 b112 m111 u11 120 0
2216 2216 2 b111 m11 u12 111 3600
2217 2217 1 b11 m12 u111 112 7200
2218 2218 0 b12 m111 u112 111 10800
2219 2219
2220 2220 compare variable-length strings (issue5218):
2221 2221
2222 2222 $ hg log -r 'sort(all(), branch)'
2223 2223 1 b11 m12 u111 112 7200
2224 2224 2 b111 m11 u12 111 3600
2225 2225 4 b111 m112 u111 110 14400
2226 2226 3 b112 m111 u11 120 0
2227 2227 0 b12 m111 u112 111 10800
2228 2228
2229 2229 $ hg log -r 'sort(all(), -branch)'
2230 2230 0 b12 m111 u112 111 10800
2231 2231 3 b112 m111 u11 120 0
2232 2232 2 b111 m11 u12 111 3600
2233 2233 4 b111 m112 u111 110 14400
2234 2234 1 b11 m12 u111 112 7200
2235 2235
2236 2236 $ hg log -r 'sort(all(), desc)'
2237 2237 2 b111 m11 u12 111 3600
2238 2238 0 b12 m111 u112 111 10800
2239 2239 3 b112 m111 u11 120 0
2240 2240 4 b111 m112 u111 110 14400
2241 2241 1 b11 m12 u111 112 7200
2242 2242
2243 2243 $ hg log -r 'sort(all(), -desc)'
2244 2244 1 b11 m12 u111 112 7200
2245 2245 4 b111 m112 u111 110 14400
2246 2246 0 b12 m111 u112 111 10800
2247 2247 3 b112 m111 u11 120 0
2248 2248 2 b111 m11 u12 111 3600
2249 2249
2250 2250 $ hg log -r 'sort(all(), user)'
2251 2251 3 b112 m111 u11 120 0
2252 2252 1 b11 m12 u111 112 7200
2253 2253 4 b111 m112 u111 110 14400
2254 2254 0 b12 m111 u112 111 10800
2255 2255 2 b111 m11 u12 111 3600
2256 2256
2257 2257 $ hg log -r 'sort(all(), -user)'
2258 2258 2 b111 m11 u12 111 3600
2259 2259 0 b12 m111 u112 111 10800
2260 2260 1 b11 m12 u111 112 7200
2261 2261 4 b111 m112 u111 110 14400
2262 2262 3 b112 m111 u11 120 0
2263 2263
2264 2264 compare dates (tz offset should have no effect):
2265 2265
2266 2266 $ hg log -r 'sort(all(), date)'
2267 2267 4 b111 m112 u111 110 14400
2268 2268 0 b12 m111 u112 111 10800
2269 2269 2 b111 m11 u12 111 3600
2270 2270 1 b11 m12 u111 112 7200
2271 2271 3 b112 m111 u11 120 0
2272 2272
2273 2273 $ hg log -r 'sort(all(), -date)'
2274 2274 3 b112 m111 u11 120 0
2275 2275 1 b11 m12 u111 112 7200
2276 2276 0 b12 m111 u112 111 10800
2277 2277 2 b111 m11 u12 111 3600
2278 2278 4 b111 m112 u111 110 14400
2279 2279
2280 2280 be aware that 'sort(x, -k)' is not exactly the same as 'reverse(sort(x, k))'
2281 2281 because '-k' reverses the comparison, not the list itself:
2282 2282
2283 2283 $ hg log -r 'sort(0 + 2, date)'
2284 2284 0 b12 m111 u112 111 10800
2285 2285 2 b111 m11 u12 111 3600
2286 2286
2287 2287 $ hg log -r 'sort(0 + 2, -date)'
2288 2288 0 b12 m111 u112 111 10800
2289 2289 2 b111 m11 u12 111 3600
2290 2290
2291 2291 $ hg log -r 'reverse(sort(0 + 2, date))'
2292 2292 2 b111 m11 u12 111 3600
2293 2293 0 b12 m111 u112 111 10800
2294 2294
2295 2295 sort by multiple keys:
2296 2296
2297 2297 $ hg log -r 'sort(all(), "branch -rev")'
2298 2298 1 b11 m12 u111 112 7200
2299 2299 4 b111 m112 u111 110 14400
2300 2300 2 b111 m11 u12 111 3600
2301 2301 3 b112 m111 u11 120 0
2302 2302 0 b12 m111 u112 111 10800
2303 2303
2304 2304 $ hg log -r 'sort(all(), "-desc -date")'
2305 2305 1 b11 m12 u111 112 7200
2306 2306 4 b111 m112 u111 110 14400
2307 2307 3 b112 m111 u11 120 0
2308 2308 0 b12 m111 u112 111 10800
2309 2309 2 b111 m11 u12 111 3600
2310 2310
2311 2311 $ hg log -r 'sort(all(), "user -branch date rev")'
2312 2312 3 b112 m111 u11 120 0
2313 2313 4 b111 m112 u111 110 14400
2314 2314 1 b11 m12 u111 112 7200
2315 2315 0 b12 m111 u112 111 10800
2316 2316 2 b111 m11 u12 111 3600
2317 2317
2318 2318 toposort prioritises graph branches
2319 2319
2320 2320 $ hg up 2
2321 2321 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2322 2322 $ touch a
2323 2323 $ hg addremove
2324 2324 adding a
2325 2325 $ hg ci -m 't1' -u 'tu' -d '130 0'
2326 2326 created new head
2327 2327 $ echo 'a' >> a
2328 2328 $ hg ci -m 't2' -u 'tu' -d '130 0'
2329 2329 $ hg book book1
2330 2330 $ hg up 4
2331 2331 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2332 2332 (leaving bookmark book1)
2333 2333 $ touch a
2334 2334 $ hg addremove
2335 2335 adding a
2336 2336 $ hg ci -m 't3' -u 'tu' -d '130 0'
2337 2337
2338 2338 $ hg log -r 'sort(all(), topo)'
2339 2339 7 b111 t3 tu 130 0
2340 2340 4 b111 m112 u111 110 14400
2341 2341 3 b112 m111 u11 120 0
2342 2342 6 b111 t2 tu 130 0
2343 2343 5 b111 t1 tu 130 0
2344 2344 2 b111 m11 u12 111 3600
2345 2345 1 b11 m12 u111 112 7200
2346 2346 0 b12 m111 u112 111 10800
2347 2347
2348 2348 $ hg log -r 'sort(all(), -topo)'
2349 2349 0 b12 m111 u112 111 10800
2350 2350 1 b11 m12 u111 112 7200
2351 2351 2 b111 m11 u12 111 3600
2352 2352 5 b111 t1 tu 130 0
2353 2353 6 b111 t2 tu 130 0
2354 2354 3 b112 m111 u11 120 0
2355 2355 4 b111 m112 u111 110 14400
2356 2356 7 b111 t3 tu 130 0
2357 2357
2358 2358 $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)'
2359 2359 6 b111 t2 tu 130 0
2360 2360 5 b111 t1 tu 130 0
2361 2361 7 b111 t3 tu 130 0
2362 2362 4 b111 m112 u111 110 14400
2363 2363 3 b112 m111 u11 120 0
2364 2364 2 b111 m11 u12 111 3600
2365 2365 1 b11 m12 u111 112 7200
2366 2366 0 b12 m111 u112 111 10800
2367 2367
2368 2368 topographical sorting can't be combined with other sort keys, and you can't
2369 2369 use the topo.firstbranch option when topo sort is not active:
2370 2370
2371 2371 $ hg log -r 'sort(all(), "topo user")'
2372 2372 hg: parse error: topo sort order cannot be combined with other sort keys
2373 2373 [255]
2374 2374
2375 2375 $ hg log -r 'sort(all(), user, topo.firstbranch=book1)'
2376 2376 hg: parse error: topo.firstbranch can only be used when using the topo sort key
2377 2377 [255]
2378 2378
2379 2379 topo.firstbranch should accept any kind of expressions:
2380 2380
2381 2381 $ hg log -r 'sort(0, topo, topo.firstbranch=(book1))'
2382 2382 0 b12 m111 u112 111 10800
2383 2383
2384 2384 $ cd ..
2385 2385 $ cd repo
2386 2386
2387 2387 test subtracting something from an addset
2388 2388
2389 2389 $ log '(outgoing() or removes(a)) - removes(a)'
2390 2390 8
2391 2391 9
2392 2392
2393 2393 test intersecting something with an addset
2394 2394
2395 2395 $ log 'parents(outgoing() or removes(a))'
2396 2396 1
2397 2397 4
2398 2398 5
2399 2399 8
2400 2400
2401 2401 test that `or` operation combines elements in the right order:
2402 2402
2403 2403 $ log '3:4 or 2:5'
2404 2404 3
2405 2405 4
2406 2406 2
2407 2407 5
2408 2408 $ log '3:4 or 5:2'
2409 2409 3
2410 2410 4
2411 2411 5
2412 2412 2
2413 2413 $ log 'sort(3:4 or 2:5)'
2414 2414 2
2415 2415 3
2416 2416 4
2417 2417 5
2418 2418 $ log 'sort(3:4 or 5:2)'
2419 2419 2
2420 2420 3
2421 2421 4
2422 2422 5
2423 2423
2424 2424 test that more than one `-r`s are combined in the right order and deduplicated:
2425 2425
2426 2426 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
2427 2427 3
2428 2428 4
2429 2429 5
2430 2430 2
2431 2431 0
2432 2432 1
2433 2433
2434 2434 test that `or` operation skips duplicated revisions from right-hand side
2435 2435
2436 2436 $ try 'reverse(1::5) or ancestors(4)'
2437 2437 (or
2438 2438 (list
2439 2439 (func
2440 2440 ('symbol', 'reverse')
2441 2441 (dagrange
2442 2442 ('symbol', '1')
2443 2443 ('symbol', '5')))
2444 2444 (func
2445 2445 ('symbol', 'ancestors')
2446 2446 ('symbol', '4'))))
2447 2447 * set:
2448 2448 <addset
2449 2449 <baseset- [1, 3, 5]>,
2450 2450 <generatorset+>>
2451 2451 5
2452 2452 3
2453 2453 1
2454 2454 0
2455 2455 2
2456 2456 4
2457 2457 $ try 'sort(ancestors(4) or reverse(1::5))'
2458 2458 (func
2459 2459 ('symbol', 'sort')
2460 2460 (or
2461 2461 (list
2462 2462 (func
2463 2463 ('symbol', 'ancestors')
2464 2464 ('symbol', '4'))
2465 2465 (func
2466 2466 ('symbol', 'reverse')
2467 2467 (dagrange
2468 2468 ('symbol', '1')
2469 2469 ('symbol', '5'))))))
2470 2470 * set:
2471 2471 <addset+
2472 2472 <generatorset+>,
2473 2473 <baseset- [1, 3, 5]>>
2474 2474 0
2475 2475 1
2476 2476 2
2477 2477 3
2478 2478 4
2479 2479 5
2480 2480
2481 2481 test optimization of trivial `or` operation
2482 2482
2483 2483 $ try --optimize '0|(1)|"2"|-2|tip|null'
2484 2484 (or
2485 2485 (list
2486 2486 ('symbol', '0')
2487 2487 (group
2488 2488 ('symbol', '1'))
2489 2489 ('string', '2')
2490 2490 (negate
2491 2491 ('symbol', '2'))
2492 2492 ('symbol', 'tip')
2493 2493 ('symbol', 'null')))
2494 2494 * optimized:
2495 2495 (func
2496 2496 ('symbol', '_list')
2497 2497 ('string', '0\x001\x002\x00-2\x00tip\x00null')
2498 2498 define)
2499 2499 * set:
2500 2500 <baseset [0, 1, 2, 8, 9, -1]>
2501 2501 0
2502 2502 1
2503 2503 2
2504 2504 8
2505 2505 9
2506 2506 -1
2507 2507
2508 2508 $ try --optimize '0|1|2:3'
2509 2509 (or
2510 2510 (list
2511 2511 ('symbol', '0')
2512 2512 ('symbol', '1')
2513 2513 (range
2514 2514 ('symbol', '2')
2515 2515 ('symbol', '3'))))
2516 2516 * optimized:
2517 2517 (or
2518 2518 (list
2519 2519 (func
2520 2520 ('symbol', '_list')
2521 2521 ('string', '0\x001')
2522 2522 define)
2523 2523 (range
2524 2524 ('symbol', '2')
2525 2525 ('symbol', '3')
2526 2526 define))
2527 2527 define)
2528 2528 * set:
2529 2529 <addset
2530 2530 <baseset [0, 1]>,
2531 2531 <spanset+ 2:3>>
2532 2532 0
2533 2533 1
2534 2534 2
2535 2535 3
2536 2536
2537 2537 $ try --optimize '0:1|2|3:4|5|6'
2538 2538 (or
2539 2539 (list
2540 2540 (range
2541 2541 ('symbol', '0')
2542 2542 ('symbol', '1'))
2543 2543 ('symbol', '2')
2544 2544 (range
2545 2545 ('symbol', '3')
2546 2546 ('symbol', '4'))
2547 2547 ('symbol', '5')
2548 2548 ('symbol', '6')))
2549 2549 * optimized:
2550 2550 (or
2551 2551 (list
2552 2552 (range
2553 2553 ('symbol', '0')
2554 2554 ('symbol', '1')
2555 2555 define)
2556 2556 ('symbol', '2')
2557 2557 (range
2558 2558 ('symbol', '3')
2559 2559 ('symbol', '4')
2560 2560 define)
2561 2561 (func
2562 2562 ('symbol', '_list')
2563 2563 ('string', '5\x006')
2564 2564 define))
2565 2565 define)
2566 2566 * set:
2567 2567 <addset
2568 2568 <addset
2569 2569 <spanset+ 0:1>,
2570 2570 <baseset [2]>>,
2571 2571 <addset
2572 2572 <spanset+ 3:4>,
2573 2573 <baseset [5, 6]>>>
2574 2574 0
2575 2575 1
2576 2576 2
2577 2577 3
2578 2578 4
2579 2579 5
2580 2580 6
2581 2581
2582 2582 unoptimized `or` looks like this
2583 2583
2584 2584 $ try --no-optimized -p analyzed '0|1|2|3|4'
2585 2585 * analyzed:
2586 2586 (or
2587 2587 (list
2588 2588 ('symbol', '0')
2589 2589 ('symbol', '1')
2590 2590 ('symbol', '2')
2591 2591 ('symbol', '3')
2592 2592 ('symbol', '4'))
2593 2593 define)
2594 2594 * set:
2595 2595 <addset
2596 2596 <addset
2597 2597 <baseset [0]>,
2598 2598 <baseset [1]>>,
2599 2599 <addset
2600 2600 <baseset [2]>,
2601 2601 <addset
2602 2602 <baseset [3]>,
2603 2603 <baseset [4]>>>>
2604 2604 0
2605 2605 1
2606 2606 2
2607 2607 3
2608 2608 4
2609 2609
2610 2610 test that `_list` should be narrowed by provided `subset`
2611 2611
2612 2612 $ log '0:2 and (null|1|2|3)'
2613 2613 1
2614 2614 2
2615 2615
2616 2616 test that `_list` should remove duplicates
2617 2617
2618 2618 $ log '0|1|2|1|2|-1|tip'
2619 2619 0
2620 2620 1
2621 2621 2
2622 2622 9
2623 2623
2624 2624 test unknown revision in `_list`
2625 2625
2626 2626 $ log '0|unknown'
2627 2627 abort: unknown revision 'unknown'!
2628 2628 [255]
2629 2629
2630 2630 test integer range in `_list`
2631 2631
2632 2632 $ log '-1|-10'
2633 2633 9
2634 2634 0
2635 2635
2636 2636 $ log '-10|-11'
2637 2637 abort: unknown revision '-11'!
2638 2638 [255]
2639 2639
2640 2640 $ log '9|10'
2641 2641 abort: unknown revision '10'!
2642 2642 [255]
2643 2643
2644 2644 test '0000' != '0' in `_list`
2645 2645
2646 2646 $ log '0|0000'
2647 2647 0
2648 2648 -1
2649 2649
2650 2650 test ',' in `_list`
2651 2651 $ log '0,1'
2652 2652 hg: parse error: can't use a list in this context
2653 2653 (see hg help "revsets.x or y")
2654 2654 [255]
2655 2655 $ try '0,1,2'
2656 2656 (list
2657 2657 ('symbol', '0')
2658 2658 ('symbol', '1')
2659 2659 ('symbol', '2'))
2660 2660 hg: parse error: can't use a list in this context
2661 2661 (see hg help "revsets.x or y")
2662 2662 [255]
2663 2663
2664 2664 test that chained `or` operations make balanced addsets
2665 2665
2666 2666 $ try '0:1|1:2|2:3|3:4|4:5'
2667 2667 (or
2668 2668 (list
2669 2669 (range
2670 2670 ('symbol', '0')
2671 2671 ('symbol', '1'))
2672 2672 (range
2673 2673 ('symbol', '1')
2674 2674 ('symbol', '2'))
2675 2675 (range
2676 2676 ('symbol', '2')
2677 2677 ('symbol', '3'))
2678 2678 (range
2679 2679 ('symbol', '3')
2680 2680 ('symbol', '4'))
2681 2681 (range
2682 2682 ('symbol', '4')
2683 2683 ('symbol', '5'))))
2684 2684 * set:
2685 2685 <addset
2686 2686 <addset
2687 2687 <spanset+ 0:1>,
2688 2688 <spanset+ 1:2>>,
2689 2689 <addset
2690 2690 <spanset+ 2:3>,
2691 2691 <addset
2692 2692 <spanset+ 3:4>,
2693 2693 <spanset+ 4:5>>>>
2694 2694 0
2695 2695 1
2696 2696 2
2697 2697 3
2698 2698 4
2699 2699 5
2700 2700
2701 2701 no crash by empty group "()" while optimizing `or` operations
2702 2702
2703 2703 $ try --optimize '0|()'
2704 2704 (or
2705 2705 (list
2706 2706 ('symbol', '0')
2707 2707 (group
2708 2708 None)))
2709 2709 * optimized:
2710 2710 (or
2711 2711 (list
2712 2712 ('symbol', '0')
2713 2713 None)
2714 2714 define)
2715 2715 hg: parse error: missing argument
2716 2716 [255]
2717 2717
2718 2718 test that chained `or` operations never eat up stack (issue4624)
2719 2719 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
2720 2720
2721 2721 $ hg log -T '{rev}\n' -r `python -c "print '+'.join(['0:1'] * 500)"`
2722 2722 0
2723 2723 1
2724 2724
2725 2725 test that repeated `-r` options never eat up stack (issue4565)
2726 2726 (uses `-r 0::1` to avoid possible optimization at old-style parser)
2727 2727
2728 2728 $ hg log -T '{rev}\n' `python -c "for i in xrange(500): print '-r 0::1 ',"`
2729 2729 0
2730 2730 1
2731 2731
2732 2732 check that conversion to only works
2733 2733 $ try --optimize '::3 - ::1'
2734 2734 (minus
2735 2735 (dagrangepre
2736 2736 ('symbol', '3'))
2737 2737 (dagrangepre
2738 2738 ('symbol', '1')))
2739 2739 * optimized:
2740 2740 (func
2741 2741 ('symbol', 'only')
2742 2742 (list
2743 2743 ('symbol', '3')
2744 2744 ('symbol', '1'))
2745 2745 define)
2746 2746 * set:
2747 2747 <baseset+ [3]>
2748 2748 3
2749 2749 $ try --optimize 'ancestors(1) - ancestors(3)'
2750 2750 (minus
2751 2751 (func
2752 2752 ('symbol', 'ancestors')
2753 2753 ('symbol', '1'))
2754 2754 (func
2755 2755 ('symbol', 'ancestors')
2756 2756 ('symbol', '3')))
2757 2757 * optimized:
2758 2758 (func
2759 2759 ('symbol', 'only')
2760 2760 (list
2761 2761 ('symbol', '1')
2762 2762 ('symbol', '3'))
2763 2763 define)
2764 2764 * set:
2765 2765 <baseset+ []>
2766 2766 $ try --optimize 'not ::2 and ::6'
2767 2767 (and
2768 2768 (not
2769 2769 (dagrangepre
2770 2770 ('symbol', '2')))
2771 2771 (dagrangepre
2772 2772 ('symbol', '6')))
2773 2773 * optimized:
2774 2774 (func
2775 2775 ('symbol', 'only')
2776 2776 (list
2777 2777 ('symbol', '6')
2778 2778 ('symbol', '2'))
2779 2779 define)
2780 2780 * set:
2781 2781 <baseset+ [3, 4, 5, 6]>
2782 2782 3
2783 2783 4
2784 2784 5
2785 2785 6
2786 2786 $ try --optimize 'ancestors(6) and not ancestors(4)'
2787 2787 (and
2788 2788 (func
2789 2789 ('symbol', 'ancestors')
2790 2790 ('symbol', '6'))
2791 2791 (not
2792 2792 (func
2793 2793 ('symbol', 'ancestors')
2794 2794 ('symbol', '4'))))
2795 2795 * optimized:
2796 2796 (func
2797 2797 ('symbol', 'only')
2798 2798 (list
2799 2799 ('symbol', '6')
2800 2800 ('symbol', '4'))
2801 2801 define)
2802 2802 * set:
2803 2803 <baseset+ [3, 5, 6]>
2804 2804 3
2805 2805 5
2806 2806 6
2807 2807
2808 2808 no crash by empty group "()" while optimizing to "only()"
2809 2809
2810 2810 $ try --optimize '::1 and ()'
2811 2811 (and
2812 2812 (dagrangepre
2813 2813 ('symbol', '1'))
2814 2814 (group
2815 2815 None))
2816 2816 * optimized:
2817 2817 (and
2818 2818 None
2819 2819 (func
2820 2820 ('symbol', 'ancestors')
2821 2821 ('symbol', '1')
2822 2822 define)
2823 2823 define)
2824 2824 hg: parse error: missing argument
2825 2825 [255]
2826 2826
2827 2827 invalid function call should not be optimized to only()
2828 2828
2829 2829 $ log '"ancestors"(6) and not ancestors(4)'
2830 2830 hg: parse error: not a symbol
2831 2831 [255]
2832 2832
2833 2833 $ log 'ancestors(6) and not "ancestors"(4)'
2834 2834 hg: parse error: not a symbol
2835 2835 [255]
2836 2836
2837 2837 we can use patterns when searching for tags
2838 2838
2839 2839 $ log 'tag("1..*")'
2840 2840 abort: tag '1..*' does not exist!
2841 2841 [255]
2842 2842 $ log 'tag("re:1..*")'
2843 2843 6
2844 2844 $ log 'tag("re:[0-9].[0-9]")'
2845 2845 6
2846 2846 $ log 'tag("literal:1.0")'
2847 2847 6
2848 2848 $ log 'tag("re:0..*")'
2849 2849
2850 2850 $ log 'tag(unknown)'
2851 2851 abort: tag 'unknown' does not exist!
2852 2852 [255]
2853 2853 $ log 'tag("re:unknown")'
2854 2854 $ log 'present(tag("unknown"))'
2855 2855 $ log 'present(tag("re:unknown"))'
2856 2856 $ log 'branch(unknown)'
2857 2857 abort: unknown revision 'unknown'!
2858 2858 [255]
2859 2859 $ log 'branch("literal:unknown")'
2860 2860 abort: branch 'unknown' does not exist!
2861 2861 [255]
2862 2862 $ log 'branch("re:unknown")'
2863 2863 $ log 'present(branch("unknown"))'
2864 2864 $ log 'present(branch("re:unknown"))'
2865 2865 $ log 'user(bob)'
2866 2866 2
2867 2867
2868 2868 $ log '4::8'
2869 2869 4
2870 2870 8
2871 2871 $ log '4:8'
2872 2872 4
2873 2873 5
2874 2874 6
2875 2875 7
2876 2876 8
2877 2877
2878 2878 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
2879 2879 4
2880 2880 2
2881 2881 5
2882 2882
2883 2883 $ log 'not 0 and 0:2'
2884 2884 1
2885 2885 2
2886 2886 $ log 'not 1 and 0:2'
2887 2887 0
2888 2888 2
2889 2889 $ log 'not 2 and 0:2'
2890 2890 0
2891 2891 1
2892 2892 $ log '(1 and 2)::'
2893 2893 $ log '(1 and 2):'
2894 2894 $ log '(1 and 2):3'
2895 2895 $ log 'sort(head(), -rev)'
2896 2896 9
2897 2897 7
2898 2898 6
2899 2899 5
2900 2900 4
2901 2901 3
2902 2902 2
2903 2903 1
2904 2904 0
2905 2905 $ log '4::8 - 8'
2906 2906 4
2907 2907
2908 2908 matching() should preserve the order of the input set:
2909 2909
2910 2910 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
2911 2911 2
2912 2912 3
2913 2913 1
2914 2914
2915 2915 $ log 'named("unknown")'
2916 2916 abort: namespace 'unknown' does not exist!
2917 2917 [255]
2918 2918 $ log 'named("re:unknown")'
2919 2919 abort: no namespace exists that match 'unknown'!
2920 2920 [255]
2921 2921 $ log 'present(named("unknown"))'
2922 2922 $ log 'present(named("re:unknown"))'
2923 2923
2924 2924 $ log 'tag()'
2925 2925 6
2926 2926 $ log 'named("tags")'
2927 2927 6
2928 2928
2929 2929 issue2437
2930 2930
2931 2931 $ log '3 and p1(5)'
2932 2932 3
2933 2933 $ log '4 and p2(6)'
2934 2934 4
2935 2935 $ log '1 and parents(:2)'
2936 2936 1
2937 2937 $ log '2 and children(1:)'
2938 2938 2
2939 2939 $ log 'roots(all()) or roots(all())'
2940 2940 0
2941 2941 $ hg debugrevspec 'roots(all()) or roots(all())'
2942 2942 0
2943 2943 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
2944 2944 9
2945 2945 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
2946 2946 4
2947 2947
2948 2948 issue2654: report a parse error if the revset was not completely parsed
2949 2949
2950 2950 $ log '1 OR 2'
2951 2951 hg: parse error at 2: invalid token
2952 2952 [255]
2953 2953
2954 2954 or operator should preserve ordering:
2955 2955 $ log 'reverse(2::4) or tip'
2956 2956 4
2957 2957 2
2958 2958 9
2959 2959
2960 2960 parentrevspec
2961 2961
2962 2962 $ log 'merge()^0'
2963 2963 6
2964 2964 $ log 'merge()^'
2965 2965 5
2966 2966 $ log 'merge()^1'
2967 2967 5
2968 2968 $ log 'merge()^2'
2969 2969 4
2970 2970 $ log '(not merge())^2'
2971 2971 $ log 'merge()^^'
2972 2972 3
2973 2973 $ log 'merge()^1^'
2974 2974 3
2975 2975 $ log 'merge()^^^'
2976 2976 1
2977 2977
2978 $ log '(merge() | 0)~-1'
2979 7
2980 1
2981 $ log 'merge()~-1'
2982 7
2983 $ log 'tip~-1'
2984 $ log '(tip | merge())~-1'
2985 7
2978 2986 $ log 'merge()~0'
2979 2987 6
2980 2988 $ log 'merge()~1'
2981 2989 5
2982 2990 $ log 'merge()~2'
2983 2991 3
2984 2992 $ log 'merge()~2^1'
2985 2993 1
2986 2994 $ log 'merge()~3'
2987 2995 1
2988 2996
2989 2997 $ log '(-3:tip)^'
2990 2998 4
2991 2999 6
2992 3000 8
2993 3001
2994 3002 $ log 'tip^foo'
2995 3003 hg: parse error: ^ expects a number 0, 1, or 2
2996 3004 [255]
2997 3005
3006 $ log 'branchpoint()~-1'
3007 abort: revision in set has more than one child!
3008 [255]
3009
2998 3010 Bogus function gets suggestions
2999 3011 $ log 'add()'
3000 3012 hg: parse error: unknown identifier: add
3001 3013 (did you mean adds?)
3002 3014 [255]
3003 3015 $ log 'added()'
3004 3016 hg: parse error: unknown identifier: added
3005 3017 (did you mean adds?)
3006 3018 [255]
3007 3019 $ log 'remo()'
3008 3020 hg: parse error: unknown identifier: remo
3009 3021 (did you mean one of remote, removes?)
3010 3022 [255]
3011 3023 $ log 'babar()'
3012 3024 hg: parse error: unknown identifier: babar
3013 3025 [255]
3014 3026
3015 3027 Bogus function with a similar internal name doesn't suggest the internal name
3016 3028 $ log 'matches()'
3017 3029 hg: parse error: unknown identifier: matches
3018 3030 (did you mean matching?)
3019 3031 [255]
3020 3032
3021 3033 Undocumented functions aren't suggested as similar either
3022 3034 $ log 'tagged2()'
3023 3035 hg: parse error: unknown identifier: tagged2
3024 3036 [255]
3025 3037
3026 3038 multiple revspecs
3027 3039
3028 3040 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
3029 3041 8
3030 3042 9
3031 3043 4
3032 3044 5
3033 3045 6
3034 3046 7
3035 3047
3036 3048 test usage in revpair (with "+")
3037 3049
3038 3050 (real pair)
3039 3051
3040 3052 $ hg diff -r 'tip^^' -r 'tip'
3041 3053 diff -r 2326846efdab -r 24286f4ae135 .hgtags
3042 3054 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3043 3055 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
3044 3056 @@ -0,0 +1,1 @@
3045 3057 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3046 3058 $ hg diff -r 'tip^^::tip'
3047 3059 diff -r 2326846efdab -r 24286f4ae135 .hgtags
3048 3060 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3049 3061 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
3050 3062 @@ -0,0 +1,1 @@
3051 3063 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3052 3064
3053 3065 (single rev)
3054 3066
3055 3067 $ hg diff -r 'tip^' -r 'tip^'
3056 3068 $ hg diff -r 'tip^:tip^'
3057 3069
3058 3070 (single rev that does not looks like a range)
3059 3071
3060 3072 $ hg diff -r 'tip^::tip^ or tip^'
3061 3073 diff -r d5d0dcbdc4d9 .hgtags
3062 3074 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3063 3075 +++ b/.hgtags * (glob)
3064 3076 @@ -0,0 +1,1 @@
3065 3077 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3066 3078 $ hg diff -r 'tip^ or tip^'
3067 3079 diff -r d5d0dcbdc4d9 .hgtags
3068 3080 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3069 3081 +++ b/.hgtags * (glob)
3070 3082 @@ -0,0 +1,1 @@
3071 3083 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3072 3084
3073 3085 (no rev)
3074 3086
3075 3087 $ hg diff -r 'author("babar") or author("celeste")'
3076 3088 abort: empty revision range
3077 3089 [255]
3078 3090
3079 3091 aliases:
3080 3092
3081 3093 $ echo '[revsetalias]' >> .hg/hgrc
3082 3094 $ echo 'm = merge()' >> .hg/hgrc
3083 3095 (revset aliases can override builtin revsets)
3084 3096 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
3085 3097 $ echo 'sincem = descendants(m)' >> .hg/hgrc
3086 3098 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
3087 3099 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
3088 3100 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
3089 3101
3090 3102 $ try m
3091 3103 ('symbol', 'm')
3092 3104 * expanded:
3093 3105 (func
3094 3106 ('symbol', 'merge')
3095 3107 None)
3096 3108 * set:
3097 3109 <filteredset
3098 3110 <fullreposet+ 0:9>,
3099 3111 <merge>>
3100 3112 6
3101 3113
3102 3114 $ HGPLAIN=1
3103 3115 $ export HGPLAIN
3104 3116 $ try m
3105 3117 ('symbol', 'm')
3106 3118 abort: unknown revision 'm'!
3107 3119 [255]
3108 3120
3109 3121 $ HGPLAINEXCEPT=revsetalias
3110 3122 $ export HGPLAINEXCEPT
3111 3123 $ try m
3112 3124 ('symbol', 'm')
3113 3125 * expanded:
3114 3126 (func
3115 3127 ('symbol', 'merge')
3116 3128 None)
3117 3129 * set:
3118 3130 <filteredset
3119 3131 <fullreposet+ 0:9>,
3120 3132 <merge>>
3121 3133 6
3122 3134
3123 3135 $ unset HGPLAIN
3124 3136 $ unset HGPLAINEXCEPT
3125 3137
3126 3138 $ try 'p2(.)'
3127 3139 (func
3128 3140 ('symbol', 'p2')
3129 3141 ('symbol', '.'))
3130 3142 * expanded:
3131 3143 (func
3132 3144 ('symbol', 'p1')
3133 3145 ('symbol', '.'))
3134 3146 * set:
3135 3147 <baseset+ [8]>
3136 3148 8
3137 3149
3138 3150 $ HGPLAIN=1
3139 3151 $ export HGPLAIN
3140 3152 $ try 'p2(.)'
3141 3153 (func
3142 3154 ('symbol', 'p2')
3143 3155 ('symbol', '.'))
3144 3156 * set:
3145 3157 <baseset+ []>
3146 3158
3147 3159 $ HGPLAINEXCEPT=revsetalias
3148 3160 $ export HGPLAINEXCEPT
3149 3161 $ try 'p2(.)'
3150 3162 (func
3151 3163 ('symbol', 'p2')
3152 3164 ('symbol', '.'))
3153 3165 * expanded:
3154 3166 (func
3155 3167 ('symbol', 'p1')
3156 3168 ('symbol', '.'))
3157 3169 * set:
3158 3170 <baseset+ [8]>
3159 3171 8
3160 3172
3161 3173 $ unset HGPLAIN
3162 3174 $ unset HGPLAINEXCEPT
3163 3175
3164 3176 test alias recursion
3165 3177
3166 3178 $ try sincem
3167 3179 ('symbol', 'sincem')
3168 3180 * expanded:
3169 3181 (func
3170 3182 ('symbol', 'descendants')
3171 3183 (func
3172 3184 ('symbol', 'merge')
3173 3185 None))
3174 3186 * set:
3175 3187 <addset+
3176 3188 <filteredset
3177 3189 <fullreposet+ 0:9>,
3178 3190 <merge>>,
3179 3191 <generatorset+>>
3180 3192 6
3181 3193 7
3182 3194
3183 3195 test infinite recursion
3184 3196
3185 3197 $ echo 'recurse1 = recurse2' >> .hg/hgrc
3186 3198 $ echo 'recurse2 = recurse1' >> .hg/hgrc
3187 3199 $ try recurse1
3188 3200 ('symbol', 'recurse1')
3189 3201 hg: parse error: infinite expansion of revset alias "recurse1" detected
3190 3202 [255]
3191 3203
3192 3204 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
3193 3205 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
3194 3206 $ try "level2(level1(1, 2), 3)"
3195 3207 (func
3196 3208 ('symbol', 'level2')
3197 3209 (list
3198 3210 (func
3199 3211 ('symbol', 'level1')
3200 3212 (list
3201 3213 ('symbol', '1')
3202 3214 ('symbol', '2')))
3203 3215 ('symbol', '3')))
3204 3216 * expanded:
3205 3217 (or
3206 3218 (list
3207 3219 ('symbol', '3')
3208 3220 (or
3209 3221 (list
3210 3222 ('symbol', '1')
3211 3223 ('symbol', '2')))))
3212 3224 * set:
3213 3225 <addset
3214 3226 <baseset [3]>,
3215 3227 <baseset [1, 2]>>
3216 3228 3
3217 3229 1
3218 3230 2
3219 3231
3220 3232 test nesting and variable passing
3221 3233
3222 3234 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
3223 3235 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
3224 3236 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
3225 3237 $ try 'nested(2:5)'
3226 3238 (func
3227 3239 ('symbol', 'nested')
3228 3240 (range
3229 3241 ('symbol', '2')
3230 3242 ('symbol', '5')))
3231 3243 * expanded:
3232 3244 (func
3233 3245 ('symbol', 'max')
3234 3246 (range
3235 3247 ('symbol', '2')
3236 3248 ('symbol', '5')))
3237 3249 * set:
3238 3250 <baseset
3239 3251 <max
3240 3252 <fullreposet+ 0:9>,
3241 3253 <spanset+ 2:5>>>
3242 3254 5
3243 3255
3244 3256 test chained `or` operations are flattened at parsing phase
3245 3257
3246 3258 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
3247 3259 $ try 'chainedorops(0:1, 1:2, 2:3)'
3248 3260 (func
3249 3261 ('symbol', 'chainedorops')
3250 3262 (list
3251 3263 (range
3252 3264 ('symbol', '0')
3253 3265 ('symbol', '1'))
3254 3266 (range
3255 3267 ('symbol', '1')
3256 3268 ('symbol', '2'))
3257 3269 (range
3258 3270 ('symbol', '2')
3259 3271 ('symbol', '3'))))
3260 3272 * expanded:
3261 3273 (or
3262 3274 (list
3263 3275 (range
3264 3276 ('symbol', '0')
3265 3277 ('symbol', '1'))
3266 3278 (range
3267 3279 ('symbol', '1')
3268 3280 ('symbol', '2'))
3269 3281 (range
3270 3282 ('symbol', '2')
3271 3283 ('symbol', '3'))))
3272 3284 * set:
3273 3285 <addset
3274 3286 <spanset+ 0:1>,
3275 3287 <addset
3276 3288 <spanset+ 1:2>,
3277 3289 <spanset+ 2:3>>>
3278 3290 0
3279 3291 1
3280 3292 2
3281 3293 3
3282 3294
3283 3295 test variable isolation, variable placeholders are rewritten as string
3284 3296 then parsed and matched again as string. Check they do not leak too
3285 3297 far away.
3286 3298
3287 3299 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
3288 3300 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
3289 3301 $ try 'callinjection(2:5)'
3290 3302 (func
3291 3303 ('symbol', 'callinjection')
3292 3304 (range
3293 3305 ('symbol', '2')
3294 3306 ('symbol', '5')))
3295 3307 * expanded:
3296 3308 (func
3297 3309 ('symbol', 'descendants')
3298 3310 (func
3299 3311 ('symbol', 'max')
3300 3312 ('string', '$1')))
3301 3313 abort: unknown revision '$1'!
3302 3314 [255]
3303 3315
3304 3316 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
3305 3317 but 'all()' should never be substituted to '0()'.
3306 3318
3307 3319 $ echo 'universe = all()' >> .hg/hgrc
3308 3320 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
3309 3321 $ try 'shadowall(0)'
3310 3322 (func
3311 3323 ('symbol', 'shadowall')
3312 3324 ('symbol', '0'))
3313 3325 * expanded:
3314 3326 (and
3315 3327 ('symbol', '0')
3316 3328 (func
3317 3329 ('symbol', 'all')
3318 3330 None))
3319 3331 * set:
3320 3332 <filteredset
3321 3333 <baseset [0]>,
3322 3334 <spanset+ 0:9>>
3323 3335 0
3324 3336
3325 3337 test unknown reference:
3326 3338
3327 3339 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
3328 3340 (func
3329 3341 ('symbol', 'unknownref')
3330 3342 ('symbol', '0'))
3331 3343 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
3332 3344 [255]
3333 3345
3334 3346 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
3335 3347 ('symbol', 'tip')
3336 3348 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
3337 3349 * set:
3338 3350 <baseset [9]>
3339 3351 9
3340 3352
3341 3353 $ try 'tip'
3342 3354 ('symbol', 'tip')
3343 3355 * set:
3344 3356 <baseset [9]>
3345 3357 9
3346 3358
3347 3359 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
3348 3360 ('symbol', 'tip')
3349 3361 warning: bad declaration of revset alias "bad name": at 4: invalid token
3350 3362 * set:
3351 3363 <baseset [9]>
3352 3364 9
3353 3365 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
3354 3366 $ try 'strictreplacing("foo", tip)'
3355 3367 (func
3356 3368 ('symbol', 'strictreplacing')
3357 3369 (list
3358 3370 ('string', 'foo')
3359 3371 ('symbol', 'tip')))
3360 3372 * expanded:
3361 3373 (or
3362 3374 (list
3363 3375 ('symbol', 'tip')
3364 3376 (func
3365 3377 ('symbol', 'desc')
3366 3378 ('string', '$1'))))
3367 3379 * set:
3368 3380 <addset
3369 3381 <baseset [9]>,
3370 3382 <filteredset
3371 3383 <fullreposet+ 0:9>,
3372 3384 <desc '$1'>>>
3373 3385 9
3374 3386
3375 3387 $ try 'd(2:5)'
3376 3388 (func
3377 3389 ('symbol', 'd')
3378 3390 (range
3379 3391 ('symbol', '2')
3380 3392 ('symbol', '5')))
3381 3393 * expanded:
3382 3394 (func
3383 3395 ('symbol', 'reverse')
3384 3396 (func
3385 3397 ('symbol', 'sort')
3386 3398 (list
3387 3399 (range
3388 3400 ('symbol', '2')
3389 3401 ('symbol', '5'))
3390 3402 ('symbol', 'date'))))
3391 3403 * set:
3392 3404 <baseset [4, 5, 3, 2]>
3393 3405 4
3394 3406 5
3395 3407 3
3396 3408 2
3397 3409 $ try 'rs(2 or 3, date)'
3398 3410 (func
3399 3411 ('symbol', 'rs')
3400 3412 (list
3401 3413 (or
3402 3414 (list
3403 3415 ('symbol', '2')
3404 3416 ('symbol', '3')))
3405 3417 ('symbol', 'date')))
3406 3418 * expanded:
3407 3419 (func
3408 3420 ('symbol', 'reverse')
3409 3421 (func
3410 3422 ('symbol', 'sort')
3411 3423 (list
3412 3424 (or
3413 3425 (list
3414 3426 ('symbol', '2')
3415 3427 ('symbol', '3')))
3416 3428 ('symbol', 'date'))))
3417 3429 * set:
3418 3430 <baseset [3, 2]>
3419 3431 3
3420 3432 2
3421 3433 $ try 'rs()'
3422 3434 (func
3423 3435 ('symbol', 'rs')
3424 3436 None)
3425 3437 hg: parse error: invalid number of arguments: 0
3426 3438 [255]
3427 3439 $ try 'rs(2)'
3428 3440 (func
3429 3441 ('symbol', 'rs')
3430 3442 ('symbol', '2'))
3431 3443 hg: parse error: invalid number of arguments: 1
3432 3444 [255]
3433 3445 $ try 'rs(2, data, 7)'
3434 3446 (func
3435 3447 ('symbol', 'rs')
3436 3448 (list
3437 3449 ('symbol', '2')
3438 3450 ('symbol', 'data')
3439 3451 ('symbol', '7')))
3440 3452 hg: parse error: invalid number of arguments: 3
3441 3453 [255]
3442 3454 $ try 'rs4(2 or 3, x, x, date)'
3443 3455 (func
3444 3456 ('symbol', 'rs4')
3445 3457 (list
3446 3458 (or
3447 3459 (list
3448 3460 ('symbol', '2')
3449 3461 ('symbol', '3')))
3450 3462 ('symbol', 'x')
3451 3463 ('symbol', 'x')
3452 3464 ('symbol', 'date')))
3453 3465 * expanded:
3454 3466 (func
3455 3467 ('symbol', 'reverse')
3456 3468 (func
3457 3469 ('symbol', 'sort')
3458 3470 (list
3459 3471 (or
3460 3472 (list
3461 3473 ('symbol', '2')
3462 3474 ('symbol', '3')))
3463 3475 ('symbol', 'date'))))
3464 3476 * set:
3465 3477 <baseset [3, 2]>
3466 3478 3
3467 3479 2
3468 3480
3469 3481 issue4553: check that revset aliases override existing hash prefix
3470 3482
3471 3483 $ hg log -qr e
3472 3484 6:e0cc66ef77e8
3473 3485
3474 3486 $ hg log -qr e --config revsetalias.e="all()"
3475 3487 0:2785f51eece5
3476 3488 1:d75937da8da0
3477 3489 2:5ed5505e9f1c
3478 3490 3:8528aa5637f2
3479 3491 4:2326846efdab
3480 3492 5:904fa392b941
3481 3493 6:e0cc66ef77e8
3482 3494 7:013af1973af4
3483 3495 8:d5d0dcbdc4d9
3484 3496 9:24286f4ae135
3485 3497
3486 3498 $ hg log -qr e: --config revsetalias.e="0"
3487 3499 0:2785f51eece5
3488 3500 1:d75937da8da0
3489 3501 2:5ed5505e9f1c
3490 3502 3:8528aa5637f2
3491 3503 4:2326846efdab
3492 3504 5:904fa392b941
3493 3505 6:e0cc66ef77e8
3494 3506 7:013af1973af4
3495 3507 8:d5d0dcbdc4d9
3496 3508 9:24286f4ae135
3497 3509
3498 3510 $ hg log -qr :e --config revsetalias.e="9"
3499 3511 0:2785f51eece5
3500 3512 1:d75937da8da0
3501 3513 2:5ed5505e9f1c
3502 3514 3:8528aa5637f2
3503 3515 4:2326846efdab
3504 3516 5:904fa392b941
3505 3517 6:e0cc66ef77e8
3506 3518 7:013af1973af4
3507 3519 8:d5d0dcbdc4d9
3508 3520 9:24286f4ae135
3509 3521
3510 3522 $ hg log -qr e:
3511 3523 6:e0cc66ef77e8
3512 3524 7:013af1973af4
3513 3525 8:d5d0dcbdc4d9
3514 3526 9:24286f4ae135
3515 3527
3516 3528 $ hg log -qr :e
3517 3529 0:2785f51eece5
3518 3530 1:d75937da8da0
3519 3531 2:5ed5505e9f1c
3520 3532 3:8528aa5637f2
3521 3533 4:2326846efdab
3522 3534 5:904fa392b941
3523 3535 6:e0cc66ef77e8
3524 3536
3525 3537 issue2549 - correct optimizations
3526 3538
3527 3539 $ try 'limit(1 or 2 or 3, 2) and not 2'
3528 3540 (and
3529 3541 (func
3530 3542 ('symbol', 'limit')
3531 3543 (list
3532 3544 (or
3533 3545 (list
3534 3546 ('symbol', '1')
3535 3547 ('symbol', '2')
3536 3548 ('symbol', '3')))
3537 3549 ('symbol', '2')))
3538 3550 (not
3539 3551 ('symbol', '2')))
3540 3552 * set:
3541 3553 <filteredset
3542 3554 <baseset
3543 3555 <limit n=2, offset=0,
3544 3556 <fullreposet+ 0:9>,
3545 3557 <baseset [1, 2, 3]>>>,
3546 3558 <not
3547 3559 <baseset [2]>>>
3548 3560 1
3549 3561 $ try 'max(1 or 2) and not 2'
3550 3562 (and
3551 3563 (func
3552 3564 ('symbol', 'max')
3553 3565 (or
3554 3566 (list
3555 3567 ('symbol', '1')
3556 3568 ('symbol', '2'))))
3557 3569 (not
3558 3570 ('symbol', '2')))
3559 3571 * set:
3560 3572 <filteredset
3561 3573 <baseset
3562 3574 <max
3563 3575 <fullreposet+ 0:9>,
3564 3576 <baseset [1, 2]>>>,
3565 3577 <not
3566 3578 <baseset [2]>>>
3567 3579 $ try 'min(1 or 2) and not 1'
3568 3580 (and
3569 3581 (func
3570 3582 ('symbol', 'min')
3571 3583 (or
3572 3584 (list
3573 3585 ('symbol', '1')
3574 3586 ('symbol', '2'))))
3575 3587 (not
3576 3588 ('symbol', '1')))
3577 3589 * set:
3578 3590 <filteredset
3579 3591 <baseset
3580 3592 <min
3581 3593 <fullreposet+ 0:9>,
3582 3594 <baseset [1, 2]>>>,
3583 3595 <not
3584 3596 <baseset [1]>>>
3585 3597 $ try 'last(1 or 2, 1) and not 2'
3586 3598 (and
3587 3599 (func
3588 3600 ('symbol', 'last')
3589 3601 (list
3590 3602 (or
3591 3603 (list
3592 3604 ('symbol', '1')
3593 3605 ('symbol', '2')))
3594 3606 ('symbol', '1')))
3595 3607 (not
3596 3608 ('symbol', '2')))
3597 3609 * set:
3598 3610 <filteredset
3599 3611 <baseset
3600 3612 <last n=1,
3601 3613 <fullreposet+ 0:9>,
3602 3614 <baseset [2, 1]>>>,
3603 3615 <not
3604 3616 <baseset [2]>>>
3605 3617
3606 3618 issue4289 - ordering of built-ins
3607 3619 $ hg log -M -q -r 3:2
3608 3620 3:8528aa5637f2
3609 3621 2:5ed5505e9f1c
3610 3622
3611 3623 test revsets started with 40-chars hash (issue3669)
3612 3624
3613 3625 $ ISSUE3669_TIP=`hg tip --template '{node}'`
3614 3626 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
3615 3627 9
3616 3628 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
3617 3629 8
3618 3630
3619 3631 test or-ed indirect predicates (issue3775)
3620 3632
3621 3633 $ log '6 or 6^1' | sort
3622 3634 5
3623 3635 6
3624 3636 $ log '6^1 or 6' | sort
3625 3637 5
3626 3638 6
3627 3639 $ log '4 or 4~1' | sort
3628 3640 2
3629 3641 4
3630 3642 $ log '4~1 or 4' | sort
3631 3643 2
3632 3644 4
3633 3645 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
3634 3646 0
3635 3647 1
3636 3648 2
3637 3649 3
3638 3650 4
3639 3651 5
3640 3652 6
3641 3653 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
3642 3654 0
3643 3655 1
3644 3656 2
3645 3657 3
3646 3658 4
3647 3659 5
3648 3660 6
3649 3661
3650 3662 tests for 'remote()' predicate:
3651 3663 #. (csets in remote) (id) (remote)
3652 3664 1. less than local current branch "default"
3653 3665 2. same with local specified "default"
3654 3666 3. more than local specified specified
3655 3667
3656 3668 $ hg clone --quiet -U . ../remote3
3657 3669 $ cd ../remote3
3658 3670 $ hg update -q 7
3659 3671 $ echo r > r
3660 3672 $ hg ci -Aqm 10
3661 3673 $ log 'remote()'
3662 3674 7
3663 3675 $ log 'remote("a-b-c-")'
3664 3676 2
3665 3677 $ cd ../repo
3666 3678 $ log 'remote(".a.b.c.", "../remote3")'
3667 3679
3668 3680 tests for concatenation of strings/symbols by "##"
3669 3681
3670 3682 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
3671 3683 (_concat
3672 3684 (_concat
3673 3685 (_concat
3674 3686 ('symbol', '278')
3675 3687 ('string', '5f5'))
3676 3688 ('symbol', '1ee'))
3677 3689 ('string', 'ce5'))
3678 3690 * concatenated:
3679 3691 ('string', '2785f51eece5')
3680 3692 * set:
3681 3693 <baseset [0]>
3682 3694 0
3683 3695
3684 3696 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
3685 3697 $ try "cat4(278, '5f5', 1ee, 'ce5')"
3686 3698 (func
3687 3699 ('symbol', 'cat4')
3688 3700 (list
3689 3701 ('symbol', '278')
3690 3702 ('string', '5f5')
3691 3703 ('symbol', '1ee')
3692 3704 ('string', 'ce5')))
3693 3705 * expanded:
3694 3706 (_concat
3695 3707 (_concat
3696 3708 (_concat
3697 3709 ('symbol', '278')
3698 3710 ('string', '5f5'))
3699 3711 ('symbol', '1ee'))
3700 3712 ('string', 'ce5'))
3701 3713 * concatenated:
3702 3714 ('string', '2785f51eece5')
3703 3715 * set:
3704 3716 <baseset [0]>
3705 3717 0
3706 3718
3707 3719 (check concatenation in alias nesting)
3708 3720
3709 3721 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
3710 3722 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
3711 3723 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
3712 3724 0
3713 3725
3714 3726 (check operator priority)
3715 3727
3716 3728 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
3717 3729 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
3718 3730 0
3719 3731 4
3720 3732
3721 3733 $ cd ..
3722 3734
3723 3735 prepare repository that has "default" branches of multiple roots
3724 3736
3725 3737 $ hg init namedbranch
3726 3738 $ cd namedbranch
3727 3739
3728 3740 $ echo default0 >> a
3729 3741 $ hg ci -Aqm0
3730 3742 $ echo default1 >> a
3731 3743 $ hg ci -m1
3732 3744
3733 3745 $ hg branch -q stable
3734 3746 $ echo stable2 >> a
3735 3747 $ hg ci -m2
3736 3748 $ echo stable3 >> a
3737 3749 $ hg ci -m3
3738 3750
3739 3751 $ hg update -q null
3740 3752 $ echo default4 >> a
3741 3753 $ hg ci -Aqm4
3742 3754 $ echo default5 >> a
3743 3755 $ hg ci -m5
3744 3756
3745 3757 "null" revision belongs to "default" branch (issue4683)
3746 3758
3747 3759 $ log 'branch(null)'
3748 3760 0
3749 3761 1
3750 3762 4
3751 3763 5
3752 3764
3753 3765 "null" revision belongs to "default" branch, but it shouldn't appear in set
3754 3766 unless explicitly specified (issue4682)
3755 3767
3756 3768 $ log 'children(branch(default))'
3757 3769 1
3758 3770 2
3759 3771 5
3760 3772
3761 3773 $ cd ..
3762 3774
3763 3775 test author/desc/keyword in problematic encoding
3764 3776 # unicode: cp932:
3765 3777 # u30A2 0x83 0x41(= 'A')
3766 3778 # u30C2 0x83 0x61(= 'a')
3767 3779
3768 3780 $ hg init problematicencoding
3769 3781 $ cd problematicencoding
3770 3782
3771 3783 $ python > setup.sh <<EOF
3772 3784 > print u'''
3773 3785 > echo a > text
3774 3786 > hg add text
3775 3787 > hg --encoding utf-8 commit -u '\u30A2' -m none
3776 3788 > echo b > text
3777 3789 > hg --encoding utf-8 commit -u '\u30C2' -m none
3778 3790 > echo c > text
3779 3791 > hg --encoding utf-8 commit -u none -m '\u30A2'
3780 3792 > echo d > text
3781 3793 > hg --encoding utf-8 commit -u none -m '\u30C2'
3782 3794 > '''.encode('utf-8')
3783 3795 > EOF
3784 3796 $ sh < setup.sh
3785 3797
3786 3798 test in problematic encoding
3787 3799 $ python > test.sh <<EOF
3788 3800 > print u'''
3789 3801 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
3790 3802 > echo ====
3791 3803 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
3792 3804 > echo ====
3793 3805 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
3794 3806 > echo ====
3795 3807 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
3796 3808 > echo ====
3797 3809 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
3798 3810 > echo ====
3799 3811 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
3800 3812 > '''.encode('cp932')
3801 3813 > EOF
3802 3814 $ sh < test.sh
3803 3815 0
3804 3816 ====
3805 3817 1
3806 3818 ====
3807 3819 2
3808 3820 ====
3809 3821 3
3810 3822 ====
3811 3823 0
3812 3824 2
3813 3825 ====
3814 3826 1
3815 3827 3
3816 3828
3817 3829 test error message of bad revset
3818 3830 $ hg log -r 'foo\\'
3819 3831 hg: parse error at 3: syntax error in revset 'foo\\'
3820 3832 [255]
3821 3833
3822 3834 $ cd ..
3823 3835
3824 3836 Test that revset predicate of extension isn't loaded at failure of
3825 3837 loading it
3826 3838
3827 3839 $ cd repo
3828 3840
3829 3841 $ cat <<EOF > $TESTTMP/custompredicate.py
3830 3842 > from mercurial import error, registrar, revset
3831 3843 >
3832 3844 > revsetpredicate = registrar.revsetpredicate()
3833 3845 >
3834 3846 > @revsetpredicate('custom1()')
3835 3847 > def custom1(repo, subset, x):
3836 3848 > return revset.baseset([1])
3837 3849 >
3838 3850 > raise error.Abort('intentional failure of loading extension')
3839 3851 > EOF
3840 3852 $ cat <<EOF > .hg/hgrc
3841 3853 > [extensions]
3842 3854 > custompredicate = $TESTTMP/custompredicate.py
3843 3855 > EOF
3844 3856
3845 3857 $ hg debugrevspec "custom1()"
3846 3858 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
3847 3859 hg: parse error: unknown identifier: custom1
3848 3860 [255]
3849 3861
3850 3862 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now