##// END OF EJS Templates
revset: optimize "draft() & ::x" pattern...
Jun Wu -
r34067:c6c8a52e default
parent child Browse files
Show More
@@ -1,497 +1,519 b''
1 # dagop.py - graph ancestry and topology algorithm for revset
1 # dagop.py - graph ancestry and topology algorithm for revset
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import heapq
10 import heapq
11
11
12 from . import (
12 from . import (
13 error,
13 error,
14 mdiff,
14 mdiff,
15 node,
15 node,
16 patch,
16 patch,
17 smartset,
17 smartset,
18 )
18 )
19
19
20 baseset = smartset.baseset
20 baseset = smartset.baseset
21 generatorset = smartset.generatorset
21 generatorset = smartset.generatorset
22
22
23 # possible maximum depth between null and wdir()
23 # possible maximum depth between null and wdir()
24 _maxlogdepth = 0x80000000
24 _maxlogdepth = 0x80000000
25
25
26 def _walkrevtree(pfunc, revs, startdepth, stopdepth, reverse):
26 def _walkrevtree(pfunc, revs, startdepth, stopdepth, reverse):
27 """Walk DAG using 'pfunc' from the given 'revs' nodes
27 """Walk DAG using 'pfunc' from the given 'revs' nodes
28
28
29 'pfunc(rev)' should return the parent/child revisions of the given 'rev'
29 'pfunc(rev)' should return the parent/child revisions of the given 'rev'
30 if 'reverse' is True/False respectively.
30 if 'reverse' is True/False respectively.
31
31
32 Scan ends at the stopdepth (exlusive) if specified. Revisions found
32 Scan ends at the stopdepth (exlusive) if specified. Revisions found
33 earlier than the startdepth are omitted.
33 earlier than the startdepth are omitted.
34 """
34 """
35 if startdepth is None:
35 if startdepth is None:
36 startdepth = 0
36 startdepth = 0
37 if stopdepth is None:
37 if stopdepth is None:
38 stopdepth = _maxlogdepth
38 stopdepth = _maxlogdepth
39 if stopdepth == 0:
39 if stopdepth == 0:
40 return
40 return
41 if stopdepth < 0:
41 if stopdepth < 0:
42 raise error.ProgrammingError('negative stopdepth')
42 raise error.ProgrammingError('negative stopdepth')
43 if reverse:
43 if reverse:
44 heapsign = -1 # max heap
44 heapsign = -1 # max heap
45 else:
45 else:
46 heapsign = +1 # min heap
46 heapsign = +1 # min heap
47
47
48 # load input revs lazily to heap so earlier revisions can be yielded
48 # load input revs lazily to heap so earlier revisions can be yielded
49 # without fully computing the input revs
49 # without fully computing the input revs
50 revs.sort(reverse)
50 revs.sort(reverse)
51 irevs = iter(revs)
51 irevs = iter(revs)
52 pendingheap = [] # [(heapsign * rev, depth), ...] (i.e. lower depth first)
52 pendingheap = [] # [(heapsign * rev, depth), ...] (i.e. lower depth first)
53
53
54 inputrev = next(irevs, None)
54 inputrev = next(irevs, None)
55 if inputrev is not None:
55 if inputrev is not None:
56 heapq.heappush(pendingheap, (heapsign * inputrev, 0))
56 heapq.heappush(pendingheap, (heapsign * inputrev, 0))
57
57
58 lastrev = None
58 lastrev = None
59 while pendingheap:
59 while pendingheap:
60 currev, curdepth = heapq.heappop(pendingheap)
60 currev, curdepth = heapq.heappop(pendingheap)
61 currev = heapsign * currev
61 currev = heapsign * currev
62 if currev == inputrev:
62 if currev == inputrev:
63 inputrev = next(irevs, None)
63 inputrev = next(irevs, None)
64 if inputrev is not None:
64 if inputrev is not None:
65 heapq.heappush(pendingheap, (heapsign * inputrev, 0))
65 heapq.heappush(pendingheap, (heapsign * inputrev, 0))
66 # rescan parents until curdepth >= startdepth because queued entries
66 # rescan parents until curdepth >= startdepth because queued entries
67 # of the same revision are iterated from the lowest depth
67 # of the same revision are iterated from the lowest depth
68 foundnew = (currev != lastrev)
68 foundnew = (currev != lastrev)
69 if foundnew and curdepth >= startdepth:
69 if foundnew and curdepth >= startdepth:
70 lastrev = currev
70 lastrev = currev
71 yield currev
71 yield currev
72 pdepth = curdepth + 1
72 pdepth = curdepth + 1
73 if foundnew and pdepth < stopdepth:
73 if foundnew and pdepth < stopdepth:
74 for prev in pfunc(currev):
74 for prev in pfunc(currev):
75 if prev != node.nullrev:
75 if prev != node.nullrev:
76 heapq.heappush(pendingheap, (heapsign * prev, pdepth))
76 heapq.heappush(pendingheap, (heapsign * prev, pdepth))
77
77
78 def _genrevancestors(repo, revs, followfirst, startdepth, stopdepth):
78 def _genrevancestors(repo, revs, followfirst, startdepth, stopdepth, cutfunc):
79 if followfirst:
79 if followfirst:
80 cut = 1
80 cut = 1
81 else:
81 else:
82 cut = None
82 cut = None
83 cl = repo.changelog
83 cl = repo.changelog
84 def pfunc(rev):
84 def plainpfunc(rev):
85 try:
85 try:
86 return cl.parentrevs(rev)[:cut]
86 return cl.parentrevs(rev)[:cut]
87 except error.WdirUnsupported:
87 except error.WdirUnsupported:
88 return (pctx.rev() for pctx in repo[rev].parents()[:cut])
88 return (pctx.rev() for pctx in repo[rev].parents()[:cut])
89 if cutfunc is None:
90 pfunc = plainpfunc
91 else:
92 pfunc = lambda rev: [r for r in plainpfunc(rev) if not cutfunc(r)]
93 revs = revs.filter(lambda rev: not cutfunc(rev))
89 return _walkrevtree(pfunc, revs, startdepth, stopdepth, reverse=True)
94 return _walkrevtree(pfunc, revs, startdepth, stopdepth, reverse=True)
90
95
91 def revancestors(repo, revs, followfirst, startdepth=None, stopdepth=None):
96 def revancestors(repo, revs, followfirst=False, startdepth=None,
97 stopdepth=None, cutfunc=None):
92 """Like revlog.ancestors(), but supports additional options, includes
98 """Like revlog.ancestors(), but supports additional options, includes
93 the given revs themselves, and returns a smartset
99 the given revs themselves, and returns a smartset
94
100
95 Scan ends at the stopdepth (exlusive) if specified. Revisions found
101 Scan ends at the stopdepth (exlusive) if specified. Revisions found
96 earlier than the startdepth are omitted.
102 earlier than the startdepth are omitted.
103
104 If cutfunc is provided, it will be used to cut the traversal of the DAG.
105 When cutfunc(X) returns True, the DAG traversal stops - revision X and
106 X's ancestors in the traversal path will be skipped. This could be an
107 optimization sometimes.
108
109 Note: if Y is an ancestor of X, cutfunc(X) returning True does not
110 necessarily mean Y will also be cut. Usually cutfunc(Y) also wants to
111 return True in this case. For example,
112
113 D # revancestors(repo, D, cutfunc=lambda rev: rev == B)
114 |\ # will include "A", because the path D -> C -> A was not cut.
115 B C # If "B" gets cut, "A" might want to be cut too.
116 |/
117 A
97 """
118 """
98 gen = _genrevancestors(repo, revs, followfirst, startdepth, stopdepth)
119 gen = _genrevancestors(repo, revs, followfirst, startdepth, stopdepth,
120 cutfunc)
99 return generatorset(gen, iterasc=False)
121 return generatorset(gen, iterasc=False)
100
122
101 def _genrevdescendants(repo, revs, followfirst):
123 def _genrevdescendants(repo, revs, followfirst):
102 if followfirst:
124 if followfirst:
103 cut = 1
125 cut = 1
104 else:
126 else:
105 cut = None
127 cut = None
106
128
107 cl = repo.changelog
129 cl = repo.changelog
108 first = revs.min()
130 first = revs.min()
109 nullrev = node.nullrev
131 nullrev = node.nullrev
110 if first == nullrev:
132 if first == nullrev:
111 # Are there nodes with a null first parent and a non-null
133 # Are there nodes with a null first parent and a non-null
112 # second one? Maybe. Do we care? Probably not.
134 # second one? Maybe. Do we care? Probably not.
113 yield first
135 yield first
114 for i in cl:
136 for i in cl:
115 yield i
137 yield i
116 else:
138 else:
117 seen = set(revs)
139 seen = set(revs)
118 for i in cl.revs(first):
140 for i in cl.revs(first):
119 if i in seen:
141 if i in seen:
120 yield i
142 yield i
121 continue
143 continue
122 for x in cl.parentrevs(i)[:cut]:
144 for x in cl.parentrevs(i)[:cut]:
123 if x != nullrev and x in seen:
145 if x != nullrev and x in seen:
124 seen.add(i)
146 seen.add(i)
125 yield i
147 yield i
126 break
148 break
127
149
128 def _builddescendantsmap(repo, startrev, followfirst):
150 def _builddescendantsmap(repo, startrev, followfirst):
129 """Build map of 'rev -> child revs', offset from startrev"""
151 """Build map of 'rev -> child revs', offset from startrev"""
130 cl = repo.changelog
152 cl = repo.changelog
131 nullrev = node.nullrev
153 nullrev = node.nullrev
132 descmap = [[] for _rev in xrange(startrev, len(cl))]
154 descmap = [[] for _rev in xrange(startrev, len(cl))]
133 for currev in cl.revs(startrev + 1):
155 for currev in cl.revs(startrev + 1):
134 p1rev, p2rev = cl.parentrevs(currev)
156 p1rev, p2rev = cl.parentrevs(currev)
135 if p1rev >= startrev:
157 if p1rev >= startrev:
136 descmap[p1rev - startrev].append(currev)
158 descmap[p1rev - startrev].append(currev)
137 if not followfirst and p2rev != nullrev and p2rev >= startrev:
159 if not followfirst and p2rev != nullrev and p2rev >= startrev:
138 descmap[p2rev - startrev].append(currev)
160 descmap[p2rev - startrev].append(currev)
139 return descmap
161 return descmap
140
162
141 def _genrevdescendantsofdepth(repo, revs, followfirst, startdepth, stopdepth):
163 def _genrevdescendantsofdepth(repo, revs, followfirst, startdepth, stopdepth):
142 startrev = revs.min()
164 startrev = revs.min()
143 descmap = _builddescendantsmap(repo, startrev, followfirst)
165 descmap = _builddescendantsmap(repo, startrev, followfirst)
144 def pfunc(rev):
166 def pfunc(rev):
145 return descmap[rev - startrev]
167 return descmap[rev - startrev]
146 return _walkrevtree(pfunc, revs, startdepth, stopdepth, reverse=False)
168 return _walkrevtree(pfunc, revs, startdepth, stopdepth, reverse=False)
147
169
148 def revdescendants(repo, revs, followfirst, startdepth=None, stopdepth=None):
170 def revdescendants(repo, revs, followfirst, startdepth=None, stopdepth=None):
149 """Like revlog.descendants() but supports additional options, includes
171 """Like revlog.descendants() but supports additional options, includes
150 the given revs themselves, and returns a smartset
172 the given revs themselves, and returns a smartset
151
173
152 Scan ends at the stopdepth (exlusive) if specified. Revisions found
174 Scan ends at the stopdepth (exlusive) if specified. Revisions found
153 earlier than the startdepth are omitted.
175 earlier than the startdepth are omitted.
154 """
176 """
155 if startdepth is None and stopdepth is None:
177 if startdepth is None and stopdepth is None:
156 gen = _genrevdescendants(repo, revs, followfirst)
178 gen = _genrevdescendants(repo, revs, followfirst)
157 else:
179 else:
158 gen = _genrevdescendantsofdepth(repo, revs, followfirst,
180 gen = _genrevdescendantsofdepth(repo, revs, followfirst,
159 startdepth, stopdepth)
181 startdepth, stopdepth)
160 return generatorset(gen, iterasc=True)
182 return generatorset(gen, iterasc=True)
161
183
162 def _reachablerootspure(repo, minroot, roots, heads, includepath):
184 def _reachablerootspure(repo, minroot, roots, heads, includepath):
163 """return (heads(::<roots> and ::<heads>))
185 """return (heads(::<roots> and ::<heads>))
164
186
165 If includepath is True, return (<roots>::<heads>)."""
187 If includepath is True, return (<roots>::<heads>)."""
166 if not roots:
188 if not roots:
167 return []
189 return []
168 parentrevs = repo.changelog.parentrevs
190 parentrevs = repo.changelog.parentrevs
169 roots = set(roots)
191 roots = set(roots)
170 visit = list(heads)
192 visit = list(heads)
171 reachable = set()
193 reachable = set()
172 seen = {}
194 seen = {}
173 # prefetch all the things! (because python is slow)
195 # prefetch all the things! (because python is slow)
174 reached = reachable.add
196 reached = reachable.add
175 dovisit = visit.append
197 dovisit = visit.append
176 nextvisit = visit.pop
198 nextvisit = visit.pop
177 # open-code the post-order traversal due to the tiny size of
199 # open-code the post-order traversal due to the tiny size of
178 # sys.getrecursionlimit()
200 # sys.getrecursionlimit()
179 while visit:
201 while visit:
180 rev = nextvisit()
202 rev = nextvisit()
181 if rev in roots:
203 if rev in roots:
182 reached(rev)
204 reached(rev)
183 if not includepath:
205 if not includepath:
184 continue
206 continue
185 parents = parentrevs(rev)
207 parents = parentrevs(rev)
186 seen[rev] = parents
208 seen[rev] = parents
187 for parent in parents:
209 for parent in parents:
188 if parent >= minroot and parent not in seen:
210 if parent >= minroot and parent not in seen:
189 dovisit(parent)
211 dovisit(parent)
190 if not reachable:
212 if not reachable:
191 return baseset()
213 return baseset()
192 if not includepath:
214 if not includepath:
193 return reachable
215 return reachable
194 for rev in sorted(seen):
216 for rev in sorted(seen):
195 for parent in seen[rev]:
217 for parent in seen[rev]:
196 if parent in reachable:
218 if parent in reachable:
197 reached(rev)
219 reached(rev)
198 return reachable
220 return reachable
199
221
200 def reachableroots(repo, roots, heads, includepath=False):
222 def reachableroots(repo, roots, heads, includepath=False):
201 """return (heads(::<roots> and ::<heads>))
223 """return (heads(::<roots> and ::<heads>))
202
224
203 If includepath is True, return (<roots>::<heads>)."""
225 If includepath is True, return (<roots>::<heads>)."""
204 if not roots:
226 if not roots:
205 return baseset()
227 return baseset()
206 minroot = roots.min()
228 minroot = roots.min()
207 roots = list(roots)
229 roots = list(roots)
208 heads = list(heads)
230 heads = list(heads)
209 try:
231 try:
210 revs = repo.changelog.reachableroots(minroot, heads, roots, includepath)
232 revs = repo.changelog.reachableroots(minroot, heads, roots, includepath)
211 except AttributeError:
233 except AttributeError:
212 revs = _reachablerootspure(repo, minroot, roots, heads, includepath)
234 revs = _reachablerootspure(repo, minroot, roots, heads, includepath)
213 revs = baseset(revs)
235 revs = baseset(revs)
214 revs.sort()
236 revs.sort()
215 return revs
237 return revs
216
238
217 def _changesrange(fctx1, fctx2, linerange2, diffopts):
239 def _changesrange(fctx1, fctx2, linerange2, diffopts):
218 """Return `(diffinrange, linerange1)` where `diffinrange` is True
240 """Return `(diffinrange, linerange1)` where `diffinrange` is True
219 if diff from fctx2 to fctx1 has changes in linerange2 and
241 if diff from fctx2 to fctx1 has changes in linerange2 and
220 `linerange1` is the new line range for fctx1.
242 `linerange1` is the new line range for fctx1.
221 """
243 """
222 blocks = mdiff.allblocks(fctx1.data(), fctx2.data(), diffopts)
244 blocks = mdiff.allblocks(fctx1.data(), fctx2.data(), diffopts)
223 filteredblocks, linerange1 = mdiff.blocksinrange(blocks, linerange2)
245 filteredblocks, linerange1 = mdiff.blocksinrange(blocks, linerange2)
224 diffinrange = any(stype == '!' for _, stype in filteredblocks)
246 diffinrange = any(stype == '!' for _, stype in filteredblocks)
225 return diffinrange, linerange1
247 return diffinrange, linerange1
226
248
227 def blockancestors(fctx, fromline, toline, followfirst=False):
249 def blockancestors(fctx, fromline, toline, followfirst=False):
228 """Yield ancestors of `fctx` with respect to the block of lines within
250 """Yield ancestors of `fctx` with respect to the block of lines within
229 `fromline`-`toline` range.
251 `fromline`-`toline` range.
230 """
252 """
231 diffopts = patch.diffopts(fctx._repo.ui)
253 diffopts = patch.diffopts(fctx._repo.ui)
232 introrev = fctx.introrev()
254 introrev = fctx.introrev()
233 if fctx.rev() != introrev:
255 if fctx.rev() != introrev:
234 fctx = fctx.filectx(fctx.filenode(), changeid=introrev)
256 fctx = fctx.filectx(fctx.filenode(), changeid=introrev)
235 visit = {(fctx.linkrev(), fctx.filenode()): (fctx, (fromline, toline))}
257 visit = {(fctx.linkrev(), fctx.filenode()): (fctx, (fromline, toline))}
236 while visit:
258 while visit:
237 c, linerange2 = visit.pop(max(visit))
259 c, linerange2 = visit.pop(max(visit))
238 pl = c.parents()
260 pl = c.parents()
239 if followfirst:
261 if followfirst:
240 pl = pl[:1]
262 pl = pl[:1]
241 if not pl:
263 if not pl:
242 # The block originates from the initial revision.
264 # The block originates from the initial revision.
243 yield c, linerange2
265 yield c, linerange2
244 continue
266 continue
245 inrange = False
267 inrange = False
246 for p in pl:
268 for p in pl:
247 inrangep, linerange1 = _changesrange(p, c, linerange2, diffopts)
269 inrangep, linerange1 = _changesrange(p, c, linerange2, diffopts)
248 inrange = inrange or inrangep
270 inrange = inrange or inrangep
249 if linerange1[0] == linerange1[1]:
271 if linerange1[0] == linerange1[1]:
250 # Parent's linerange is empty, meaning that the block got
272 # Parent's linerange is empty, meaning that the block got
251 # introduced in this revision; no need to go futher in this
273 # introduced in this revision; no need to go futher in this
252 # branch.
274 # branch.
253 continue
275 continue
254 # Set _descendantrev with 'c' (a known descendant) so that, when
276 # Set _descendantrev with 'c' (a known descendant) so that, when
255 # _adjustlinkrev is called for 'p', it receives this descendant
277 # _adjustlinkrev is called for 'p', it receives this descendant
256 # (as srcrev) instead possibly topmost introrev.
278 # (as srcrev) instead possibly topmost introrev.
257 p._descendantrev = c.rev()
279 p._descendantrev = c.rev()
258 visit[p.linkrev(), p.filenode()] = p, linerange1
280 visit[p.linkrev(), p.filenode()] = p, linerange1
259 if inrange:
281 if inrange:
260 yield c, linerange2
282 yield c, linerange2
261
283
262 def blockdescendants(fctx, fromline, toline):
284 def blockdescendants(fctx, fromline, toline):
263 """Yield descendants of `fctx` with respect to the block of lines within
285 """Yield descendants of `fctx` with respect to the block of lines within
264 `fromline`-`toline` range.
286 `fromline`-`toline` range.
265 """
287 """
266 # First possibly yield 'fctx' if it has changes in range with respect to
288 # First possibly yield 'fctx' if it has changes in range with respect to
267 # its parents.
289 # its parents.
268 try:
290 try:
269 c, linerange1 = next(blockancestors(fctx, fromline, toline))
291 c, linerange1 = next(blockancestors(fctx, fromline, toline))
270 except StopIteration:
292 except StopIteration:
271 pass
293 pass
272 else:
294 else:
273 if c == fctx:
295 if c == fctx:
274 yield c, linerange1
296 yield c, linerange1
275
297
276 diffopts = patch.diffopts(fctx._repo.ui)
298 diffopts = patch.diffopts(fctx._repo.ui)
277 fl = fctx.filelog()
299 fl = fctx.filelog()
278 seen = {fctx.filerev(): (fctx, (fromline, toline))}
300 seen = {fctx.filerev(): (fctx, (fromline, toline))}
279 for i in fl.descendants([fctx.filerev()]):
301 for i in fl.descendants([fctx.filerev()]):
280 c = fctx.filectx(i)
302 c = fctx.filectx(i)
281 inrange = False
303 inrange = False
282 for x in fl.parentrevs(i):
304 for x in fl.parentrevs(i):
283 try:
305 try:
284 p, linerange2 = seen[x]
306 p, linerange2 = seen[x]
285 except KeyError:
307 except KeyError:
286 # nullrev or other branch
308 # nullrev or other branch
287 continue
309 continue
288 inrangep, linerange1 = _changesrange(c, p, linerange2, diffopts)
310 inrangep, linerange1 = _changesrange(c, p, linerange2, diffopts)
289 inrange = inrange or inrangep
311 inrange = inrange or inrangep
290 # If revision 'i' has been seen (it's a merge) and the line range
312 # If revision 'i' has been seen (it's a merge) and the line range
291 # previously computed differs from the one we just got, we take the
313 # previously computed differs from the one we just got, we take the
292 # surrounding interval. This is conservative but avoids loosing
314 # surrounding interval. This is conservative but avoids loosing
293 # information.
315 # information.
294 if i in seen and seen[i][1] != linerange1:
316 if i in seen and seen[i][1] != linerange1:
295 lbs, ubs = zip(linerange1, seen[i][1])
317 lbs, ubs = zip(linerange1, seen[i][1])
296 linerange1 = min(lbs), max(ubs)
318 linerange1 = min(lbs), max(ubs)
297 seen[i] = c, linerange1
319 seen[i] = c, linerange1
298 if inrange:
320 if inrange:
299 yield c, linerange1
321 yield c, linerange1
300
322
301 def toposort(revs, parentsfunc, firstbranch=()):
323 def toposort(revs, parentsfunc, firstbranch=()):
302 """Yield revisions from heads to roots one (topo) branch at a time.
324 """Yield revisions from heads to roots one (topo) branch at a time.
303
325
304 This function aims to be used by a graph generator that wishes to minimize
326 This function aims to be used by a graph generator that wishes to minimize
305 the number of parallel branches and their interleaving.
327 the number of parallel branches and their interleaving.
306
328
307 Example iteration order (numbers show the "true" order in a changelog):
329 Example iteration order (numbers show the "true" order in a changelog):
308
330
309 o 4
331 o 4
310 |
332 |
311 o 1
333 o 1
312 |
334 |
313 | o 3
335 | o 3
314 | |
336 | |
315 | o 2
337 | o 2
316 |/
338 |/
317 o 0
339 o 0
318
340
319 Note that the ancestors of merges are understood by the current
341 Note that the ancestors of merges are understood by the current
320 algorithm to be on the same branch. This means no reordering will
342 algorithm to be on the same branch. This means no reordering will
321 occur behind a merge.
343 occur behind a merge.
322 """
344 """
323
345
324 ### Quick summary of the algorithm
346 ### Quick summary of the algorithm
325 #
347 #
326 # This function is based around a "retention" principle. We keep revisions
348 # This function is based around a "retention" principle. We keep revisions
327 # in memory until we are ready to emit a whole branch that immediately
349 # in memory until we are ready to emit a whole branch that immediately
328 # "merges" into an existing one. This reduces the number of parallel
350 # "merges" into an existing one. This reduces the number of parallel
329 # branches with interleaved revisions.
351 # branches with interleaved revisions.
330 #
352 #
331 # During iteration revs are split into two groups:
353 # During iteration revs are split into two groups:
332 # A) revision already emitted
354 # A) revision already emitted
333 # B) revision in "retention". They are stored as different subgroups.
355 # B) revision in "retention". They are stored as different subgroups.
334 #
356 #
335 # for each REV, we do the following logic:
357 # for each REV, we do the following logic:
336 #
358 #
337 # 1) if REV is a parent of (A), we will emit it. If there is a
359 # 1) if REV is a parent of (A), we will emit it. If there is a
338 # retention group ((B) above) that is blocked on REV being
360 # retention group ((B) above) that is blocked on REV being
339 # available, we emit all the revisions out of that retention
361 # available, we emit all the revisions out of that retention
340 # group first.
362 # group first.
341 #
363 #
342 # 2) else, we'll search for a subgroup in (B) awaiting for REV to be
364 # 2) else, we'll search for a subgroup in (B) awaiting for REV to be
343 # available, if such subgroup exist, we add REV to it and the subgroup is
365 # available, if such subgroup exist, we add REV to it and the subgroup is
344 # now awaiting for REV.parents() to be available.
366 # now awaiting for REV.parents() to be available.
345 #
367 #
346 # 3) finally if no such group existed in (B), we create a new subgroup.
368 # 3) finally if no such group existed in (B), we create a new subgroup.
347 #
369 #
348 #
370 #
349 # To bootstrap the algorithm, we emit the tipmost revision (which
371 # To bootstrap the algorithm, we emit the tipmost revision (which
350 # puts it in group (A) from above).
372 # puts it in group (A) from above).
351
373
352 revs.sort(reverse=True)
374 revs.sort(reverse=True)
353
375
354 # Set of parents of revision that have been emitted. They can be considered
376 # Set of parents of revision that have been emitted. They can be considered
355 # unblocked as the graph generator is already aware of them so there is no
377 # unblocked as the graph generator is already aware of them so there is no
356 # need to delay the revisions that reference them.
378 # need to delay the revisions that reference them.
357 #
379 #
358 # If someone wants to prioritize a branch over the others, pre-filling this
380 # If someone wants to prioritize a branch over the others, pre-filling this
359 # set will force all other branches to wait until this branch is ready to be
381 # set will force all other branches to wait until this branch is ready to be
360 # emitted.
382 # emitted.
361 unblocked = set(firstbranch)
383 unblocked = set(firstbranch)
362
384
363 # list of groups waiting to be displayed, each group is defined by:
385 # list of groups waiting to be displayed, each group is defined by:
364 #
386 #
365 # (revs: lists of revs waiting to be displayed,
387 # (revs: lists of revs waiting to be displayed,
366 # blocked: set of that cannot be displayed before those in 'revs')
388 # blocked: set of that cannot be displayed before those in 'revs')
367 #
389 #
368 # The second value ('blocked') correspond to parents of any revision in the
390 # The second value ('blocked') correspond to parents of any revision in the
369 # group ('revs') that is not itself contained in the group. The main idea
391 # group ('revs') that is not itself contained in the group. The main idea
370 # of this algorithm is to delay as much as possible the emission of any
392 # of this algorithm is to delay as much as possible the emission of any
371 # revision. This means waiting for the moment we are about to display
393 # revision. This means waiting for the moment we are about to display
372 # these parents to display the revs in a group.
394 # these parents to display the revs in a group.
373 #
395 #
374 # This first implementation is smart until it encounters a merge: it will
396 # This first implementation is smart until it encounters a merge: it will
375 # emit revs as soon as any parent is about to be emitted and can grow an
397 # emit revs as soon as any parent is about to be emitted and can grow an
376 # arbitrary number of revs in 'blocked'. In practice this mean we properly
398 # arbitrary number of revs in 'blocked'. In practice this mean we properly
377 # retains new branches but gives up on any special ordering for ancestors
399 # retains new branches but gives up on any special ordering for ancestors
378 # of merges. The implementation can be improved to handle this better.
400 # of merges. The implementation can be improved to handle this better.
379 #
401 #
380 # The first subgroup is special. It corresponds to all the revision that
402 # The first subgroup is special. It corresponds to all the revision that
381 # were already emitted. The 'revs' lists is expected to be empty and the
403 # were already emitted. The 'revs' lists is expected to be empty and the
382 # 'blocked' set contains the parents revisions of already emitted revision.
404 # 'blocked' set contains the parents revisions of already emitted revision.
383 #
405 #
384 # You could pre-seed the <parents> set of groups[0] to a specific
406 # You could pre-seed the <parents> set of groups[0] to a specific
385 # changesets to select what the first emitted branch should be.
407 # changesets to select what the first emitted branch should be.
386 groups = [([], unblocked)]
408 groups = [([], unblocked)]
387 pendingheap = []
409 pendingheap = []
388 pendingset = set()
410 pendingset = set()
389
411
390 heapq.heapify(pendingheap)
412 heapq.heapify(pendingheap)
391 heappop = heapq.heappop
413 heappop = heapq.heappop
392 heappush = heapq.heappush
414 heappush = heapq.heappush
393 for currentrev in revs:
415 for currentrev in revs:
394 # Heap works with smallest element, we want highest so we invert
416 # Heap works with smallest element, we want highest so we invert
395 if currentrev not in pendingset:
417 if currentrev not in pendingset:
396 heappush(pendingheap, -currentrev)
418 heappush(pendingheap, -currentrev)
397 pendingset.add(currentrev)
419 pendingset.add(currentrev)
398 # iterates on pending rev until after the current rev have been
420 # iterates on pending rev until after the current rev have been
399 # processed.
421 # processed.
400 rev = None
422 rev = None
401 while rev != currentrev:
423 while rev != currentrev:
402 rev = -heappop(pendingheap)
424 rev = -heappop(pendingheap)
403 pendingset.remove(rev)
425 pendingset.remove(rev)
404
426
405 # Seek for a subgroup blocked, waiting for the current revision.
427 # Seek for a subgroup blocked, waiting for the current revision.
406 matching = [i for i, g in enumerate(groups) if rev in g[1]]
428 matching = [i for i, g in enumerate(groups) if rev in g[1]]
407
429
408 if matching:
430 if matching:
409 # The main idea is to gather together all sets that are blocked
431 # The main idea is to gather together all sets that are blocked
410 # on the same revision.
432 # on the same revision.
411 #
433 #
412 # Groups are merged when a common blocking ancestor is
434 # Groups are merged when a common blocking ancestor is
413 # observed. For example, given two groups:
435 # observed. For example, given two groups:
414 #
436 #
415 # revs [5, 4] waiting for 1
437 # revs [5, 4] waiting for 1
416 # revs [3, 2] waiting for 1
438 # revs [3, 2] waiting for 1
417 #
439 #
418 # These two groups will be merged when we process
440 # These two groups will be merged when we process
419 # 1. In theory, we could have merged the groups when
441 # 1. In theory, we could have merged the groups when
420 # we added 2 to the group it is now in (we could have
442 # we added 2 to the group it is now in (we could have
421 # noticed the groups were both blocked on 1 then), but
443 # noticed the groups were both blocked on 1 then), but
422 # the way it works now makes the algorithm simpler.
444 # the way it works now makes the algorithm simpler.
423 #
445 #
424 # We also always keep the oldest subgroup first. We can
446 # We also always keep the oldest subgroup first. We can
425 # probably improve the behavior by having the longest set
447 # probably improve the behavior by having the longest set
426 # first. That way, graph algorithms could minimise the length
448 # first. That way, graph algorithms could minimise the length
427 # of parallel lines their drawing. This is currently not done.
449 # of parallel lines their drawing. This is currently not done.
428 targetidx = matching.pop(0)
450 targetidx = matching.pop(0)
429 trevs, tparents = groups[targetidx]
451 trevs, tparents = groups[targetidx]
430 for i in matching:
452 for i in matching:
431 gr = groups[i]
453 gr = groups[i]
432 trevs.extend(gr[0])
454 trevs.extend(gr[0])
433 tparents |= gr[1]
455 tparents |= gr[1]
434 # delete all merged subgroups (except the one we kept)
456 # delete all merged subgroups (except the one we kept)
435 # (starting from the last subgroup for performance and
457 # (starting from the last subgroup for performance and
436 # sanity reasons)
458 # sanity reasons)
437 for i in reversed(matching):
459 for i in reversed(matching):
438 del groups[i]
460 del groups[i]
439 else:
461 else:
440 # This is a new head. We create a new subgroup for it.
462 # This is a new head. We create a new subgroup for it.
441 targetidx = len(groups)
463 targetidx = len(groups)
442 groups.append(([], {rev}))
464 groups.append(([], {rev}))
443
465
444 gr = groups[targetidx]
466 gr = groups[targetidx]
445
467
446 # We now add the current nodes to this subgroups. This is done
468 # We now add the current nodes to this subgroups. This is done
447 # after the subgroup merging because all elements from a subgroup
469 # after the subgroup merging because all elements from a subgroup
448 # that relied on this rev must precede it.
470 # that relied on this rev must precede it.
449 #
471 #
450 # we also update the <parents> set to include the parents of the
472 # we also update the <parents> set to include the parents of the
451 # new nodes.
473 # new nodes.
452 if rev == currentrev: # only display stuff in rev
474 if rev == currentrev: # only display stuff in rev
453 gr[0].append(rev)
475 gr[0].append(rev)
454 gr[1].remove(rev)
476 gr[1].remove(rev)
455 parents = [p for p in parentsfunc(rev) if p > node.nullrev]
477 parents = [p for p in parentsfunc(rev) if p > node.nullrev]
456 gr[1].update(parents)
478 gr[1].update(parents)
457 for p in parents:
479 for p in parents:
458 if p not in pendingset:
480 if p not in pendingset:
459 pendingset.add(p)
481 pendingset.add(p)
460 heappush(pendingheap, -p)
482 heappush(pendingheap, -p)
461
483
462 # Look for a subgroup to display
484 # Look for a subgroup to display
463 #
485 #
464 # When unblocked is empty (if clause), we were not waiting for any
486 # When unblocked is empty (if clause), we were not waiting for any
465 # revisions during the first iteration (if no priority was given) or
487 # revisions during the first iteration (if no priority was given) or
466 # if we emitted a whole disconnected set of the graph (reached a
488 # if we emitted a whole disconnected set of the graph (reached a
467 # root). In that case we arbitrarily take the oldest known
489 # root). In that case we arbitrarily take the oldest known
468 # subgroup. The heuristic could probably be better.
490 # subgroup. The heuristic could probably be better.
469 #
491 #
470 # Otherwise (elif clause) if the subgroup is blocked on
492 # Otherwise (elif clause) if the subgroup is blocked on
471 # a revision we just emitted, we can safely emit it as
493 # a revision we just emitted, we can safely emit it as
472 # well.
494 # well.
473 if not unblocked:
495 if not unblocked:
474 if len(groups) > 1: # display other subset
496 if len(groups) > 1: # display other subset
475 targetidx = 1
497 targetidx = 1
476 gr = groups[1]
498 gr = groups[1]
477 elif not gr[1] & unblocked:
499 elif not gr[1] & unblocked:
478 gr = None
500 gr = None
479
501
480 if gr is not None:
502 if gr is not None:
481 # update the set of awaited revisions with the one from the
503 # update the set of awaited revisions with the one from the
482 # subgroup
504 # subgroup
483 unblocked |= gr[1]
505 unblocked |= gr[1]
484 # output all revisions in the subgroup
506 # output all revisions in the subgroup
485 for r in gr[0]:
507 for r in gr[0]:
486 yield r
508 yield r
487 # delete the subgroup that you just output
509 # delete the subgroup that you just output
488 # unless it is groups[0] in which case you just empty it.
510 # unless it is groups[0] in which case you just empty it.
489 if targetidx:
511 if targetidx:
490 del groups[targetidx]
512 del groups[targetidx]
491 else:
513 else:
492 gr[0][:] = []
514 gr[0][:] = []
493 # Check if we have some subgroup waiting for revisions we are not going to
515 # Check if we have some subgroup waiting for revisions we are not going to
494 # iterate over
516 # iterate over
495 for g in groups:
517 for g in groups:
496 for r in g[0]:
518 for r in g[0]:
497 yield r
519 yield r
@@ -1,2189 +1,2220 b''
1 # revset.py - revision set queries for mercurial
1 # revset.py - revision set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import re
10 import re
11
11
12 from .i18n import _
12 from .i18n import _
13 from . import (
13 from . import (
14 dagop,
14 dagop,
15 destutil,
15 destutil,
16 encoding,
16 encoding,
17 error,
17 error,
18 hbisect,
18 hbisect,
19 match as matchmod,
19 match as matchmod,
20 node,
20 node,
21 obsolete as obsmod,
21 obsolete as obsmod,
22 obsutil,
22 obsutil,
23 pathutil,
23 pathutil,
24 phases,
24 phases,
25 registrar,
25 registrar,
26 repoview,
26 repoview,
27 revsetlang,
27 revsetlang,
28 scmutil,
28 scmutil,
29 smartset,
29 smartset,
30 util,
30 util,
31 )
31 )
32
32
33 # helpers for processing parsed tree
33 # helpers for processing parsed tree
34 getsymbol = revsetlang.getsymbol
34 getsymbol = revsetlang.getsymbol
35 getstring = revsetlang.getstring
35 getstring = revsetlang.getstring
36 getinteger = revsetlang.getinteger
36 getinteger = revsetlang.getinteger
37 getboolean = revsetlang.getboolean
37 getboolean = revsetlang.getboolean
38 getlist = revsetlang.getlist
38 getlist = revsetlang.getlist
39 getrange = revsetlang.getrange
39 getrange = revsetlang.getrange
40 getargs = revsetlang.getargs
40 getargs = revsetlang.getargs
41 getargsdict = revsetlang.getargsdict
41 getargsdict = revsetlang.getargsdict
42
42
43 baseset = smartset.baseset
43 baseset = smartset.baseset
44 generatorset = smartset.generatorset
44 generatorset = smartset.generatorset
45 spanset = smartset.spanset
45 spanset = smartset.spanset
46 fullreposet = smartset.fullreposet
46 fullreposet = smartset.fullreposet
47
47
48 # Constants for ordering requirement, used in getset():
48 # Constants for ordering requirement, used in getset():
49 #
49 #
50 # If 'define', any nested functions and operations MAY change the ordering of
50 # If 'define', any nested functions and operations MAY change the ordering of
51 # the entries in the set (but if changes the ordering, it MUST ALWAYS change
51 # the entries in the set (but if changes the ordering, it MUST ALWAYS change
52 # it). If 'follow', any nested functions and operations MUST take the ordering
52 # it). If 'follow', any nested functions and operations MUST take the ordering
53 # specified by the first operand to the '&' operator.
53 # specified by the first operand to the '&' operator.
54 #
54 #
55 # For instance,
55 # For instance,
56 #
56 #
57 # X & (Y | Z)
57 # X & (Y | Z)
58 # ^ ^^^^^^^
58 # ^ ^^^^^^^
59 # | follow
59 # | follow
60 # define
60 # define
61 #
61 #
62 # will be evaluated as 'or(y(x()), z(x()))', where 'x()' can change the order
62 # will be evaluated as 'or(y(x()), z(x()))', where 'x()' can change the order
63 # of the entries in the set, but 'y()', 'z()' and 'or()' shouldn't.
63 # of the entries in the set, but 'y()', 'z()' and 'or()' shouldn't.
64 #
64 #
65 # 'any' means the order doesn't matter. For instance,
65 # 'any' means the order doesn't matter. For instance,
66 #
66 #
67 # (X & !Y) | ancestors(Z)
67 # (X & !Y) | ancestors(Z)
68 # ^ ^
68 # ^ ^
69 # any any
69 # any any
70 #
70 #
71 # For 'X & !Y', 'X' decides the order and 'Y' is subtracted from 'X', so the
71 # For 'X & !Y', 'X' decides the order and 'Y' is subtracted from 'X', so the
72 # order of 'Y' does not matter. For 'ancestors(Z)', Z's order does not matter
72 # order of 'Y' does not matter. For 'ancestors(Z)', Z's order does not matter
73 # since 'ancestors' does not care about the order of its argument.
73 # since 'ancestors' does not care about the order of its argument.
74 #
74 #
75 # Currently, most revsets do not care about the order, so 'define' is
75 # Currently, most revsets do not care about the order, so 'define' is
76 # equivalent to 'follow' for them, and the resulting order is based on the
76 # equivalent to 'follow' for them, and the resulting order is based on the
77 # 'subset' parameter passed down to them:
77 # 'subset' parameter passed down to them:
78 #
78 #
79 # m = revset.match(...)
79 # m = revset.match(...)
80 # m(repo, subset, order=defineorder)
80 # m(repo, subset, order=defineorder)
81 # ^^^^^^
81 # ^^^^^^
82 # For most revsets, 'define' means using the order this subset provides
82 # For most revsets, 'define' means using the order this subset provides
83 #
83 #
84 # There are a few revsets that always redefine the order if 'define' is
84 # There are a few revsets that always redefine the order if 'define' is
85 # specified: 'sort(X)', 'reverse(X)', 'x:y'.
85 # specified: 'sort(X)', 'reverse(X)', 'x:y'.
86 anyorder = 'any' # don't care the order, could be even random-shuffled
86 anyorder = 'any' # don't care the order, could be even random-shuffled
87 defineorder = 'define' # ALWAYS redefine, or ALWAYS follow the current order
87 defineorder = 'define' # ALWAYS redefine, or ALWAYS follow the current order
88 followorder = 'follow' # MUST follow the current order
88 followorder = 'follow' # MUST follow the current order
89
89
90 # helpers
90 # helpers
91
91
92 def getset(repo, subset, x, order=defineorder):
92 def getset(repo, subset, x, order=defineorder):
93 if not x:
93 if not x:
94 raise error.ParseError(_("missing argument"))
94 raise error.ParseError(_("missing argument"))
95 return methods[x[0]](repo, subset, *x[1:], order=order)
95 return methods[x[0]](repo, subset, *x[1:], order=order)
96
96
97 def _getrevsource(repo, r):
97 def _getrevsource(repo, r):
98 extra = repo[r].extra()
98 extra = repo[r].extra()
99 for label in ('source', 'transplant_source', 'rebase_source'):
99 for label in ('source', 'transplant_source', 'rebase_source'):
100 if label in extra:
100 if label in extra:
101 try:
101 try:
102 return repo[extra[label]].rev()
102 return repo[extra[label]].rev()
103 except error.RepoLookupError:
103 except error.RepoLookupError:
104 pass
104 pass
105 return None
105 return None
106
106
107 # operator methods
107 # operator methods
108
108
109 def stringset(repo, subset, x, order):
109 def stringset(repo, subset, x, order):
110 x = scmutil.intrev(repo[x])
110 x = scmutil.intrev(repo[x])
111 if (x in subset
111 if (x in subset
112 or x == node.nullrev and isinstance(subset, fullreposet)):
112 or x == node.nullrev and isinstance(subset, fullreposet)):
113 return baseset([x])
113 return baseset([x])
114 return baseset()
114 return baseset()
115
115
116 def rangeset(repo, subset, x, y, order):
116 def rangeset(repo, subset, x, y, order):
117 m = getset(repo, fullreposet(repo), x)
117 m = getset(repo, fullreposet(repo), x)
118 n = getset(repo, fullreposet(repo), y)
118 n = getset(repo, fullreposet(repo), y)
119
119
120 if not m or not n:
120 if not m or not n:
121 return baseset()
121 return baseset()
122 return _makerangeset(repo, subset, m.first(), n.last(), order)
122 return _makerangeset(repo, subset, m.first(), n.last(), order)
123
123
124 def rangeall(repo, subset, x, order):
124 def rangeall(repo, subset, x, order):
125 assert x is None
125 assert x is None
126 return _makerangeset(repo, subset, 0, len(repo) - 1, order)
126 return _makerangeset(repo, subset, 0, len(repo) - 1, order)
127
127
128 def rangepre(repo, subset, y, order):
128 def rangepre(repo, subset, y, order):
129 # ':y' can't be rewritten to '0:y' since '0' may be hidden
129 # ':y' can't be rewritten to '0:y' since '0' may be hidden
130 n = getset(repo, fullreposet(repo), y)
130 n = getset(repo, fullreposet(repo), y)
131 if not n:
131 if not n:
132 return baseset()
132 return baseset()
133 return _makerangeset(repo, subset, 0, n.last(), order)
133 return _makerangeset(repo, subset, 0, n.last(), order)
134
134
135 def rangepost(repo, subset, x, order):
135 def rangepost(repo, subset, x, order):
136 m = getset(repo, fullreposet(repo), x)
136 m = getset(repo, fullreposet(repo), x)
137 if not m:
137 if not m:
138 return baseset()
138 return baseset()
139 return _makerangeset(repo, subset, m.first(), len(repo) - 1, order)
139 return _makerangeset(repo, subset, m.first(), len(repo) - 1, order)
140
140
141 def _makerangeset(repo, subset, m, n, order):
141 def _makerangeset(repo, subset, m, n, order):
142 if m == n:
142 if m == n:
143 r = baseset([m])
143 r = baseset([m])
144 elif n == node.wdirrev:
144 elif n == node.wdirrev:
145 r = spanset(repo, m, len(repo)) + baseset([n])
145 r = spanset(repo, m, len(repo)) + baseset([n])
146 elif m == node.wdirrev:
146 elif m == node.wdirrev:
147 r = baseset([m]) + spanset(repo, len(repo) - 1, n - 1)
147 r = baseset([m]) + spanset(repo, len(repo) - 1, n - 1)
148 elif m < n:
148 elif m < n:
149 r = spanset(repo, m, n + 1)
149 r = spanset(repo, m, n + 1)
150 else:
150 else:
151 r = spanset(repo, m, n - 1)
151 r = spanset(repo, m, n - 1)
152
152
153 if order == defineorder:
153 if order == defineorder:
154 return r & subset
154 return r & subset
155 else:
155 else:
156 # carrying the sorting over when possible would be more efficient
156 # carrying the sorting over when possible would be more efficient
157 return subset & r
157 return subset & r
158
158
159 def dagrange(repo, subset, x, y, order):
159 def dagrange(repo, subset, x, y, order):
160 r = fullreposet(repo)
160 r = fullreposet(repo)
161 xs = dagop.reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
161 xs = dagop.reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
162 includepath=True)
162 includepath=True)
163 return subset & xs
163 return subset & xs
164
164
165 def andset(repo, subset, x, y, order):
165 def andset(repo, subset, x, y, order):
166 if order == anyorder:
166 if order == anyorder:
167 yorder = anyorder
167 yorder = anyorder
168 else:
168 else:
169 yorder = followorder
169 yorder = followorder
170 return getset(repo, getset(repo, subset, x, order), y, yorder)
170 return getset(repo, getset(repo, subset, x, order), y, yorder)
171
171
172 def andsmallyset(repo, subset, x, y, order):
172 def andsmallyset(repo, subset, x, y, order):
173 # 'andsmally(x, y)' is equivalent to 'and(x, y)', but faster when y is small
173 # 'andsmally(x, y)' is equivalent to 'and(x, y)', but faster when y is small
174 if order == anyorder:
174 if order == anyorder:
175 yorder = anyorder
175 yorder = anyorder
176 else:
176 else:
177 yorder = followorder
177 yorder = followorder
178 return getset(repo, getset(repo, subset, y, yorder), x, order)
178 return getset(repo, getset(repo, subset, y, yorder), x, order)
179
179
180 def differenceset(repo, subset, x, y, order):
180 def differenceset(repo, subset, x, y, order):
181 return getset(repo, subset, x, order) - getset(repo, subset, y, anyorder)
181 return getset(repo, subset, x, order) - getset(repo, subset, y, anyorder)
182
182
183 def _orsetlist(repo, subset, xs, order):
183 def _orsetlist(repo, subset, xs, order):
184 assert xs
184 assert xs
185 if len(xs) == 1:
185 if len(xs) == 1:
186 return getset(repo, subset, xs[0], order)
186 return getset(repo, subset, xs[0], order)
187 p = len(xs) // 2
187 p = len(xs) // 2
188 a = _orsetlist(repo, subset, xs[:p], order)
188 a = _orsetlist(repo, subset, xs[:p], order)
189 b = _orsetlist(repo, subset, xs[p:], order)
189 b = _orsetlist(repo, subset, xs[p:], order)
190 return a + b
190 return a + b
191
191
192 def orset(repo, subset, x, order):
192 def orset(repo, subset, x, order):
193 xs = getlist(x)
193 xs = getlist(x)
194 if order == followorder:
194 if order == followorder:
195 # slow path to take the subset order
195 # slow path to take the subset order
196 return subset & _orsetlist(repo, fullreposet(repo), xs, anyorder)
196 return subset & _orsetlist(repo, fullreposet(repo), xs, anyorder)
197 else:
197 else:
198 return _orsetlist(repo, subset, xs, order)
198 return _orsetlist(repo, subset, xs, order)
199
199
200 def notset(repo, subset, x, order):
200 def notset(repo, subset, x, order):
201 return subset - getset(repo, subset, x, anyorder)
201 return subset - getset(repo, subset, x, anyorder)
202
202
203 def relationset(repo, subset, x, y, order):
203 def relationset(repo, subset, x, y, order):
204 raise error.ParseError(_("can't use a relation in this context"))
204 raise error.ParseError(_("can't use a relation in this context"))
205
205
206 def relsubscriptset(repo, subset, x, y, z, order):
206 def relsubscriptset(repo, subset, x, y, z, order):
207 # this is pretty basic implementation of 'x#y[z]' operator, still
207 # this is pretty basic implementation of 'x#y[z]' operator, still
208 # experimental so undocumented. see the wiki for further ideas.
208 # experimental so undocumented. see the wiki for further ideas.
209 # https://www.mercurial-scm.org/wiki/RevsetOperatorPlan
209 # https://www.mercurial-scm.org/wiki/RevsetOperatorPlan
210 rel = getsymbol(y)
210 rel = getsymbol(y)
211 n = getinteger(z, _("relation subscript must be an integer"))
211 n = getinteger(z, _("relation subscript must be an integer"))
212
212
213 # TODO: perhaps this should be a table of relation functions
213 # TODO: perhaps this should be a table of relation functions
214 if rel in ('g', 'generations'):
214 if rel in ('g', 'generations'):
215 # TODO: support range, rewrite tests, and drop startdepth argument
215 # TODO: support range, rewrite tests, and drop startdepth argument
216 # from ancestors() and descendants() predicates
216 # from ancestors() and descendants() predicates
217 if n <= 0:
217 if n <= 0:
218 n = -n
218 n = -n
219 return _ancestors(repo, subset, x, startdepth=n, stopdepth=n + 1)
219 return _ancestors(repo, subset, x, startdepth=n, stopdepth=n + 1)
220 else:
220 else:
221 return _descendants(repo, subset, x, startdepth=n, stopdepth=n + 1)
221 return _descendants(repo, subset, x, startdepth=n, stopdepth=n + 1)
222
222
223 raise error.UnknownIdentifier(rel, ['generations'])
223 raise error.UnknownIdentifier(rel, ['generations'])
224
224
225 def subscriptset(repo, subset, x, y, order):
225 def subscriptset(repo, subset, x, y, order):
226 raise error.ParseError(_("can't use a subscript in this context"))
226 raise error.ParseError(_("can't use a subscript in this context"))
227
227
228 def listset(repo, subset, *xs, **opts):
228 def listset(repo, subset, *xs, **opts):
229 raise error.ParseError(_("can't use a list in this context"),
229 raise error.ParseError(_("can't use a list in this context"),
230 hint=_('see hg help "revsets.x or y"'))
230 hint=_('see hg help "revsets.x or y"'))
231
231
232 def keyvaluepair(repo, subset, k, v, order):
232 def keyvaluepair(repo, subset, k, v, order):
233 raise error.ParseError(_("can't use a key-value pair in this context"))
233 raise error.ParseError(_("can't use a key-value pair in this context"))
234
234
235 def func(repo, subset, a, b, order):
235 def func(repo, subset, a, b, order):
236 f = getsymbol(a)
236 f = getsymbol(a)
237 if f in symbols:
237 if f in symbols:
238 func = symbols[f]
238 func = symbols[f]
239 if getattr(func, '_takeorder', False):
239 if getattr(func, '_takeorder', False):
240 return func(repo, subset, b, order)
240 return func(repo, subset, b, order)
241 return func(repo, subset, b)
241 return func(repo, subset, b)
242
242
243 keep = lambda fn: getattr(fn, '__doc__', None) is not None
243 keep = lambda fn: getattr(fn, '__doc__', None) is not None
244
244
245 syms = [s for (s, fn) in symbols.items() if keep(fn)]
245 syms = [s for (s, fn) in symbols.items() if keep(fn)]
246 raise error.UnknownIdentifier(f, syms)
246 raise error.UnknownIdentifier(f, syms)
247
247
248 # functions
248 # functions
249
249
250 # symbols are callables like:
250 # symbols are callables like:
251 # fn(repo, subset, x)
251 # fn(repo, subset, x)
252 # with:
252 # with:
253 # repo - current repository instance
253 # repo - current repository instance
254 # subset - of revisions to be examined
254 # subset - of revisions to be examined
255 # x - argument in tree form
255 # x - argument in tree form
256 symbols = {}
256 symbols = {}
257
257
258 # symbols which can't be used for a DoS attack for any given input
258 # symbols which can't be used for a DoS attack for any given input
259 # (e.g. those which accept regexes as plain strings shouldn't be included)
259 # (e.g. those which accept regexes as plain strings shouldn't be included)
260 # functions that just return a lot of changesets (like all) don't count here
260 # functions that just return a lot of changesets (like all) don't count here
261 safesymbols = set()
261 safesymbols = set()
262
262
263 predicate = registrar.revsetpredicate()
263 predicate = registrar.revsetpredicate()
264
264
265 @predicate('_destupdate')
265 @predicate('_destupdate')
266 def _destupdate(repo, subset, x):
266 def _destupdate(repo, subset, x):
267 # experimental revset for update destination
267 # experimental revset for update destination
268 args = getargsdict(x, 'limit', 'clean')
268 args = getargsdict(x, 'limit', 'clean')
269 return subset & baseset([destutil.destupdate(repo, **args)[0]])
269 return subset & baseset([destutil.destupdate(repo, **args)[0]])
270
270
271 @predicate('_destmerge')
271 @predicate('_destmerge')
272 def _destmerge(repo, subset, x):
272 def _destmerge(repo, subset, x):
273 # experimental revset for merge destination
273 # experimental revset for merge destination
274 sourceset = None
274 sourceset = None
275 if x is not None:
275 if x is not None:
276 sourceset = getset(repo, fullreposet(repo), x)
276 sourceset = getset(repo, fullreposet(repo), x)
277 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
277 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
278
278
279 @predicate('adds(pattern)', safe=True)
279 @predicate('adds(pattern)', safe=True)
280 def adds(repo, subset, x):
280 def adds(repo, subset, x):
281 """Changesets that add a file matching pattern.
281 """Changesets that add a file matching pattern.
282
282
283 The pattern without explicit kind like ``glob:`` is expected to be
283 The pattern without explicit kind like ``glob:`` is expected to be
284 relative to the current directory and match against a file or a
284 relative to the current directory and match against a file or a
285 directory.
285 directory.
286 """
286 """
287 # i18n: "adds" is a keyword
287 # i18n: "adds" is a keyword
288 pat = getstring(x, _("adds requires a pattern"))
288 pat = getstring(x, _("adds requires a pattern"))
289 return checkstatus(repo, subset, pat, 1)
289 return checkstatus(repo, subset, pat, 1)
290
290
291 @predicate('ancestor(*changeset)', safe=True)
291 @predicate('ancestor(*changeset)', safe=True)
292 def ancestor(repo, subset, x):
292 def ancestor(repo, subset, x):
293 """A greatest common ancestor of the changesets.
293 """A greatest common ancestor of the changesets.
294
294
295 Accepts 0 or more changesets.
295 Accepts 0 or more changesets.
296 Will return empty list when passed no args.
296 Will return empty list when passed no args.
297 Greatest common ancestor of a single changeset is that changeset.
297 Greatest common ancestor of a single changeset is that changeset.
298 """
298 """
299 # i18n: "ancestor" is a keyword
299 # i18n: "ancestor" is a keyword
300 l = getlist(x)
300 l = getlist(x)
301 rl = fullreposet(repo)
301 rl = fullreposet(repo)
302 anc = None
302 anc = None
303
303
304 # (getset(repo, rl, i) for i in l) generates a list of lists
304 # (getset(repo, rl, i) for i in l) generates a list of lists
305 for revs in (getset(repo, rl, i) for i in l):
305 for revs in (getset(repo, rl, i) for i in l):
306 for r in revs:
306 for r in revs:
307 if anc is None:
307 if anc is None:
308 anc = repo[r]
308 anc = repo[r]
309 else:
309 else:
310 anc = anc.ancestor(repo[r])
310 anc = anc.ancestor(repo[r])
311
311
312 if anc is not None and anc.rev() in subset:
312 if anc is not None and anc.rev() in subset:
313 return baseset([anc.rev()])
313 return baseset([anc.rev()])
314 return baseset()
314 return baseset()
315
315
316 def _ancestors(repo, subset, x, followfirst=False, startdepth=None,
316 def _ancestors(repo, subset, x, followfirst=False, startdepth=None,
317 stopdepth=None):
317 stopdepth=None):
318 heads = getset(repo, fullreposet(repo), x)
318 heads = getset(repo, fullreposet(repo), x)
319 if not heads:
319 if not heads:
320 return baseset()
320 return baseset()
321 s = dagop.revancestors(repo, heads, followfirst, startdepth, stopdepth)
321 s = dagop.revancestors(repo, heads, followfirst, startdepth, stopdepth)
322 return subset & s
322 return subset & s
323
323
324 @predicate('ancestors(set[, depth])', safe=True)
324 @predicate('ancestors(set[, depth])', safe=True)
325 def ancestors(repo, subset, x):
325 def ancestors(repo, subset, x):
326 """Changesets that are ancestors of changesets in set, including the
326 """Changesets that are ancestors of changesets in set, including the
327 given changesets themselves.
327 given changesets themselves.
328
328
329 If depth is specified, the result only includes changesets up to
329 If depth is specified, the result only includes changesets up to
330 the specified generation.
330 the specified generation.
331 """
331 """
332 # startdepth is for internal use only until we can decide the UI
332 # startdepth is for internal use only until we can decide the UI
333 args = getargsdict(x, 'ancestors', 'set depth startdepth')
333 args = getargsdict(x, 'ancestors', 'set depth startdepth')
334 if 'set' not in args:
334 if 'set' not in args:
335 # i18n: "ancestors" is a keyword
335 # i18n: "ancestors" is a keyword
336 raise error.ParseError(_('ancestors takes at least 1 argument'))
336 raise error.ParseError(_('ancestors takes at least 1 argument'))
337 startdepth = stopdepth = None
337 startdepth = stopdepth = None
338 if 'startdepth' in args:
338 if 'startdepth' in args:
339 n = getinteger(args['startdepth'],
339 n = getinteger(args['startdepth'],
340 "ancestors expects an integer startdepth")
340 "ancestors expects an integer startdepth")
341 if n < 0:
341 if n < 0:
342 raise error.ParseError("negative startdepth")
342 raise error.ParseError("negative startdepth")
343 startdepth = n
343 startdepth = n
344 if 'depth' in args:
344 if 'depth' in args:
345 # i18n: "ancestors" is a keyword
345 # i18n: "ancestors" is a keyword
346 n = getinteger(args['depth'], _("ancestors expects an integer depth"))
346 n = getinteger(args['depth'], _("ancestors expects an integer depth"))
347 if n < 0:
347 if n < 0:
348 raise error.ParseError(_("negative depth"))
348 raise error.ParseError(_("negative depth"))
349 stopdepth = n + 1
349 stopdepth = n + 1
350 return _ancestors(repo, subset, args['set'],
350 return _ancestors(repo, subset, args['set'],
351 startdepth=startdepth, stopdepth=stopdepth)
351 startdepth=startdepth, stopdepth=stopdepth)
352
352
353 @predicate('_firstancestors', safe=True)
353 @predicate('_firstancestors', safe=True)
354 def _firstancestors(repo, subset, x):
354 def _firstancestors(repo, subset, x):
355 # ``_firstancestors(set)``
355 # ``_firstancestors(set)``
356 # Like ``ancestors(set)`` but follows only the first parents.
356 # Like ``ancestors(set)`` but follows only the first parents.
357 return _ancestors(repo, subset, x, followfirst=True)
357 return _ancestors(repo, subset, x, followfirst=True)
358
358
359 def _childrenspec(repo, subset, x, n, order):
359 def _childrenspec(repo, subset, x, n, order):
360 """Changesets that are the Nth child of a changeset
360 """Changesets that are the Nth child of a changeset
361 in set.
361 in set.
362 """
362 """
363 cs = set()
363 cs = set()
364 for r in getset(repo, fullreposet(repo), x):
364 for r in getset(repo, fullreposet(repo), x):
365 for i in range(n):
365 for i in range(n):
366 c = repo[r].children()
366 c = repo[r].children()
367 if len(c) == 0:
367 if len(c) == 0:
368 break
368 break
369 if len(c) > 1:
369 if len(c) > 1:
370 raise error.RepoLookupError(
370 raise error.RepoLookupError(
371 _("revision in set has more than one child"))
371 _("revision in set has more than one child"))
372 r = c[0].rev()
372 r = c[0].rev()
373 else:
373 else:
374 cs.add(r)
374 cs.add(r)
375 return subset & cs
375 return subset & cs
376
376
377 def ancestorspec(repo, subset, x, n, order):
377 def ancestorspec(repo, subset, x, n, order):
378 """``set~n``
378 """``set~n``
379 Changesets that are the Nth ancestor (first parents only) of a changeset
379 Changesets that are the Nth ancestor (first parents only) of a changeset
380 in set.
380 in set.
381 """
381 """
382 n = getinteger(n, _("~ expects a number"))
382 n = getinteger(n, _("~ expects a number"))
383 if n < 0:
383 if n < 0:
384 # children lookup
384 # children lookup
385 return _childrenspec(repo, subset, x, -n, order)
385 return _childrenspec(repo, subset, x, -n, order)
386 ps = set()
386 ps = set()
387 cl = repo.changelog
387 cl = repo.changelog
388 for r in getset(repo, fullreposet(repo), x):
388 for r in getset(repo, fullreposet(repo), x):
389 for i in range(n):
389 for i in range(n):
390 try:
390 try:
391 r = cl.parentrevs(r)[0]
391 r = cl.parentrevs(r)[0]
392 except error.WdirUnsupported:
392 except error.WdirUnsupported:
393 r = repo[r].parents()[0].rev()
393 r = repo[r].parents()[0].rev()
394 ps.add(r)
394 ps.add(r)
395 return subset & ps
395 return subset & ps
396
396
397 @predicate('author(string)', safe=True)
397 @predicate('author(string)', safe=True)
398 def author(repo, subset, x):
398 def author(repo, subset, x):
399 """Alias for ``user(string)``.
399 """Alias for ``user(string)``.
400 """
400 """
401 # i18n: "author" is a keyword
401 # i18n: "author" is a keyword
402 n = getstring(x, _("author requires a string"))
402 n = getstring(x, _("author requires a string"))
403 kind, pattern, matcher = _substringmatcher(n, casesensitive=False)
403 kind, pattern, matcher = _substringmatcher(n, casesensitive=False)
404 return subset.filter(lambda x: matcher(repo[x].user()),
404 return subset.filter(lambda x: matcher(repo[x].user()),
405 condrepr=('<user %r>', n))
405 condrepr=('<user %r>', n))
406
406
407 @predicate('bisect(string)', safe=True)
407 @predicate('bisect(string)', safe=True)
408 def bisect(repo, subset, x):
408 def bisect(repo, subset, x):
409 """Changesets marked in the specified bisect status:
409 """Changesets marked in the specified bisect status:
410
410
411 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
411 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
412 - ``goods``, ``bads`` : csets topologically good/bad
412 - ``goods``, ``bads`` : csets topologically good/bad
413 - ``range`` : csets taking part in the bisection
413 - ``range`` : csets taking part in the bisection
414 - ``pruned`` : csets that are goods, bads or skipped
414 - ``pruned`` : csets that are goods, bads or skipped
415 - ``untested`` : csets whose fate is yet unknown
415 - ``untested`` : csets whose fate is yet unknown
416 - ``ignored`` : csets ignored due to DAG topology
416 - ``ignored`` : csets ignored due to DAG topology
417 - ``current`` : the cset currently being bisected
417 - ``current`` : the cset currently being bisected
418 """
418 """
419 # i18n: "bisect" is a keyword
419 # i18n: "bisect" is a keyword
420 status = getstring(x, _("bisect requires a string")).lower()
420 status = getstring(x, _("bisect requires a string")).lower()
421 state = set(hbisect.get(repo, status))
421 state = set(hbisect.get(repo, status))
422 return subset & state
422 return subset & state
423
423
424 # Backward-compatibility
424 # Backward-compatibility
425 # - no help entry so that we do not advertise it any more
425 # - no help entry so that we do not advertise it any more
426 @predicate('bisected', safe=True)
426 @predicate('bisected', safe=True)
427 def bisected(repo, subset, x):
427 def bisected(repo, subset, x):
428 return bisect(repo, subset, x)
428 return bisect(repo, subset, x)
429
429
430 @predicate('bookmark([name])', safe=True)
430 @predicate('bookmark([name])', safe=True)
431 def bookmark(repo, subset, x):
431 def bookmark(repo, subset, x):
432 """The named bookmark or all bookmarks.
432 """The named bookmark or all bookmarks.
433
433
434 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`.
434 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`.
435 """
435 """
436 # i18n: "bookmark" is a keyword
436 # i18n: "bookmark" is a keyword
437 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
437 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
438 if args:
438 if args:
439 bm = getstring(args[0],
439 bm = getstring(args[0],
440 # i18n: "bookmark" is a keyword
440 # i18n: "bookmark" is a keyword
441 _('the argument to bookmark must be a string'))
441 _('the argument to bookmark must be a string'))
442 kind, pattern, matcher = util.stringmatcher(bm)
442 kind, pattern, matcher = util.stringmatcher(bm)
443 bms = set()
443 bms = set()
444 if kind == 'literal':
444 if kind == 'literal':
445 bmrev = repo._bookmarks.get(pattern, None)
445 bmrev = repo._bookmarks.get(pattern, None)
446 if not bmrev:
446 if not bmrev:
447 raise error.RepoLookupError(_("bookmark '%s' does not exist")
447 raise error.RepoLookupError(_("bookmark '%s' does not exist")
448 % pattern)
448 % pattern)
449 bms.add(repo[bmrev].rev())
449 bms.add(repo[bmrev].rev())
450 else:
450 else:
451 matchrevs = set()
451 matchrevs = set()
452 for name, bmrev in repo._bookmarks.iteritems():
452 for name, bmrev in repo._bookmarks.iteritems():
453 if matcher(name):
453 if matcher(name):
454 matchrevs.add(bmrev)
454 matchrevs.add(bmrev)
455 if not matchrevs:
455 if not matchrevs:
456 raise error.RepoLookupError(_("no bookmarks exist"
456 raise error.RepoLookupError(_("no bookmarks exist"
457 " that match '%s'") % pattern)
457 " that match '%s'") % pattern)
458 for bmrev in matchrevs:
458 for bmrev in matchrevs:
459 bms.add(repo[bmrev].rev())
459 bms.add(repo[bmrev].rev())
460 else:
460 else:
461 bms = {repo[r].rev() for r in repo._bookmarks.values()}
461 bms = {repo[r].rev() for r in repo._bookmarks.values()}
462 bms -= {node.nullrev}
462 bms -= {node.nullrev}
463 return subset & bms
463 return subset & bms
464
464
465 @predicate('branch(string or set)', safe=True)
465 @predicate('branch(string or set)', safe=True)
466 def branch(repo, subset, x):
466 def branch(repo, subset, x):
467 """
467 """
468 All changesets belonging to the given branch or the branches of the given
468 All changesets belonging to the given branch or the branches of the given
469 changesets.
469 changesets.
470
470
471 Pattern matching is supported for `string`. See
471 Pattern matching is supported for `string`. See
472 :hg:`help revisions.patterns`.
472 :hg:`help revisions.patterns`.
473 """
473 """
474 getbi = repo.revbranchcache().branchinfo
474 getbi = repo.revbranchcache().branchinfo
475 def getbranch(r):
475 def getbranch(r):
476 try:
476 try:
477 return getbi(r)[0]
477 return getbi(r)[0]
478 except error.WdirUnsupported:
478 except error.WdirUnsupported:
479 return repo[r].branch()
479 return repo[r].branch()
480
480
481 try:
481 try:
482 b = getstring(x, '')
482 b = getstring(x, '')
483 except error.ParseError:
483 except error.ParseError:
484 # not a string, but another revspec, e.g. tip()
484 # not a string, but another revspec, e.g. tip()
485 pass
485 pass
486 else:
486 else:
487 kind, pattern, matcher = util.stringmatcher(b)
487 kind, pattern, matcher = util.stringmatcher(b)
488 if kind == 'literal':
488 if kind == 'literal':
489 # note: falls through to the revspec case if no branch with
489 # note: falls through to the revspec case if no branch with
490 # this name exists and pattern kind is not specified explicitly
490 # this name exists and pattern kind is not specified explicitly
491 if pattern in repo.branchmap():
491 if pattern in repo.branchmap():
492 return subset.filter(lambda r: matcher(getbranch(r)),
492 return subset.filter(lambda r: matcher(getbranch(r)),
493 condrepr=('<branch %r>', b))
493 condrepr=('<branch %r>', b))
494 if b.startswith('literal:'):
494 if b.startswith('literal:'):
495 raise error.RepoLookupError(_("branch '%s' does not exist")
495 raise error.RepoLookupError(_("branch '%s' does not exist")
496 % pattern)
496 % pattern)
497 else:
497 else:
498 return subset.filter(lambda r: matcher(getbranch(r)),
498 return subset.filter(lambda r: matcher(getbranch(r)),
499 condrepr=('<branch %r>', b))
499 condrepr=('<branch %r>', b))
500
500
501 s = getset(repo, fullreposet(repo), x)
501 s = getset(repo, fullreposet(repo), x)
502 b = set()
502 b = set()
503 for r in s:
503 for r in s:
504 b.add(getbranch(r))
504 b.add(getbranch(r))
505 c = s.__contains__
505 c = s.__contains__
506 return subset.filter(lambda r: c(r) or getbranch(r) in b,
506 return subset.filter(lambda r: c(r) or getbranch(r) in b,
507 condrepr=lambda: '<branch %r>' % sorted(b))
507 condrepr=lambda: '<branch %r>' % sorted(b))
508
508
509 @predicate('bumped()', safe=True)
509 @predicate('bumped()', safe=True)
510 def bumped(repo, subset, x):
510 def bumped(repo, subset, x):
511 msg = ("'bumped()' is deprecated, "
511 msg = ("'bumped()' is deprecated, "
512 "use 'phasedivergent()'")
512 "use 'phasedivergent()'")
513 repo.ui.deprecwarn(msg, '4.4')
513 repo.ui.deprecwarn(msg, '4.4')
514
514
515 return phasedivergent(repo, subset, x)
515 return phasedivergent(repo, subset, x)
516
516
517 @predicate('phasedivergent()', safe=True)
517 @predicate('phasedivergent()', safe=True)
518 def phasedivergent(repo, subset, x):
518 def phasedivergent(repo, subset, x):
519 """Mutable changesets marked as successors of public changesets.
519 """Mutable changesets marked as successors of public changesets.
520
520
521 Only non-public and non-obsolete changesets can be `phasedivergent`.
521 Only non-public and non-obsolete changesets can be `phasedivergent`.
522 (EXPERIMENTAL)
522 (EXPERIMENTAL)
523 """
523 """
524 # i18n: "phasedivergent" is a keyword
524 # i18n: "phasedivergent" is a keyword
525 getargs(x, 0, 0, _("phasedivergent takes no arguments"))
525 getargs(x, 0, 0, _("phasedivergent takes no arguments"))
526 phasedivergent = obsmod.getrevs(repo, 'phasedivergent')
526 phasedivergent = obsmod.getrevs(repo, 'phasedivergent')
527 return subset & phasedivergent
527 return subset & phasedivergent
528
528
529 @predicate('bundle()', safe=True)
529 @predicate('bundle()', safe=True)
530 def bundle(repo, subset, x):
530 def bundle(repo, subset, x):
531 """Changesets in the bundle.
531 """Changesets in the bundle.
532
532
533 Bundle must be specified by the -R option."""
533 Bundle must be specified by the -R option."""
534
534
535 try:
535 try:
536 bundlerevs = repo.changelog.bundlerevs
536 bundlerevs = repo.changelog.bundlerevs
537 except AttributeError:
537 except AttributeError:
538 raise error.Abort(_("no bundle provided - specify with -R"))
538 raise error.Abort(_("no bundle provided - specify with -R"))
539 return subset & bundlerevs
539 return subset & bundlerevs
540
540
541 def checkstatus(repo, subset, pat, field):
541 def checkstatus(repo, subset, pat, field):
542 hasset = matchmod.patkind(pat) == 'set'
542 hasset = matchmod.patkind(pat) == 'set'
543
543
544 mcache = [None]
544 mcache = [None]
545 def matches(x):
545 def matches(x):
546 c = repo[x]
546 c = repo[x]
547 if not mcache[0] or hasset:
547 if not mcache[0] or hasset:
548 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
548 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
549 m = mcache[0]
549 m = mcache[0]
550 fname = None
550 fname = None
551 if not m.anypats() and len(m.files()) == 1:
551 if not m.anypats() and len(m.files()) == 1:
552 fname = m.files()[0]
552 fname = m.files()[0]
553 if fname is not None:
553 if fname is not None:
554 if fname not in c.files():
554 if fname not in c.files():
555 return False
555 return False
556 else:
556 else:
557 for f in c.files():
557 for f in c.files():
558 if m(f):
558 if m(f):
559 break
559 break
560 else:
560 else:
561 return False
561 return False
562 files = repo.status(c.p1().node(), c.node())[field]
562 files = repo.status(c.p1().node(), c.node())[field]
563 if fname is not None:
563 if fname is not None:
564 if fname in files:
564 if fname in files:
565 return True
565 return True
566 else:
566 else:
567 for f in files:
567 for f in files:
568 if m(f):
568 if m(f):
569 return True
569 return True
570
570
571 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
571 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
572
572
573 def _children(repo, subset, parentset):
573 def _children(repo, subset, parentset):
574 if not parentset:
574 if not parentset:
575 return baseset()
575 return baseset()
576 cs = set()
576 cs = set()
577 pr = repo.changelog.parentrevs
577 pr = repo.changelog.parentrevs
578 minrev = parentset.min()
578 minrev = parentset.min()
579 nullrev = node.nullrev
579 nullrev = node.nullrev
580 for r in subset:
580 for r in subset:
581 if r <= minrev:
581 if r <= minrev:
582 continue
582 continue
583 p1, p2 = pr(r)
583 p1, p2 = pr(r)
584 if p1 in parentset:
584 if p1 in parentset:
585 cs.add(r)
585 cs.add(r)
586 if p2 != nullrev and p2 in parentset:
586 if p2 != nullrev and p2 in parentset:
587 cs.add(r)
587 cs.add(r)
588 return baseset(cs)
588 return baseset(cs)
589
589
590 @predicate('children(set)', safe=True)
590 @predicate('children(set)', safe=True)
591 def children(repo, subset, x):
591 def children(repo, subset, x):
592 """Child changesets of changesets in set.
592 """Child changesets of changesets in set.
593 """
593 """
594 s = getset(repo, fullreposet(repo), x)
594 s = getset(repo, fullreposet(repo), x)
595 cs = _children(repo, subset, s)
595 cs = _children(repo, subset, s)
596 return subset & cs
596 return subset & cs
597
597
598 @predicate('closed()', safe=True)
598 @predicate('closed()', safe=True)
599 def closed(repo, subset, x):
599 def closed(repo, subset, x):
600 """Changeset is closed.
600 """Changeset is closed.
601 """
601 """
602 # i18n: "closed" is a keyword
602 # i18n: "closed" is a keyword
603 getargs(x, 0, 0, _("closed takes no arguments"))
603 getargs(x, 0, 0, _("closed takes no arguments"))
604 return subset.filter(lambda r: repo[r].closesbranch(),
604 return subset.filter(lambda r: repo[r].closesbranch(),
605 condrepr='<branch closed>')
605 condrepr='<branch closed>')
606
606
607 @predicate('contains(pattern)')
607 @predicate('contains(pattern)')
608 def contains(repo, subset, x):
608 def contains(repo, subset, x):
609 """The revision's manifest contains a file matching pattern (but might not
609 """The revision's manifest contains a file matching pattern (but might not
610 modify it). See :hg:`help patterns` for information about file patterns.
610 modify it). See :hg:`help patterns` for information about file patterns.
611
611
612 The pattern without explicit kind like ``glob:`` is expected to be
612 The pattern without explicit kind like ``glob:`` is expected to be
613 relative to the current directory and match against a file exactly
613 relative to the current directory and match against a file exactly
614 for efficiency.
614 for efficiency.
615 """
615 """
616 # i18n: "contains" is a keyword
616 # i18n: "contains" is a keyword
617 pat = getstring(x, _("contains requires a pattern"))
617 pat = getstring(x, _("contains requires a pattern"))
618
618
619 def matches(x):
619 def matches(x):
620 if not matchmod.patkind(pat):
620 if not matchmod.patkind(pat):
621 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
621 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
622 if pats in repo[x]:
622 if pats in repo[x]:
623 return True
623 return True
624 else:
624 else:
625 c = repo[x]
625 c = repo[x]
626 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
626 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
627 for f in c.manifest():
627 for f in c.manifest():
628 if m(f):
628 if m(f):
629 return True
629 return True
630 return False
630 return False
631
631
632 return subset.filter(matches, condrepr=('<contains %r>', pat))
632 return subset.filter(matches, condrepr=('<contains %r>', pat))
633
633
634 @predicate('converted([id])', safe=True)
634 @predicate('converted([id])', safe=True)
635 def converted(repo, subset, x):
635 def converted(repo, subset, x):
636 """Changesets converted from the given identifier in the old repository if
636 """Changesets converted from the given identifier in the old repository if
637 present, or all converted changesets if no identifier is specified.
637 present, or all converted changesets if no identifier is specified.
638 """
638 """
639
639
640 # There is exactly no chance of resolving the revision, so do a simple
640 # There is exactly no chance of resolving the revision, so do a simple
641 # string compare and hope for the best
641 # string compare and hope for the best
642
642
643 rev = None
643 rev = None
644 # i18n: "converted" is a keyword
644 # i18n: "converted" is a keyword
645 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
645 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
646 if l:
646 if l:
647 # i18n: "converted" is a keyword
647 # i18n: "converted" is a keyword
648 rev = getstring(l[0], _('converted requires a revision'))
648 rev = getstring(l[0], _('converted requires a revision'))
649
649
650 def _matchvalue(r):
650 def _matchvalue(r):
651 source = repo[r].extra().get('convert_revision', None)
651 source = repo[r].extra().get('convert_revision', None)
652 return source is not None and (rev is None or source.startswith(rev))
652 return source is not None and (rev is None or source.startswith(rev))
653
653
654 return subset.filter(lambda r: _matchvalue(r),
654 return subset.filter(lambda r: _matchvalue(r),
655 condrepr=('<converted %r>', rev))
655 condrepr=('<converted %r>', rev))
656
656
657 @predicate('date(interval)', safe=True)
657 @predicate('date(interval)', safe=True)
658 def date(repo, subset, x):
658 def date(repo, subset, x):
659 """Changesets within the interval, see :hg:`help dates`.
659 """Changesets within the interval, see :hg:`help dates`.
660 """
660 """
661 # i18n: "date" is a keyword
661 # i18n: "date" is a keyword
662 ds = getstring(x, _("date requires a string"))
662 ds = getstring(x, _("date requires a string"))
663 dm = util.matchdate(ds)
663 dm = util.matchdate(ds)
664 return subset.filter(lambda x: dm(repo[x].date()[0]),
664 return subset.filter(lambda x: dm(repo[x].date()[0]),
665 condrepr=('<date %r>', ds))
665 condrepr=('<date %r>', ds))
666
666
667 @predicate('desc(string)', safe=True)
667 @predicate('desc(string)', safe=True)
668 def desc(repo, subset, x):
668 def desc(repo, subset, x):
669 """Search commit message for string. The match is case-insensitive.
669 """Search commit message for string. The match is case-insensitive.
670
670
671 Pattern matching is supported for `string`. See
671 Pattern matching is supported for `string`. See
672 :hg:`help revisions.patterns`.
672 :hg:`help revisions.patterns`.
673 """
673 """
674 # i18n: "desc" is a keyword
674 # i18n: "desc" is a keyword
675 ds = getstring(x, _("desc requires a string"))
675 ds = getstring(x, _("desc requires a string"))
676
676
677 kind, pattern, matcher = _substringmatcher(ds, casesensitive=False)
677 kind, pattern, matcher = _substringmatcher(ds, casesensitive=False)
678
678
679 return subset.filter(lambda r: matcher(repo[r].description()),
679 return subset.filter(lambda r: matcher(repo[r].description()),
680 condrepr=('<desc %r>', ds))
680 condrepr=('<desc %r>', ds))
681
681
682 def _descendants(repo, subset, x, followfirst=False, startdepth=None,
682 def _descendants(repo, subset, x, followfirst=False, startdepth=None,
683 stopdepth=None):
683 stopdepth=None):
684 roots = getset(repo, fullreposet(repo), x)
684 roots = getset(repo, fullreposet(repo), x)
685 if not roots:
685 if not roots:
686 return baseset()
686 return baseset()
687 s = dagop.revdescendants(repo, roots, followfirst, startdepth, stopdepth)
687 s = dagop.revdescendants(repo, roots, followfirst, startdepth, stopdepth)
688 return subset & s
688 return subset & s
689
689
690 @predicate('descendants(set[, depth])', safe=True)
690 @predicate('descendants(set[, depth])', safe=True)
691 def descendants(repo, subset, x):
691 def descendants(repo, subset, x):
692 """Changesets which are descendants of changesets in set, including the
692 """Changesets which are descendants of changesets in set, including the
693 given changesets themselves.
693 given changesets themselves.
694
694
695 If depth is specified, the result only includes changesets up to
695 If depth is specified, the result only includes changesets up to
696 the specified generation.
696 the specified generation.
697 """
697 """
698 # startdepth is for internal use only until we can decide the UI
698 # startdepth is for internal use only until we can decide the UI
699 args = getargsdict(x, 'descendants', 'set depth startdepth')
699 args = getargsdict(x, 'descendants', 'set depth startdepth')
700 if 'set' not in args:
700 if 'set' not in args:
701 # i18n: "descendants" is a keyword
701 # i18n: "descendants" is a keyword
702 raise error.ParseError(_('descendants takes at least 1 argument'))
702 raise error.ParseError(_('descendants takes at least 1 argument'))
703 startdepth = stopdepth = None
703 startdepth = stopdepth = None
704 if 'startdepth' in args:
704 if 'startdepth' in args:
705 n = getinteger(args['startdepth'],
705 n = getinteger(args['startdepth'],
706 "descendants expects an integer startdepth")
706 "descendants expects an integer startdepth")
707 if n < 0:
707 if n < 0:
708 raise error.ParseError("negative startdepth")
708 raise error.ParseError("negative startdepth")
709 startdepth = n
709 startdepth = n
710 if 'depth' in args:
710 if 'depth' in args:
711 # i18n: "descendants" is a keyword
711 # i18n: "descendants" is a keyword
712 n = getinteger(args['depth'], _("descendants expects an integer depth"))
712 n = getinteger(args['depth'], _("descendants expects an integer depth"))
713 if n < 0:
713 if n < 0:
714 raise error.ParseError(_("negative depth"))
714 raise error.ParseError(_("negative depth"))
715 stopdepth = n + 1
715 stopdepth = n + 1
716 return _descendants(repo, subset, args['set'],
716 return _descendants(repo, subset, args['set'],
717 startdepth=startdepth, stopdepth=stopdepth)
717 startdepth=startdepth, stopdepth=stopdepth)
718
718
719 @predicate('_firstdescendants', safe=True)
719 @predicate('_firstdescendants', safe=True)
720 def _firstdescendants(repo, subset, x):
720 def _firstdescendants(repo, subset, x):
721 # ``_firstdescendants(set)``
721 # ``_firstdescendants(set)``
722 # Like ``descendants(set)`` but follows only the first parents.
722 # Like ``descendants(set)`` but follows only the first parents.
723 return _descendants(repo, subset, x, followfirst=True)
723 return _descendants(repo, subset, x, followfirst=True)
724
724
725 @predicate('destination([set])', safe=True)
725 @predicate('destination([set])', safe=True)
726 def destination(repo, subset, x):
726 def destination(repo, subset, x):
727 """Changesets that were created by a graft, transplant or rebase operation,
727 """Changesets that were created by a graft, transplant or rebase operation,
728 with the given revisions specified as the source. Omitting the optional set
728 with the given revisions specified as the source. Omitting the optional set
729 is the same as passing all().
729 is the same as passing all().
730 """
730 """
731 if x is not None:
731 if x is not None:
732 sources = getset(repo, fullreposet(repo), x)
732 sources = getset(repo, fullreposet(repo), x)
733 else:
733 else:
734 sources = fullreposet(repo)
734 sources = fullreposet(repo)
735
735
736 dests = set()
736 dests = set()
737
737
738 # subset contains all of the possible destinations that can be returned, so
738 # subset contains all of the possible destinations that can be returned, so
739 # iterate over them and see if their source(s) were provided in the arg set.
739 # iterate over them and see if their source(s) were provided in the arg set.
740 # Even if the immediate src of r is not in the arg set, src's source (or
740 # Even if the immediate src of r is not in the arg set, src's source (or
741 # further back) may be. Scanning back further than the immediate src allows
741 # further back) may be. Scanning back further than the immediate src allows
742 # transitive transplants and rebases to yield the same results as transitive
742 # transitive transplants and rebases to yield the same results as transitive
743 # grafts.
743 # grafts.
744 for r in subset:
744 for r in subset:
745 src = _getrevsource(repo, r)
745 src = _getrevsource(repo, r)
746 lineage = None
746 lineage = None
747
747
748 while src is not None:
748 while src is not None:
749 if lineage is None:
749 if lineage is None:
750 lineage = list()
750 lineage = list()
751
751
752 lineage.append(r)
752 lineage.append(r)
753
753
754 # The visited lineage is a match if the current source is in the arg
754 # The visited lineage is a match if the current source is in the arg
755 # set. Since every candidate dest is visited by way of iterating
755 # set. Since every candidate dest is visited by way of iterating
756 # subset, any dests further back in the lineage will be tested by a
756 # subset, any dests further back in the lineage will be tested by a
757 # different iteration over subset. Likewise, if the src was already
757 # different iteration over subset. Likewise, if the src was already
758 # selected, the current lineage can be selected without going back
758 # selected, the current lineage can be selected without going back
759 # further.
759 # further.
760 if src in sources or src in dests:
760 if src in sources or src in dests:
761 dests.update(lineage)
761 dests.update(lineage)
762 break
762 break
763
763
764 r = src
764 r = src
765 src = _getrevsource(repo, r)
765 src = _getrevsource(repo, r)
766
766
767 return subset.filter(dests.__contains__,
767 return subset.filter(dests.__contains__,
768 condrepr=lambda: '<destination %r>' % sorted(dests))
768 condrepr=lambda: '<destination %r>' % sorted(dests))
769
769
770 @predicate('divergent()', safe=True)
770 @predicate('divergent()', safe=True)
771 def divergent(repo, subset, x):
771 def divergent(repo, subset, x):
772 msg = ("'divergent()' is deprecated, "
772 msg = ("'divergent()' is deprecated, "
773 "use 'contentdivergent()'")
773 "use 'contentdivergent()'")
774 repo.ui.deprecwarn(msg, '4.4')
774 repo.ui.deprecwarn(msg, '4.4')
775
775
776 return contentdivergent(repo, subset, x)
776 return contentdivergent(repo, subset, x)
777
777
778 @predicate('contentdivergent()', safe=True)
778 @predicate('contentdivergent()', safe=True)
779 def contentdivergent(repo, subset, x):
779 def contentdivergent(repo, subset, x):
780 """
780 """
781 Final successors of changesets with an alternative set of final
781 Final successors of changesets with an alternative set of final
782 successors. (EXPERIMENTAL)
782 successors. (EXPERIMENTAL)
783 """
783 """
784 # i18n: "contentdivergent" is a keyword
784 # i18n: "contentdivergent" is a keyword
785 getargs(x, 0, 0, _("contentdivergent takes no arguments"))
785 getargs(x, 0, 0, _("contentdivergent takes no arguments"))
786 contentdivergent = obsmod.getrevs(repo, 'contentdivergent')
786 contentdivergent = obsmod.getrevs(repo, 'contentdivergent')
787 return subset & contentdivergent
787 return subset & contentdivergent
788
788
789 @predicate('extinct()', safe=True)
789 @predicate('extinct()', safe=True)
790 def extinct(repo, subset, x):
790 def extinct(repo, subset, x):
791 """Obsolete changesets with obsolete descendants only.
791 """Obsolete changesets with obsolete descendants only.
792 """
792 """
793 # i18n: "extinct" is a keyword
793 # i18n: "extinct" is a keyword
794 getargs(x, 0, 0, _("extinct takes no arguments"))
794 getargs(x, 0, 0, _("extinct takes no arguments"))
795 extincts = obsmod.getrevs(repo, 'extinct')
795 extincts = obsmod.getrevs(repo, 'extinct')
796 return subset & extincts
796 return subset & extincts
797
797
798 @predicate('extra(label, [value])', safe=True)
798 @predicate('extra(label, [value])', safe=True)
799 def extra(repo, subset, x):
799 def extra(repo, subset, x):
800 """Changesets with the given label in the extra metadata, with the given
800 """Changesets with the given label in the extra metadata, with the given
801 optional value.
801 optional value.
802
802
803 Pattern matching is supported for `value`. See
803 Pattern matching is supported for `value`. See
804 :hg:`help revisions.patterns`.
804 :hg:`help revisions.patterns`.
805 """
805 """
806 args = getargsdict(x, 'extra', 'label value')
806 args = getargsdict(x, 'extra', 'label value')
807 if 'label' not in args:
807 if 'label' not in args:
808 # i18n: "extra" is a keyword
808 # i18n: "extra" is a keyword
809 raise error.ParseError(_('extra takes at least 1 argument'))
809 raise error.ParseError(_('extra takes at least 1 argument'))
810 # i18n: "extra" is a keyword
810 # i18n: "extra" is a keyword
811 label = getstring(args['label'], _('first argument to extra must be '
811 label = getstring(args['label'], _('first argument to extra must be '
812 'a string'))
812 'a string'))
813 value = None
813 value = None
814
814
815 if 'value' in args:
815 if 'value' in args:
816 # i18n: "extra" is a keyword
816 # i18n: "extra" is a keyword
817 value = getstring(args['value'], _('second argument to extra must be '
817 value = getstring(args['value'], _('second argument to extra must be '
818 'a string'))
818 'a string'))
819 kind, value, matcher = util.stringmatcher(value)
819 kind, value, matcher = util.stringmatcher(value)
820
820
821 def _matchvalue(r):
821 def _matchvalue(r):
822 extra = repo[r].extra()
822 extra = repo[r].extra()
823 return label in extra and (value is None or matcher(extra[label]))
823 return label in extra and (value is None or matcher(extra[label]))
824
824
825 return subset.filter(lambda r: _matchvalue(r),
825 return subset.filter(lambda r: _matchvalue(r),
826 condrepr=('<extra[%r] %r>', label, value))
826 condrepr=('<extra[%r] %r>', label, value))
827
827
828 @predicate('filelog(pattern)', safe=True)
828 @predicate('filelog(pattern)', safe=True)
829 def filelog(repo, subset, x):
829 def filelog(repo, subset, x):
830 """Changesets connected to the specified filelog.
830 """Changesets connected to the specified filelog.
831
831
832 For performance reasons, visits only revisions mentioned in the file-level
832 For performance reasons, visits only revisions mentioned in the file-level
833 filelog, rather than filtering through all changesets (much faster, but
833 filelog, rather than filtering through all changesets (much faster, but
834 doesn't include deletes or duplicate changes). For a slower, more accurate
834 doesn't include deletes or duplicate changes). For a slower, more accurate
835 result, use ``file()``.
835 result, use ``file()``.
836
836
837 The pattern without explicit kind like ``glob:`` is expected to be
837 The pattern without explicit kind like ``glob:`` is expected to be
838 relative to the current directory and match against a file exactly
838 relative to the current directory and match against a file exactly
839 for efficiency.
839 for efficiency.
840
840
841 If some linkrev points to revisions filtered by the current repoview, we'll
841 If some linkrev points to revisions filtered by the current repoview, we'll
842 work around it to return a non-filtered value.
842 work around it to return a non-filtered value.
843 """
843 """
844
844
845 # i18n: "filelog" is a keyword
845 # i18n: "filelog" is a keyword
846 pat = getstring(x, _("filelog requires a pattern"))
846 pat = getstring(x, _("filelog requires a pattern"))
847 s = set()
847 s = set()
848 cl = repo.changelog
848 cl = repo.changelog
849
849
850 if not matchmod.patkind(pat):
850 if not matchmod.patkind(pat):
851 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
851 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
852 files = [f]
852 files = [f]
853 else:
853 else:
854 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
854 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
855 files = (f for f in repo[None] if m(f))
855 files = (f for f in repo[None] if m(f))
856
856
857 for f in files:
857 for f in files:
858 fl = repo.file(f)
858 fl = repo.file(f)
859 known = {}
859 known = {}
860 scanpos = 0
860 scanpos = 0
861 for fr in list(fl):
861 for fr in list(fl):
862 fn = fl.node(fr)
862 fn = fl.node(fr)
863 if fn in known:
863 if fn in known:
864 s.add(known[fn])
864 s.add(known[fn])
865 continue
865 continue
866
866
867 lr = fl.linkrev(fr)
867 lr = fl.linkrev(fr)
868 if lr in cl:
868 if lr in cl:
869 s.add(lr)
869 s.add(lr)
870 elif scanpos is not None:
870 elif scanpos is not None:
871 # lowest matching changeset is filtered, scan further
871 # lowest matching changeset is filtered, scan further
872 # ahead in changelog
872 # ahead in changelog
873 start = max(lr, scanpos) + 1
873 start = max(lr, scanpos) + 1
874 scanpos = None
874 scanpos = None
875 for r in cl.revs(start):
875 for r in cl.revs(start):
876 # minimize parsing of non-matching entries
876 # minimize parsing of non-matching entries
877 if f in cl.revision(r) and f in cl.readfiles(r):
877 if f in cl.revision(r) and f in cl.readfiles(r):
878 try:
878 try:
879 # try to use manifest delta fastpath
879 # try to use manifest delta fastpath
880 n = repo[r].filenode(f)
880 n = repo[r].filenode(f)
881 if n not in known:
881 if n not in known:
882 if n == fn:
882 if n == fn:
883 s.add(r)
883 s.add(r)
884 scanpos = r
884 scanpos = r
885 break
885 break
886 else:
886 else:
887 known[n] = r
887 known[n] = r
888 except error.ManifestLookupError:
888 except error.ManifestLookupError:
889 # deletion in changelog
889 # deletion in changelog
890 continue
890 continue
891
891
892 return subset & s
892 return subset & s
893
893
894 @predicate('first(set, [n])', safe=True, takeorder=True)
894 @predicate('first(set, [n])', safe=True, takeorder=True)
895 def first(repo, subset, x, order):
895 def first(repo, subset, x, order):
896 """An alias for limit().
896 """An alias for limit().
897 """
897 """
898 return limit(repo, subset, x, order)
898 return limit(repo, subset, x, order)
899
899
900 def _follow(repo, subset, x, name, followfirst=False):
900 def _follow(repo, subset, x, name, followfirst=False):
901 l = getargs(x, 0, 2, _("%s takes no arguments or a pattern "
901 l = getargs(x, 0, 2, _("%s takes no arguments or a pattern "
902 "and an optional revset") % name)
902 "and an optional revset") % name)
903 c = repo['.']
903 c = repo['.']
904 if l:
904 if l:
905 x = getstring(l[0], _("%s expected a pattern") % name)
905 x = getstring(l[0], _("%s expected a pattern") % name)
906 rev = None
906 rev = None
907 if len(l) >= 2:
907 if len(l) >= 2:
908 revs = getset(repo, fullreposet(repo), l[1])
908 revs = getset(repo, fullreposet(repo), l[1])
909 if len(revs) != 1:
909 if len(revs) != 1:
910 raise error.RepoLookupError(
910 raise error.RepoLookupError(
911 _("%s expected one starting revision") % name)
911 _("%s expected one starting revision") % name)
912 rev = revs.last()
912 rev = revs.last()
913 c = repo[rev]
913 c = repo[rev]
914 matcher = matchmod.match(repo.root, repo.getcwd(), [x],
914 matcher = matchmod.match(repo.root, repo.getcwd(), [x],
915 ctx=repo[rev], default='path')
915 ctx=repo[rev], default='path')
916
916
917 files = c.manifest().walk(matcher)
917 files = c.manifest().walk(matcher)
918
918
919 s = set()
919 s = set()
920 for fname in files:
920 for fname in files:
921 fctx = c[fname]
921 fctx = c[fname]
922 s = s.union(set(c.rev() for c in fctx.ancestors(followfirst)))
922 s = s.union(set(c.rev() for c in fctx.ancestors(followfirst)))
923 # include the revision responsible for the most recent version
923 # include the revision responsible for the most recent version
924 s.add(fctx.introrev())
924 s.add(fctx.introrev())
925 else:
925 else:
926 s = dagop.revancestors(repo, baseset([c.rev()]), followfirst)
926 s = dagop.revancestors(repo, baseset([c.rev()]), followfirst)
927
927
928 return subset & s
928 return subset & s
929
929
930 @predicate('follow([pattern[, startrev]])', safe=True)
930 @predicate('follow([pattern[, startrev]])', safe=True)
931 def follow(repo, subset, x):
931 def follow(repo, subset, x):
932 """
932 """
933 An alias for ``::.`` (ancestors of the working directory's first parent).
933 An alias for ``::.`` (ancestors of the working directory's first parent).
934 If pattern is specified, the histories of files matching given
934 If pattern is specified, the histories of files matching given
935 pattern in the revision given by startrev are followed, including copies.
935 pattern in the revision given by startrev are followed, including copies.
936 """
936 """
937 return _follow(repo, subset, x, 'follow')
937 return _follow(repo, subset, x, 'follow')
938
938
939 @predicate('_followfirst', safe=True)
939 @predicate('_followfirst', safe=True)
940 def _followfirst(repo, subset, x):
940 def _followfirst(repo, subset, x):
941 # ``followfirst([pattern[, startrev]])``
941 # ``followfirst([pattern[, startrev]])``
942 # Like ``follow([pattern[, startrev]])`` but follows only the first parent
942 # Like ``follow([pattern[, startrev]])`` but follows only the first parent
943 # of every revisions or files revisions.
943 # of every revisions or files revisions.
944 return _follow(repo, subset, x, '_followfirst', followfirst=True)
944 return _follow(repo, subset, x, '_followfirst', followfirst=True)
945
945
946 @predicate('followlines(file, fromline:toline[, startrev=., descend=False])',
946 @predicate('followlines(file, fromline:toline[, startrev=., descend=False])',
947 safe=True)
947 safe=True)
948 def followlines(repo, subset, x):
948 def followlines(repo, subset, x):
949 """Changesets modifying `file` in line range ('fromline', 'toline').
949 """Changesets modifying `file` in line range ('fromline', 'toline').
950
950
951 Line range corresponds to 'file' content at 'startrev' and should hence be
951 Line range corresponds to 'file' content at 'startrev' and should hence be
952 consistent with file size. If startrev is not specified, working directory's
952 consistent with file size. If startrev is not specified, working directory's
953 parent is used.
953 parent is used.
954
954
955 By default, ancestors of 'startrev' are returned. If 'descend' is True,
955 By default, ancestors of 'startrev' are returned. If 'descend' is True,
956 descendants of 'startrev' are returned though renames are (currently) not
956 descendants of 'startrev' are returned though renames are (currently) not
957 followed in this direction.
957 followed in this direction.
958 """
958 """
959 args = getargsdict(x, 'followlines', 'file *lines startrev descend')
959 args = getargsdict(x, 'followlines', 'file *lines startrev descend')
960 if len(args['lines']) != 1:
960 if len(args['lines']) != 1:
961 raise error.ParseError(_("followlines requires a line range"))
961 raise error.ParseError(_("followlines requires a line range"))
962
962
963 rev = '.'
963 rev = '.'
964 if 'startrev' in args:
964 if 'startrev' in args:
965 revs = getset(repo, fullreposet(repo), args['startrev'])
965 revs = getset(repo, fullreposet(repo), args['startrev'])
966 if len(revs) != 1:
966 if len(revs) != 1:
967 raise error.ParseError(
967 raise error.ParseError(
968 # i18n: "followlines" is a keyword
968 # i18n: "followlines" is a keyword
969 _("followlines expects exactly one revision"))
969 _("followlines expects exactly one revision"))
970 rev = revs.last()
970 rev = revs.last()
971
971
972 pat = getstring(args['file'], _("followlines requires a pattern"))
972 pat = getstring(args['file'], _("followlines requires a pattern"))
973 if not matchmod.patkind(pat):
973 if not matchmod.patkind(pat):
974 fname = pathutil.canonpath(repo.root, repo.getcwd(), pat)
974 fname = pathutil.canonpath(repo.root, repo.getcwd(), pat)
975 else:
975 else:
976 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[rev])
976 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[rev])
977 files = [f for f in repo[rev] if m(f)]
977 files = [f for f in repo[rev] if m(f)]
978 if len(files) != 1:
978 if len(files) != 1:
979 # i18n: "followlines" is a keyword
979 # i18n: "followlines" is a keyword
980 raise error.ParseError(_("followlines expects exactly one file"))
980 raise error.ParseError(_("followlines expects exactly one file"))
981 fname = files[0]
981 fname = files[0]
982
982
983 # i18n: "followlines" is a keyword
983 # i18n: "followlines" is a keyword
984 lr = getrange(args['lines'][0], _("followlines expects a line range"))
984 lr = getrange(args['lines'][0], _("followlines expects a line range"))
985 fromline, toline = [getinteger(a, _("line range bounds must be integers"))
985 fromline, toline = [getinteger(a, _("line range bounds must be integers"))
986 for a in lr]
986 for a in lr]
987 fromline, toline = util.processlinerange(fromline, toline)
987 fromline, toline = util.processlinerange(fromline, toline)
988
988
989 fctx = repo[rev].filectx(fname)
989 fctx = repo[rev].filectx(fname)
990 descend = False
990 descend = False
991 if 'descend' in args:
991 if 'descend' in args:
992 descend = getboolean(args['descend'],
992 descend = getboolean(args['descend'],
993 # i18n: "descend" is a keyword
993 # i18n: "descend" is a keyword
994 _("descend argument must be a boolean"))
994 _("descend argument must be a boolean"))
995 if descend:
995 if descend:
996 rs = generatorset(
996 rs = generatorset(
997 (c.rev() for c, _linerange
997 (c.rev() for c, _linerange
998 in dagop.blockdescendants(fctx, fromline, toline)),
998 in dagop.blockdescendants(fctx, fromline, toline)),
999 iterasc=True)
999 iterasc=True)
1000 else:
1000 else:
1001 rs = generatorset(
1001 rs = generatorset(
1002 (c.rev() for c, _linerange
1002 (c.rev() for c, _linerange
1003 in dagop.blockancestors(fctx, fromline, toline)),
1003 in dagop.blockancestors(fctx, fromline, toline)),
1004 iterasc=False)
1004 iterasc=False)
1005 return subset & rs
1005 return subset & rs
1006
1006
1007 @predicate('all()', safe=True)
1007 @predicate('all()', safe=True)
1008 def getall(repo, subset, x):
1008 def getall(repo, subset, x):
1009 """All changesets, the same as ``0:tip``.
1009 """All changesets, the same as ``0:tip``.
1010 """
1010 """
1011 # i18n: "all" is a keyword
1011 # i18n: "all" is a keyword
1012 getargs(x, 0, 0, _("all takes no arguments"))
1012 getargs(x, 0, 0, _("all takes no arguments"))
1013 return subset & spanset(repo) # drop "null" if any
1013 return subset & spanset(repo) # drop "null" if any
1014
1014
1015 @predicate('grep(regex)')
1015 @predicate('grep(regex)')
1016 def grep(repo, subset, x):
1016 def grep(repo, subset, x):
1017 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1017 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1018 to ensure special escape characters are handled correctly. Unlike
1018 to ensure special escape characters are handled correctly. Unlike
1019 ``keyword(string)``, the match is case-sensitive.
1019 ``keyword(string)``, the match is case-sensitive.
1020 """
1020 """
1021 try:
1021 try:
1022 # i18n: "grep" is a keyword
1022 # i18n: "grep" is a keyword
1023 gr = re.compile(getstring(x, _("grep requires a string")))
1023 gr = re.compile(getstring(x, _("grep requires a string")))
1024 except re.error as e:
1024 except re.error as e:
1025 raise error.ParseError(_('invalid match pattern: %s') % e)
1025 raise error.ParseError(_('invalid match pattern: %s') % e)
1026
1026
1027 def matches(x):
1027 def matches(x):
1028 c = repo[x]
1028 c = repo[x]
1029 for e in c.files() + [c.user(), c.description()]:
1029 for e in c.files() + [c.user(), c.description()]:
1030 if gr.search(e):
1030 if gr.search(e):
1031 return True
1031 return True
1032 return False
1032 return False
1033
1033
1034 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1034 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1035
1035
1036 @predicate('_matchfiles', safe=True)
1036 @predicate('_matchfiles', safe=True)
1037 def _matchfiles(repo, subset, x):
1037 def _matchfiles(repo, subset, x):
1038 # _matchfiles takes a revset list of prefixed arguments:
1038 # _matchfiles takes a revset list of prefixed arguments:
1039 #
1039 #
1040 # [p:foo, i:bar, x:baz]
1040 # [p:foo, i:bar, x:baz]
1041 #
1041 #
1042 # builds a match object from them and filters subset. Allowed
1042 # builds a match object from them and filters subset. Allowed
1043 # prefixes are 'p:' for regular patterns, 'i:' for include
1043 # prefixes are 'p:' for regular patterns, 'i:' for include
1044 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1044 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1045 # a revision identifier, or the empty string to reference the
1045 # a revision identifier, or the empty string to reference the
1046 # working directory, from which the match object is
1046 # working directory, from which the match object is
1047 # initialized. Use 'd:' to set the default matching mode, default
1047 # initialized. Use 'd:' to set the default matching mode, default
1048 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1048 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1049
1049
1050 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1050 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1051 pats, inc, exc = [], [], []
1051 pats, inc, exc = [], [], []
1052 rev, default = None, None
1052 rev, default = None, None
1053 for arg in l:
1053 for arg in l:
1054 s = getstring(arg, "_matchfiles requires string arguments")
1054 s = getstring(arg, "_matchfiles requires string arguments")
1055 prefix, value = s[:2], s[2:]
1055 prefix, value = s[:2], s[2:]
1056 if prefix == 'p:':
1056 if prefix == 'p:':
1057 pats.append(value)
1057 pats.append(value)
1058 elif prefix == 'i:':
1058 elif prefix == 'i:':
1059 inc.append(value)
1059 inc.append(value)
1060 elif prefix == 'x:':
1060 elif prefix == 'x:':
1061 exc.append(value)
1061 exc.append(value)
1062 elif prefix == 'r:':
1062 elif prefix == 'r:':
1063 if rev is not None:
1063 if rev is not None:
1064 raise error.ParseError('_matchfiles expected at most one '
1064 raise error.ParseError('_matchfiles expected at most one '
1065 'revision')
1065 'revision')
1066 if value != '': # empty means working directory; leave rev as None
1066 if value != '': # empty means working directory; leave rev as None
1067 rev = value
1067 rev = value
1068 elif prefix == 'd:':
1068 elif prefix == 'd:':
1069 if default is not None:
1069 if default is not None:
1070 raise error.ParseError('_matchfiles expected at most one '
1070 raise error.ParseError('_matchfiles expected at most one '
1071 'default mode')
1071 'default mode')
1072 default = value
1072 default = value
1073 else:
1073 else:
1074 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1074 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1075 if not default:
1075 if not default:
1076 default = 'glob'
1076 default = 'glob'
1077
1077
1078 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1078 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1079 exclude=exc, ctx=repo[rev], default=default)
1079 exclude=exc, ctx=repo[rev], default=default)
1080
1080
1081 # This directly read the changelog data as creating changectx for all
1081 # This directly read the changelog data as creating changectx for all
1082 # revisions is quite expensive.
1082 # revisions is quite expensive.
1083 getfiles = repo.changelog.readfiles
1083 getfiles = repo.changelog.readfiles
1084 wdirrev = node.wdirrev
1084 wdirrev = node.wdirrev
1085 def matches(x):
1085 def matches(x):
1086 if x == wdirrev:
1086 if x == wdirrev:
1087 files = repo[x].files()
1087 files = repo[x].files()
1088 else:
1088 else:
1089 files = getfiles(x)
1089 files = getfiles(x)
1090 for f in files:
1090 for f in files:
1091 if m(f):
1091 if m(f):
1092 return True
1092 return True
1093 return False
1093 return False
1094
1094
1095 return subset.filter(matches,
1095 return subset.filter(matches,
1096 condrepr=('<matchfiles patterns=%r, include=%r '
1096 condrepr=('<matchfiles patterns=%r, include=%r '
1097 'exclude=%r, default=%r, rev=%r>',
1097 'exclude=%r, default=%r, rev=%r>',
1098 pats, inc, exc, default, rev))
1098 pats, inc, exc, default, rev))
1099
1099
1100 @predicate('file(pattern)', safe=True)
1100 @predicate('file(pattern)', safe=True)
1101 def hasfile(repo, subset, x):
1101 def hasfile(repo, subset, x):
1102 """Changesets affecting files matched by pattern.
1102 """Changesets affecting files matched by pattern.
1103
1103
1104 For a faster but less accurate result, consider using ``filelog()``
1104 For a faster but less accurate result, consider using ``filelog()``
1105 instead.
1105 instead.
1106
1106
1107 This predicate uses ``glob:`` as the default kind of pattern.
1107 This predicate uses ``glob:`` as the default kind of pattern.
1108 """
1108 """
1109 # i18n: "file" is a keyword
1109 # i18n: "file" is a keyword
1110 pat = getstring(x, _("file requires a pattern"))
1110 pat = getstring(x, _("file requires a pattern"))
1111 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1111 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1112
1112
1113 @predicate('head()', safe=True)
1113 @predicate('head()', safe=True)
1114 def head(repo, subset, x):
1114 def head(repo, subset, x):
1115 """Changeset is a named branch head.
1115 """Changeset is a named branch head.
1116 """
1116 """
1117 # i18n: "head" is a keyword
1117 # i18n: "head" is a keyword
1118 getargs(x, 0, 0, _("head takes no arguments"))
1118 getargs(x, 0, 0, _("head takes no arguments"))
1119 hs = set()
1119 hs = set()
1120 cl = repo.changelog
1120 cl = repo.changelog
1121 for ls in repo.branchmap().itervalues():
1121 for ls in repo.branchmap().itervalues():
1122 hs.update(cl.rev(h) for h in ls)
1122 hs.update(cl.rev(h) for h in ls)
1123 return subset & baseset(hs)
1123 return subset & baseset(hs)
1124
1124
1125 @predicate('heads(set)', safe=True)
1125 @predicate('heads(set)', safe=True)
1126 def heads(repo, subset, x):
1126 def heads(repo, subset, x):
1127 """Members of set with no children in set.
1127 """Members of set with no children in set.
1128 """
1128 """
1129 s = getset(repo, subset, x)
1129 s = getset(repo, subset, x)
1130 ps = parents(repo, subset, x)
1130 ps = parents(repo, subset, x)
1131 return s - ps
1131 return s - ps
1132
1132
1133 @predicate('hidden()', safe=True)
1133 @predicate('hidden()', safe=True)
1134 def hidden(repo, subset, x):
1134 def hidden(repo, subset, x):
1135 """Hidden changesets.
1135 """Hidden changesets.
1136 """
1136 """
1137 # i18n: "hidden" is a keyword
1137 # i18n: "hidden" is a keyword
1138 getargs(x, 0, 0, _("hidden takes no arguments"))
1138 getargs(x, 0, 0, _("hidden takes no arguments"))
1139 hiddenrevs = repoview.filterrevs(repo, 'visible')
1139 hiddenrevs = repoview.filterrevs(repo, 'visible')
1140 return subset & hiddenrevs
1140 return subset & hiddenrevs
1141
1141
1142 @predicate('keyword(string)', safe=True)
1142 @predicate('keyword(string)', safe=True)
1143 def keyword(repo, subset, x):
1143 def keyword(repo, subset, x):
1144 """Search commit message, user name, and names of changed files for
1144 """Search commit message, user name, and names of changed files for
1145 string. The match is case-insensitive.
1145 string. The match is case-insensitive.
1146
1146
1147 For a regular expression or case sensitive search of these fields, use
1147 For a regular expression or case sensitive search of these fields, use
1148 ``grep(regex)``.
1148 ``grep(regex)``.
1149 """
1149 """
1150 # i18n: "keyword" is a keyword
1150 # i18n: "keyword" is a keyword
1151 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1151 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1152
1152
1153 def matches(r):
1153 def matches(r):
1154 c = repo[r]
1154 c = repo[r]
1155 return any(kw in encoding.lower(t)
1155 return any(kw in encoding.lower(t)
1156 for t in c.files() + [c.user(), c.description()])
1156 for t in c.files() + [c.user(), c.description()])
1157
1157
1158 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1158 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1159
1159
1160 @predicate('limit(set[, n[, offset]])', safe=True, takeorder=True)
1160 @predicate('limit(set[, n[, offset]])', safe=True, takeorder=True)
1161 def limit(repo, subset, x, order):
1161 def limit(repo, subset, x, order):
1162 """First n members of set, defaulting to 1, starting from offset.
1162 """First n members of set, defaulting to 1, starting from offset.
1163 """
1163 """
1164 args = getargsdict(x, 'limit', 'set n offset')
1164 args = getargsdict(x, 'limit', 'set n offset')
1165 if 'set' not in args:
1165 if 'set' not in args:
1166 # i18n: "limit" is a keyword
1166 # i18n: "limit" is a keyword
1167 raise error.ParseError(_("limit requires one to three arguments"))
1167 raise error.ParseError(_("limit requires one to three arguments"))
1168 # i18n: "limit" is a keyword
1168 # i18n: "limit" is a keyword
1169 lim = getinteger(args.get('n'), _("limit expects a number"), default=1)
1169 lim = getinteger(args.get('n'), _("limit expects a number"), default=1)
1170 if lim < 0:
1170 if lim < 0:
1171 raise error.ParseError(_("negative number to select"))
1171 raise error.ParseError(_("negative number to select"))
1172 # i18n: "limit" is a keyword
1172 # i18n: "limit" is a keyword
1173 ofs = getinteger(args.get('offset'), _("limit expects a number"), default=0)
1173 ofs = getinteger(args.get('offset'), _("limit expects a number"), default=0)
1174 if ofs < 0:
1174 if ofs < 0:
1175 raise error.ParseError(_("negative offset"))
1175 raise error.ParseError(_("negative offset"))
1176 os = getset(repo, fullreposet(repo), args['set'])
1176 os = getset(repo, fullreposet(repo), args['set'])
1177 ls = os.slice(ofs, ofs + lim)
1177 ls = os.slice(ofs, ofs + lim)
1178 if order == followorder and lim > 1:
1178 if order == followorder and lim > 1:
1179 return subset & ls
1179 return subset & ls
1180 return ls & subset
1180 return ls & subset
1181
1181
1182 @predicate('last(set, [n])', safe=True, takeorder=True)
1182 @predicate('last(set, [n])', safe=True, takeorder=True)
1183 def last(repo, subset, x, order):
1183 def last(repo, subset, x, order):
1184 """Last n members of set, defaulting to 1.
1184 """Last n members of set, defaulting to 1.
1185 """
1185 """
1186 # i18n: "last" is a keyword
1186 # i18n: "last" is a keyword
1187 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1187 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1188 lim = 1
1188 lim = 1
1189 if len(l) == 2:
1189 if len(l) == 2:
1190 # i18n: "last" is a keyword
1190 # i18n: "last" is a keyword
1191 lim = getinteger(l[1], _("last expects a number"))
1191 lim = getinteger(l[1], _("last expects a number"))
1192 if lim < 0:
1192 if lim < 0:
1193 raise error.ParseError(_("negative number to select"))
1193 raise error.ParseError(_("negative number to select"))
1194 os = getset(repo, fullreposet(repo), l[0])
1194 os = getset(repo, fullreposet(repo), l[0])
1195 os.reverse()
1195 os.reverse()
1196 ls = os.slice(0, lim)
1196 ls = os.slice(0, lim)
1197 if order == followorder and lim > 1:
1197 if order == followorder and lim > 1:
1198 return subset & ls
1198 return subset & ls
1199 ls.reverse()
1199 ls.reverse()
1200 return ls & subset
1200 return ls & subset
1201
1201
1202 @predicate('max(set)', safe=True)
1202 @predicate('max(set)', safe=True)
1203 def maxrev(repo, subset, x):
1203 def maxrev(repo, subset, x):
1204 """Changeset with highest revision number in set.
1204 """Changeset with highest revision number in set.
1205 """
1205 """
1206 os = getset(repo, fullreposet(repo), x)
1206 os = getset(repo, fullreposet(repo), x)
1207 try:
1207 try:
1208 m = os.max()
1208 m = os.max()
1209 if m in subset:
1209 if m in subset:
1210 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1210 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1211 except ValueError:
1211 except ValueError:
1212 # os.max() throws a ValueError when the collection is empty.
1212 # os.max() throws a ValueError when the collection is empty.
1213 # Same as python's max().
1213 # Same as python's max().
1214 pass
1214 pass
1215 return baseset(datarepr=('<max %r, %r>', subset, os))
1215 return baseset(datarepr=('<max %r, %r>', subset, os))
1216
1216
1217 @predicate('merge()', safe=True)
1217 @predicate('merge()', safe=True)
1218 def merge(repo, subset, x):
1218 def merge(repo, subset, x):
1219 """Changeset is a merge changeset.
1219 """Changeset is a merge changeset.
1220 """
1220 """
1221 # i18n: "merge" is a keyword
1221 # i18n: "merge" is a keyword
1222 getargs(x, 0, 0, _("merge takes no arguments"))
1222 getargs(x, 0, 0, _("merge takes no arguments"))
1223 cl = repo.changelog
1223 cl = repo.changelog
1224 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1224 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1225 condrepr='<merge>')
1225 condrepr='<merge>')
1226
1226
1227 @predicate('branchpoint()', safe=True)
1227 @predicate('branchpoint()', safe=True)
1228 def branchpoint(repo, subset, x):
1228 def branchpoint(repo, subset, x):
1229 """Changesets with more than one child.
1229 """Changesets with more than one child.
1230 """
1230 """
1231 # i18n: "branchpoint" is a keyword
1231 # i18n: "branchpoint" is a keyword
1232 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1232 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1233 cl = repo.changelog
1233 cl = repo.changelog
1234 if not subset:
1234 if not subset:
1235 return baseset()
1235 return baseset()
1236 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1236 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1237 # (and if it is not, it should.)
1237 # (and if it is not, it should.)
1238 baserev = min(subset)
1238 baserev = min(subset)
1239 parentscount = [0]*(len(repo) - baserev)
1239 parentscount = [0]*(len(repo) - baserev)
1240 for r in cl.revs(start=baserev + 1):
1240 for r in cl.revs(start=baserev + 1):
1241 for p in cl.parentrevs(r):
1241 for p in cl.parentrevs(r):
1242 if p >= baserev:
1242 if p >= baserev:
1243 parentscount[p - baserev] += 1
1243 parentscount[p - baserev] += 1
1244 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1244 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1245 condrepr='<branchpoint>')
1245 condrepr='<branchpoint>')
1246
1246
1247 @predicate('min(set)', safe=True)
1247 @predicate('min(set)', safe=True)
1248 def minrev(repo, subset, x):
1248 def minrev(repo, subset, x):
1249 """Changeset with lowest revision number in set.
1249 """Changeset with lowest revision number in set.
1250 """
1250 """
1251 os = getset(repo, fullreposet(repo), x)
1251 os = getset(repo, fullreposet(repo), x)
1252 try:
1252 try:
1253 m = os.min()
1253 m = os.min()
1254 if m in subset:
1254 if m in subset:
1255 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1255 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1256 except ValueError:
1256 except ValueError:
1257 # os.min() throws a ValueError when the collection is empty.
1257 # os.min() throws a ValueError when the collection is empty.
1258 # Same as python's min().
1258 # Same as python's min().
1259 pass
1259 pass
1260 return baseset(datarepr=('<min %r, %r>', subset, os))
1260 return baseset(datarepr=('<min %r, %r>', subset, os))
1261
1261
1262 @predicate('modifies(pattern)', safe=True)
1262 @predicate('modifies(pattern)', safe=True)
1263 def modifies(repo, subset, x):
1263 def modifies(repo, subset, x):
1264 """Changesets modifying files matched by pattern.
1264 """Changesets modifying files matched by pattern.
1265
1265
1266 The pattern without explicit kind like ``glob:`` is expected to be
1266 The pattern without explicit kind like ``glob:`` is expected to be
1267 relative to the current directory and match against a file or a
1267 relative to the current directory and match against a file or a
1268 directory.
1268 directory.
1269 """
1269 """
1270 # i18n: "modifies" is a keyword
1270 # i18n: "modifies" is a keyword
1271 pat = getstring(x, _("modifies requires a pattern"))
1271 pat = getstring(x, _("modifies requires a pattern"))
1272 return checkstatus(repo, subset, pat, 0)
1272 return checkstatus(repo, subset, pat, 0)
1273
1273
1274 @predicate('named(namespace)')
1274 @predicate('named(namespace)')
1275 def named(repo, subset, x):
1275 def named(repo, subset, x):
1276 """The changesets in a given namespace.
1276 """The changesets in a given namespace.
1277
1277
1278 Pattern matching is supported for `namespace`. See
1278 Pattern matching is supported for `namespace`. See
1279 :hg:`help revisions.patterns`.
1279 :hg:`help revisions.patterns`.
1280 """
1280 """
1281 # i18n: "named" is a keyword
1281 # i18n: "named" is a keyword
1282 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1282 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1283
1283
1284 ns = getstring(args[0],
1284 ns = getstring(args[0],
1285 # i18n: "named" is a keyword
1285 # i18n: "named" is a keyword
1286 _('the argument to named must be a string'))
1286 _('the argument to named must be a string'))
1287 kind, pattern, matcher = util.stringmatcher(ns)
1287 kind, pattern, matcher = util.stringmatcher(ns)
1288 namespaces = set()
1288 namespaces = set()
1289 if kind == 'literal':
1289 if kind == 'literal':
1290 if pattern not in repo.names:
1290 if pattern not in repo.names:
1291 raise error.RepoLookupError(_("namespace '%s' does not exist")
1291 raise error.RepoLookupError(_("namespace '%s' does not exist")
1292 % ns)
1292 % ns)
1293 namespaces.add(repo.names[pattern])
1293 namespaces.add(repo.names[pattern])
1294 else:
1294 else:
1295 for name, ns in repo.names.iteritems():
1295 for name, ns in repo.names.iteritems():
1296 if matcher(name):
1296 if matcher(name):
1297 namespaces.add(ns)
1297 namespaces.add(ns)
1298 if not namespaces:
1298 if not namespaces:
1299 raise error.RepoLookupError(_("no namespace exists"
1299 raise error.RepoLookupError(_("no namespace exists"
1300 " that match '%s'") % pattern)
1300 " that match '%s'") % pattern)
1301
1301
1302 names = set()
1302 names = set()
1303 for ns in namespaces:
1303 for ns in namespaces:
1304 for name in ns.listnames(repo):
1304 for name in ns.listnames(repo):
1305 if name not in ns.deprecated:
1305 if name not in ns.deprecated:
1306 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1306 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1307
1307
1308 names -= {node.nullrev}
1308 names -= {node.nullrev}
1309 return subset & names
1309 return subset & names
1310
1310
1311 @predicate('id(string)', safe=True)
1311 @predicate('id(string)', safe=True)
1312 def node_(repo, subset, x):
1312 def node_(repo, subset, x):
1313 """Revision non-ambiguously specified by the given hex string prefix.
1313 """Revision non-ambiguously specified by the given hex string prefix.
1314 """
1314 """
1315 # i18n: "id" is a keyword
1315 # i18n: "id" is a keyword
1316 l = getargs(x, 1, 1, _("id requires one argument"))
1316 l = getargs(x, 1, 1, _("id requires one argument"))
1317 # i18n: "id" is a keyword
1317 # i18n: "id" is a keyword
1318 n = getstring(l[0], _("id requires a string"))
1318 n = getstring(l[0], _("id requires a string"))
1319 if len(n) == 40:
1319 if len(n) == 40:
1320 try:
1320 try:
1321 rn = repo.changelog.rev(node.bin(n))
1321 rn = repo.changelog.rev(node.bin(n))
1322 except error.WdirUnsupported:
1322 except error.WdirUnsupported:
1323 rn = node.wdirrev
1323 rn = node.wdirrev
1324 except (LookupError, TypeError):
1324 except (LookupError, TypeError):
1325 rn = None
1325 rn = None
1326 else:
1326 else:
1327 rn = None
1327 rn = None
1328 try:
1328 try:
1329 pm = repo.changelog._partialmatch(n)
1329 pm = repo.changelog._partialmatch(n)
1330 if pm is not None:
1330 if pm is not None:
1331 rn = repo.changelog.rev(pm)
1331 rn = repo.changelog.rev(pm)
1332 except error.WdirUnsupported:
1332 except error.WdirUnsupported:
1333 rn = node.wdirrev
1333 rn = node.wdirrev
1334
1334
1335 if rn is None:
1335 if rn is None:
1336 return baseset()
1336 return baseset()
1337 result = baseset([rn])
1337 result = baseset([rn])
1338 return result & subset
1338 return result & subset
1339
1339
1340 @predicate('obsolete()', safe=True)
1340 @predicate('obsolete()', safe=True)
1341 def obsolete(repo, subset, x):
1341 def obsolete(repo, subset, x):
1342 """Mutable changeset with a newer version."""
1342 """Mutable changeset with a newer version."""
1343 # i18n: "obsolete" is a keyword
1343 # i18n: "obsolete" is a keyword
1344 getargs(x, 0, 0, _("obsolete takes no arguments"))
1344 getargs(x, 0, 0, _("obsolete takes no arguments"))
1345 obsoletes = obsmod.getrevs(repo, 'obsolete')
1345 obsoletes = obsmod.getrevs(repo, 'obsolete')
1346 return subset & obsoletes
1346 return subset & obsoletes
1347
1347
1348 @predicate('only(set, [set])', safe=True)
1348 @predicate('only(set, [set])', safe=True)
1349 def only(repo, subset, x):
1349 def only(repo, subset, x):
1350 """Changesets that are ancestors of the first set that are not ancestors
1350 """Changesets that are ancestors of the first set that are not ancestors
1351 of any other head in the repo. If a second set is specified, the result
1351 of any other head in the repo. If a second set is specified, the result
1352 is ancestors of the first set that are not ancestors of the second set
1352 is ancestors of the first set that are not ancestors of the second set
1353 (i.e. ::<set1> - ::<set2>).
1353 (i.e. ::<set1> - ::<set2>).
1354 """
1354 """
1355 cl = repo.changelog
1355 cl = repo.changelog
1356 # i18n: "only" is a keyword
1356 # i18n: "only" is a keyword
1357 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1357 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1358 include = getset(repo, fullreposet(repo), args[0])
1358 include = getset(repo, fullreposet(repo), args[0])
1359 if len(args) == 1:
1359 if len(args) == 1:
1360 if not include:
1360 if not include:
1361 return baseset()
1361 return baseset()
1362
1362
1363 descendants = set(dagop.revdescendants(repo, include, False))
1363 descendants = set(dagop.revdescendants(repo, include, False))
1364 exclude = [rev for rev in cl.headrevs()
1364 exclude = [rev for rev in cl.headrevs()
1365 if not rev in descendants and not rev in include]
1365 if not rev in descendants and not rev in include]
1366 else:
1366 else:
1367 exclude = getset(repo, fullreposet(repo), args[1])
1367 exclude = getset(repo, fullreposet(repo), args[1])
1368
1368
1369 results = set(cl.findmissingrevs(common=exclude, heads=include))
1369 results = set(cl.findmissingrevs(common=exclude, heads=include))
1370 # XXX we should turn this into a baseset instead of a set, smartset may do
1370 # XXX we should turn this into a baseset instead of a set, smartset may do
1371 # some optimizations from the fact this is a baseset.
1371 # some optimizations from the fact this is a baseset.
1372 return subset & results
1372 return subset & results
1373
1373
1374 @predicate('origin([set])', safe=True)
1374 @predicate('origin([set])', safe=True)
1375 def origin(repo, subset, x):
1375 def origin(repo, subset, x):
1376 """
1376 """
1377 Changesets that were specified as a source for the grafts, transplants or
1377 Changesets that were specified as a source for the grafts, transplants or
1378 rebases that created the given revisions. Omitting the optional set is the
1378 rebases that created the given revisions. Omitting the optional set is the
1379 same as passing all(). If a changeset created by these operations is itself
1379 same as passing all(). If a changeset created by these operations is itself
1380 specified as a source for one of these operations, only the source changeset
1380 specified as a source for one of these operations, only the source changeset
1381 for the first operation is selected.
1381 for the first operation is selected.
1382 """
1382 """
1383 if x is not None:
1383 if x is not None:
1384 dests = getset(repo, fullreposet(repo), x)
1384 dests = getset(repo, fullreposet(repo), x)
1385 else:
1385 else:
1386 dests = fullreposet(repo)
1386 dests = fullreposet(repo)
1387
1387
1388 def _firstsrc(rev):
1388 def _firstsrc(rev):
1389 src = _getrevsource(repo, rev)
1389 src = _getrevsource(repo, rev)
1390 if src is None:
1390 if src is None:
1391 return None
1391 return None
1392
1392
1393 while True:
1393 while True:
1394 prev = _getrevsource(repo, src)
1394 prev = _getrevsource(repo, src)
1395
1395
1396 if prev is None:
1396 if prev is None:
1397 return src
1397 return src
1398 src = prev
1398 src = prev
1399
1399
1400 o = {_firstsrc(r) for r in dests}
1400 o = {_firstsrc(r) for r in dests}
1401 o -= {None}
1401 o -= {None}
1402 # XXX we should turn this into a baseset instead of a set, smartset may do
1402 # XXX we should turn this into a baseset instead of a set, smartset may do
1403 # some optimizations from the fact this is a baseset.
1403 # some optimizations from the fact this is a baseset.
1404 return subset & o
1404 return subset & o
1405
1405
1406 @predicate('outgoing([path])', safe=False)
1406 @predicate('outgoing([path])', safe=False)
1407 def outgoing(repo, subset, x):
1407 def outgoing(repo, subset, x):
1408 """Changesets not found in the specified destination repository, or the
1408 """Changesets not found in the specified destination repository, or the
1409 default push location.
1409 default push location.
1410 """
1410 """
1411 # Avoid cycles.
1411 # Avoid cycles.
1412 from . import (
1412 from . import (
1413 discovery,
1413 discovery,
1414 hg,
1414 hg,
1415 )
1415 )
1416 # i18n: "outgoing" is a keyword
1416 # i18n: "outgoing" is a keyword
1417 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1417 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1418 # i18n: "outgoing" is a keyword
1418 # i18n: "outgoing" is a keyword
1419 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1419 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1420 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1420 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1421 dest, branches = hg.parseurl(dest)
1421 dest, branches = hg.parseurl(dest)
1422 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1422 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1423 if revs:
1423 if revs:
1424 revs = [repo.lookup(rev) for rev in revs]
1424 revs = [repo.lookup(rev) for rev in revs]
1425 other = hg.peer(repo, {}, dest)
1425 other = hg.peer(repo, {}, dest)
1426 repo.ui.pushbuffer()
1426 repo.ui.pushbuffer()
1427 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1427 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1428 repo.ui.popbuffer()
1428 repo.ui.popbuffer()
1429 cl = repo.changelog
1429 cl = repo.changelog
1430 o = {cl.rev(r) for r in outgoing.missing}
1430 o = {cl.rev(r) for r in outgoing.missing}
1431 return subset & o
1431 return subset & o
1432
1432
1433 @predicate('p1([set])', safe=True)
1433 @predicate('p1([set])', safe=True)
1434 def p1(repo, subset, x):
1434 def p1(repo, subset, x):
1435 """First parent of changesets in set, or the working directory.
1435 """First parent of changesets in set, or the working directory.
1436 """
1436 """
1437 if x is None:
1437 if x is None:
1438 p = repo[x].p1().rev()
1438 p = repo[x].p1().rev()
1439 if p >= 0:
1439 if p >= 0:
1440 return subset & baseset([p])
1440 return subset & baseset([p])
1441 return baseset()
1441 return baseset()
1442
1442
1443 ps = set()
1443 ps = set()
1444 cl = repo.changelog
1444 cl = repo.changelog
1445 for r in getset(repo, fullreposet(repo), x):
1445 for r in getset(repo, fullreposet(repo), x):
1446 try:
1446 try:
1447 ps.add(cl.parentrevs(r)[0])
1447 ps.add(cl.parentrevs(r)[0])
1448 except error.WdirUnsupported:
1448 except error.WdirUnsupported:
1449 ps.add(repo[r].parents()[0].rev())
1449 ps.add(repo[r].parents()[0].rev())
1450 ps -= {node.nullrev}
1450 ps -= {node.nullrev}
1451 # XXX we should turn this into a baseset instead of a set, smartset may do
1451 # XXX we should turn this into a baseset instead of a set, smartset may do
1452 # some optimizations from the fact this is a baseset.
1452 # some optimizations from the fact this is a baseset.
1453 return subset & ps
1453 return subset & ps
1454
1454
1455 @predicate('p2([set])', safe=True)
1455 @predicate('p2([set])', safe=True)
1456 def p2(repo, subset, x):
1456 def p2(repo, subset, x):
1457 """Second parent of changesets in set, or the working directory.
1457 """Second parent of changesets in set, or the working directory.
1458 """
1458 """
1459 if x is None:
1459 if x is None:
1460 ps = repo[x].parents()
1460 ps = repo[x].parents()
1461 try:
1461 try:
1462 p = ps[1].rev()
1462 p = ps[1].rev()
1463 if p >= 0:
1463 if p >= 0:
1464 return subset & baseset([p])
1464 return subset & baseset([p])
1465 return baseset()
1465 return baseset()
1466 except IndexError:
1466 except IndexError:
1467 return baseset()
1467 return baseset()
1468
1468
1469 ps = set()
1469 ps = set()
1470 cl = repo.changelog
1470 cl = repo.changelog
1471 for r in getset(repo, fullreposet(repo), x):
1471 for r in getset(repo, fullreposet(repo), x):
1472 try:
1472 try:
1473 ps.add(cl.parentrevs(r)[1])
1473 ps.add(cl.parentrevs(r)[1])
1474 except error.WdirUnsupported:
1474 except error.WdirUnsupported:
1475 parents = repo[r].parents()
1475 parents = repo[r].parents()
1476 if len(parents) == 2:
1476 if len(parents) == 2:
1477 ps.add(parents[1])
1477 ps.add(parents[1])
1478 ps -= {node.nullrev}
1478 ps -= {node.nullrev}
1479 # XXX we should turn this into a baseset instead of a set, smartset may do
1479 # XXX we should turn this into a baseset instead of a set, smartset may do
1480 # some optimizations from the fact this is a baseset.
1480 # some optimizations from the fact this is a baseset.
1481 return subset & ps
1481 return subset & ps
1482
1482
1483 def parentpost(repo, subset, x, order):
1483 def parentpost(repo, subset, x, order):
1484 return p1(repo, subset, x)
1484 return p1(repo, subset, x)
1485
1485
1486 @predicate('parents([set])', safe=True)
1486 @predicate('parents([set])', safe=True)
1487 def parents(repo, subset, x):
1487 def parents(repo, subset, x):
1488 """
1488 """
1489 The set of all parents for all changesets in set, or the working directory.
1489 The set of all parents for all changesets in set, or the working directory.
1490 """
1490 """
1491 if x is None:
1491 if x is None:
1492 ps = set(p.rev() for p in repo[x].parents())
1492 ps = set(p.rev() for p in repo[x].parents())
1493 else:
1493 else:
1494 ps = set()
1494 ps = set()
1495 cl = repo.changelog
1495 cl = repo.changelog
1496 up = ps.update
1496 up = ps.update
1497 parentrevs = cl.parentrevs
1497 parentrevs = cl.parentrevs
1498 for r in getset(repo, fullreposet(repo), x):
1498 for r in getset(repo, fullreposet(repo), x):
1499 try:
1499 try:
1500 up(parentrevs(r))
1500 up(parentrevs(r))
1501 except error.WdirUnsupported:
1501 except error.WdirUnsupported:
1502 up(p.rev() for p in repo[r].parents())
1502 up(p.rev() for p in repo[r].parents())
1503 ps -= {node.nullrev}
1503 ps -= {node.nullrev}
1504 return subset & ps
1504 return subset & ps
1505
1505
1506 def _phase(repo, subset, *targets):
1506 def _phase(repo, subset, *targets):
1507 """helper to select all rev in <targets> phases"""
1507 """helper to select all rev in <targets> phases"""
1508 s = repo._phasecache.getrevset(repo, targets)
1508 s = repo._phasecache.getrevset(repo, targets)
1509 return subset & s
1509 return subset & s
1510
1510
1511 @predicate('draft()', safe=True)
1511 @predicate('draft()', safe=True)
1512 def draft(repo, subset, x):
1512 def draft(repo, subset, x):
1513 """Changeset in draft phase."""
1513 """Changeset in draft phase."""
1514 # i18n: "draft" is a keyword
1514 # i18n: "draft" is a keyword
1515 getargs(x, 0, 0, _("draft takes no arguments"))
1515 getargs(x, 0, 0, _("draft takes no arguments"))
1516 target = phases.draft
1516 target = phases.draft
1517 return _phase(repo, subset, target)
1517 return _phase(repo, subset, target)
1518
1518
1519 @predicate('secret()', safe=True)
1519 @predicate('secret()', safe=True)
1520 def secret(repo, subset, x):
1520 def secret(repo, subset, x):
1521 """Changeset in secret phase."""
1521 """Changeset in secret phase."""
1522 # i18n: "secret" is a keyword
1522 # i18n: "secret" is a keyword
1523 getargs(x, 0, 0, _("secret takes no arguments"))
1523 getargs(x, 0, 0, _("secret takes no arguments"))
1524 target = phases.secret
1524 target = phases.secret
1525 return _phase(repo, subset, target)
1525 return _phase(repo, subset, target)
1526
1526
1527 def parentspec(repo, subset, x, n, order):
1527 def parentspec(repo, subset, x, n, order):
1528 """``set^0``
1528 """``set^0``
1529 The set.
1529 The set.
1530 ``set^1`` (or ``set^``), ``set^2``
1530 ``set^1`` (or ``set^``), ``set^2``
1531 First or second parent, respectively, of all changesets in set.
1531 First or second parent, respectively, of all changesets in set.
1532 """
1532 """
1533 try:
1533 try:
1534 n = int(n[1])
1534 n = int(n[1])
1535 if n not in (0, 1, 2):
1535 if n not in (0, 1, 2):
1536 raise ValueError
1536 raise ValueError
1537 except (TypeError, ValueError):
1537 except (TypeError, ValueError):
1538 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1538 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1539 ps = set()
1539 ps = set()
1540 cl = repo.changelog
1540 cl = repo.changelog
1541 for r in getset(repo, fullreposet(repo), x):
1541 for r in getset(repo, fullreposet(repo), x):
1542 if n == 0:
1542 if n == 0:
1543 ps.add(r)
1543 ps.add(r)
1544 elif n == 1:
1544 elif n == 1:
1545 try:
1545 try:
1546 ps.add(cl.parentrevs(r)[0])
1546 ps.add(cl.parentrevs(r)[0])
1547 except error.WdirUnsupported:
1547 except error.WdirUnsupported:
1548 ps.add(repo[r].parents()[0].rev())
1548 ps.add(repo[r].parents()[0].rev())
1549 else:
1549 else:
1550 try:
1550 try:
1551 parents = cl.parentrevs(r)
1551 parents = cl.parentrevs(r)
1552 if parents[1] != node.nullrev:
1552 if parents[1] != node.nullrev:
1553 ps.add(parents[1])
1553 ps.add(parents[1])
1554 except error.WdirUnsupported:
1554 except error.WdirUnsupported:
1555 parents = repo[r].parents()
1555 parents = repo[r].parents()
1556 if len(parents) == 2:
1556 if len(parents) == 2:
1557 ps.add(parents[1].rev())
1557 ps.add(parents[1].rev())
1558 return subset & ps
1558 return subset & ps
1559
1559
1560 @predicate('present(set)', safe=True, takeorder=True)
1560 @predicate('present(set)', safe=True, takeorder=True)
1561 def present(repo, subset, x, order):
1561 def present(repo, subset, x, order):
1562 """An empty set, if any revision in set isn't found; otherwise,
1562 """An empty set, if any revision in set isn't found; otherwise,
1563 all revisions in set.
1563 all revisions in set.
1564
1564
1565 If any of specified revisions is not present in the local repository,
1565 If any of specified revisions is not present in the local repository,
1566 the query is normally aborted. But this predicate allows the query
1566 the query is normally aborted. But this predicate allows the query
1567 to continue even in such cases.
1567 to continue even in such cases.
1568 """
1568 """
1569 try:
1569 try:
1570 return getset(repo, subset, x, order)
1570 return getset(repo, subset, x, order)
1571 except error.RepoLookupError:
1571 except error.RepoLookupError:
1572 return baseset()
1572 return baseset()
1573
1573
1574 # for internal use
1574 # for internal use
1575 @predicate('_notpublic', safe=True)
1575 @predicate('_notpublic', safe=True)
1576 def _notpublic(repo, subset, x):
1576 def _notpublic(repo, subset, x):
1577 getargs(x, 0, 0, "_notpublic takes no arguments")
1577 getargs(x, 0, 0, "_notpublic takes no arguments")
1578 return _phase(repo, subset, phases.draft, phases.secret)
1578 return _phase(repo, subset, phases.draft, phases.secret)
1579
1579
1580 # for internal use
1581 @predicate('_phaseandancestors(phasename, set)', safe=True)
1582 def _phaseandancestors(repo, subset, x):
1583 # equivalent to (phasename() & ancestors(set)) but more efficient
1584 # phasename could be one of 'draft', 'secret', or '_notpublic'
1585 args = getargs(x, 2, 2, "_phaseandancestors requires two arguments")
1586 phasename = getsymbol(args[0])
1587 s = getset(repo, fullreposet(repo), args[1])
1588
1589 draft = phases.draft
1590 secret = phases.secret
1591 phasenamemap = {
1592 '_notpublic': draft,
1593 'draft': draft, # follow secret's ancestors
1594 'secret': secret,
1595 }
1596 if phasename not in phasenamemap:
1597 raise error.ParseError('%r is not a valid phasename' % phasename)
1598
1599 minimalphase = phasenamemap[phasename]
1600 getphase = repo._phasecache.phase
1601
1602 def cutfunc(rev):
1603 return getphase(repo, rev) < minimalphase
1604
1605 revs = dagop.revancestors(repo, s, cutfunc=cutfunc)
1606
1607 if phasename == 'draft': # need to remove secret changesets
1608 revs = revs.filter(lambda r: getphase(repo, r) == draft)
1609 return subset & revs
1610
1580 @predicate('public()', safe=True)
1611 @predicate('public()', safe=True)
1581 def public(repo, subset, x):
1612 def public(repo, subset, x):
1582 """Changeset in public phase."""
1613 """Changeset in public phase."""
1583 # i18n: "public" is a keyword
1614 # i18n: "public" is a keyword
1584 getargs(x, 0, 0, _("public takes no arguments"))
1615 getargs(x, 0, 0, _("public takes no arguments"))
1585 phase = repo._phasecache.phase
1616 phase = repo._phasecache.phase
1586 target = phases.public
1617 target = phases.public
1587 condition = lambda r: phase(repo, r) == target
1618 condition = lambda r: phase(repo, r) == target
1588 return subset.filter(condition, condrepr=('<phase %r>', target),
1619 return subset.filter(condition, condrepr=('<phase %r>', target),
1589 cache=False)
1620 cache=False)
1590
1621
1591 @predicate('remote([id [,path]])', safe=False)
1622 @predicate('remote([id [,path]])', safe=False)
1592 def remote(repo, subset, x):
1623 def remote(repo, subset, x):
1593 """Local revision that corresponds to the given identifier in a
1624 """Local revision that corresponds to the given identifier in a
1594 remote repository, if present. Here, the '.' identifier is a
1625 remote repository, if present. Here, the '.' identifier is a
1595 synonym for the current local branch.
1626 synonym for the current local branch.
1596 """
1627 """
1597
1628
1598 from . import hg # avoid start-up nasties
1629 from . import hg # avoid start-up nasties
1599 # i18n: "remote" is a keyword
1630 # i18n: "remote" is a keyword
1600 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1631 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1601
1632
1602 q = '.'
1633 q = '.'
1603 if len(l) > 0:
1634 if len(l) > 0:
1604 # i18n: "remote" is a keyword
1635 # i18n: "remote" is a keyword
1605 q = getstring(l[0], _("remote requires a string id"))
1636 q = getstring(l[0], _("remote requires a string id"))
1606 if q == '.':
1637 if q == '.':
1607 q = repo['.'].branch()
1638 q = repo['.'].branch()
1608
1639
1609 dest = ''
1640 dest = ''
1610 if len(l) > 1:
1641 if len(l) > 1:
1611 # i18n: "remote" is a keyword
1642 # i18n: "remote" is a keyword
1612 dest = getstring(l[1], _("remote requires a repository path"))
1643 dest = getstring(l[1], _("remote requires a repository path"))
1613 dest = repo.ui.expandpath(dest or 'default')
1644 dest = repo.ui.expandpath(dest or 'default')
1614 dest, branches = hg.parseurl(dest)
1645 dest, branches = hg.parseurl(dest)
1615 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1646 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1616 if revs:
1647 if revs:
1617 revs = [repo.lookup(rev) for rev in revs]
1648 revs = [repo.lookup(rev) for rev in revs]
1618 other = hg.peer(repo, {}, dest)
1649 other = hg.peer(repo, {}, dest)
1619 n = other.lookup(q)
1650 n = other.lookup(q)
1620 if n in repo:
1651 if n in repo:
1621 r = repo[n].rev()
1652 r = repo[n].rev()
1622 if r in subset:
1653 if r in subset:
1623 return baseset([r])
1654 return baseset([r])
1624 return baseset()
1655 return baseset()
1625
1656
1626 @predicate('removes(pattern)', safe=True)
1657 @predicate('removes(pattern)', safe=True)
1627 def removes(repo, subset, x):
1658 def removes(repo, subset, x):
1628 """Changesets which remove files matching pattern.
1659 """Changesets which remove files matching pattern.
1629
1660
1630 The pattern without explicit kind like ``glob:`` is expected to be
1661 The pattern without explicit kind like ``glob:`` is expected to be
1631 relative to the current directory and match against a file or a
1662 relative to the current directory and match against a file or a
1632 directory.
1663 directory.
1633 """
1664 """
1634 # i18n: "removes" is a keyword
1665 # i18n: "removes" is a keyword
1635 pat = getstring(x, _("removes requires a pattern"))
1666 pat = getstring(x, _("removes requires a pattern"))
1636 return checkstatus(repo, subset, pat, 2)
1667 return checkstatus(repo, subset, pat, 2)
1637
1668
1638 @predicate('rev(number)', safe=True)
1669 @predicate('rev(number)', safe=True)
1639 def rev(repo, subset, x):
1670 def rev(repo, subset, x):
1640 """Revision with the given numeric identifier.
1671 """Revision with the given numeric identifier.
1641 """
1672 """
1642 # i18n: "rev" is a keyword
1673 # i18n: "rev" is a keyword
1643 l = getargs(x, 1, 1, _("rev requires one argument"))
1674 l = getargs(x, 1, 1, _("rev requires one argument"))
1644 try:
1675 try:
1645 # i18n: "rev" is a keyword
1676 # i18n: "rev" is a keyword
1646 l = int(getstring(l[0], _("rev requires a number")))
1677 l = int(getstring(l[0], _("rev requires a number")))
1647 except (TypeError, ValueError):
1678 except (TypeError, ValueError):
1648 # i18n: "rev" is a keyword
1679 # i18n: "rev" is a keyword
1649 raise error.ParseError(_("rev expects a number"))
1680 raise error.ParseError(_("rev expects a number"))
1650 if l not in repo.changelog and l not in (node.nullrev, node.wdirrev):
1681 if l not in repo.changelog and l not in (node.nullrev, node.wdirrev):
1651 return baseset()
1682 return baseset()
1652 return subset & baseset([l])
1683 return subset & baseset([l])
1653
1684
1654 @predicate('matching(revision [, field])', safe=True)
1685 @predicate('matching(revision [, field])', safe=True)
1655 def matching(repo, subset, x):
1686 def matching(repo, subset, x):
1656 """Changesets in which a given set of fields match the set of fields in the
1687 """Changesets in which a given set of fields match the set of fields in the
1657 selected revision or set.
1688 selected revision or set.
1658
1689
1659 To match more than one field pass the list of fields to match separated
1690 To match more than one field pass the list of fields to match separated
1660 by spaces (e.g. ``author description``).
1691 by spaces (e.g. ``author description``).
1661
1692
1662 Valid fields are most regular revision fields and some special fields.
1693 Valid fields are most regular revision fields and some special fields.
1663
1694
1664 Regular revision fields are ``description``, ``author``, ``branch``,
1695 Regular revision fields are ``description``, ``author``, ``branch``,
1665 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1696 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1666 and ``diff``.
1697 and ``diff``.
1667 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1698 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1668 contents of the revision. Two revisions matching their ``diff`` will
1699 contents of the revision. Two revisions matching their ``diff`` will
1669 also match their ``files``.
1700 also match their ``files``.
1670
1701
1671 Special fields are ``summary`` and ``metadata``:
1702 Special fields are ``summary`` and ``metadata``:
1672 ``summary`` matches the first line of the description.
1703 ``summary`` matches the first line of the description.
1673 ``metadata`` is equivalent to matching ``description user date``
1704 ``metadata`` is equivalent to matching ``description user date``
1674 (i.e. it matches the main metadata fields).
1705 (i.e. it matches the main metadata fields).
1675
1706
1676 ``metadata`` is the default field which is used when no fields are
1707 ``metadata`` is the default field which is used when no fields are
1677 specified. You can match more than one field at a time.
1708 specified. You can match more than one field at a time.
1678 """
1709 """
1679 # i18n: "matching" is a keyword
1710 # i18n: "matching" is a keyword
1680 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1711 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1681
1712
1682 revs = getset(repo, fullreposet(repo), l[0])
1713 revs = getset(repo, fullreposet(repo), l[0])
1683
1714
1684 fieldlist = ['metadata']
1715 fieldlist = ['metadata']
1685 if len(l) > 1:
1716 if len(l) > 1:
1686 fieldlist = getstring(l[1],
1717 fieldlist = getstring(l[1],
1687 # i18n: "matching" is a keyword
1718 # i18n: "matching" is a keyword
1688 _("matching requires a string "
1719 _("matching requires a string "
1689 "as its second argument")).split()
1720 "as its second argument")).split()
1690
1721
1691 # Make sure that there are no repeated fields,
1722 # Make sure that there are no repeated fields,
1692 # expand the 'special' 'metadata' field type
1723 # expand the 'special' 'metadata' field type
1693 # and check the 'files' whenever we check the 'diff'
1724 # and check the 'files' whenever we check the 'diff'
1694 fields = []
1725 fields = []
1695 for field in fieldlist:
1726 for field in fieldlist:
1696 if field == 'metadata':
1727 if field == 'metadata':
1697 fields += ['user', 'description', 'date']
1728 fields += ['user', 'description', 'date']
1698 elif field == 'diff':
1729 elif field == 'diff':
1699 # a revision matching the diff must also match the files
1730 # a revision matching the diff must also match the files
1700 # since matching the diff is very costly, make sure to
1731 # since matching the diff is very costly, make sure to
1701 # also match the files first
1732 # also match the files first
1702 fields += ['files', 'diff']
1733 fields += ['files', 'diff']
1703 else:
1734 else:
1704 if field == 'author':
1735 if field == 'author':
1705 field = 'user'
1736 field = 'user'
1706 fields.append(field)
1737 fields.append(field)
1707 fields = set(fields)
1738 fields = set(fields)
1708 if 'summary' in fields and 'description' in fields:
1739 if 'summary' in fields and 'description' in fields:
1709 # If a revision matches its description it also matches its summary
1740 # If a revision matches its description it also matches its summary
1710 fields.discard('summary')
1741 fields.discard('summary')
1711
1742
1712 # We may want to match more than one field
1743 # We may want to match more than one field
1713 # Not all fields take the same amount of time to be matched
1744 # Not all fields take the same amount of time to be matched
1714 # Sort the selected fields in order of increasing matching cost
1745 # Sort the selected fields in order of increasing matching cost
1715 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1746 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1716 'files', 'description', 'substate', 'diff']
1747 'files', 'description', 'substate', 'diff']
1717 def fieldkeyfunc(f):
1748 def fieldkeyfunc(f):
1718 try:
1749 try:
1719 return fieldorder.index(f)
1750 return fieldorder.index(f)
1720 except ValueError:
1751 except ValueError:
1721 # assume an unknown field is very costly
1752 # assume an unknown field is very costly
1722 return len(fieldorder)
1753 return len(fieldorder)
1723 fields = list(fields)
1754 fields = list(fields)
1724 fields.sort(key=fieldkeyfunc)
1755 fields.sort(key=fieldkeyfunc)
1725
1756
1726 # Each field will be matched with its own "getfield" function
1757 # Each field will be matched with its own "getfield" function
1727 # which will be added to the getfieldfuncs array of functions
1758 # which will be added to the getfieldfuncs array of functions
1728 getfieldfuncs = []
1759 getfieldfuncs = []
1729 _funcs = {
1760 _funcs = {
1730 'user': lambda r: repo[r].user(),
1761 'user': lambda r: repo[r].user(),
1731 'branch': lambda r: repo[r].branch(),
1762 'branch': lambda r: repo[r].branch(),
1732 'date': lambda r: repo[r].date(),
1763 'date': lambda r: repo[r].date(),
1733 'description': lambda r: repo[r].description(),
1764 'description': lambda r: repo[r].description(),
1734 'files': lambda r: repo[r].files(),
1765 'files': lambda r: repo[r].files(),
1735 'parents': lambda r: repo[r].parents(),
1766 'parents': lambda r: repo[r].parents(),
1736 'phase': lambda r: repo[r].phase(),
1767 'phase': lambda r: repo[r].phase(),
1737 'substate': lambda r: repo[r].substate,
1768 'substate': lambda r: repo[r].substate,
1738 'summary': lambda r: repo[r].description().splitlines()[0],
1769 'summary': lambda r: repo[r].description().splitlines()[0],
1739 'diff': lambda r: list(repo[r].diff(git=True),)
1770 'diff': lambda r: list(repo[r].diff(git=True),)
1740 }
1771 }
1741 for info in fields:
1772 for info in fields:
1742 getfield = _funcs.get(info, None)
1773 getfield = _funcs.get(info, None)
1743 if getfield is None:
1774 if getfield is None:
1744 raise error.ParseError(
1775 raise error.ParseError(
1745 # i18n: "matching" is a keyword
1776 # i18n: "matching" is a keyword
1746 _("unexpected field name passed to matching: %s") % info)
1777 _("unexpected field name passed to matching: %s") % info)
1747 getfieldfuncs.append(getfield)
1778 getfieldfuncs.append(getfield)
1748 # convert the getfield array of functions into a "getinfo" function
1779 # convert the getfield array of functions into a "getinfo" function
1749 # which returns an array of field values (or a single value if there
1780 # which returns an array of field values (or a single value if there
1750 # is only one field to match)
1781 # is only one field to match)
1751 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1782 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1752
1783
1753 def matches(x):
1784 def matches(x):
1754 for rev in revs:
1785 for rev in revs:
1755 target = getinfo(rev)
1786 target = getinfo(rev)
1756 match = True
1787 match = True
1757 for n, f in enumerate(getfieldfuncs):
1788 for n, f in enumerate(getfieldfuncs):
1758 if target[n] != f(x):
1789 if target[n] != f(x):
1759 match = False
1790 match = False
1760 if match:
1791 if match:
1761 return True
1792 return True
1762 return False
1793 return False
1763
1794
1764 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1795 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1765
1796
1766 @predicate('reverse(set)', safe=True, takeorder=True)
1797 @predicate('reverse(set)', safe=True, takeorder=True)
1767 def reverse(repo, subset, x, order):
1798 def reverse(repo, subset, x, order):
1768 """Reverse order of set.
1799 """Reverse order of set.
1769 """
1800 """
1770 l = getset(repo, subset, x, order)
1801 l = getset(repo, subset, x, order)
1771 if order == defineorder:
1802 if order == defineorder:
1772 l.reverse()
1803 l.reverse()
1773 return l
1804 return l
1774
1805
1775 @predicate('roots(set)', safe=True)
1806 @predicate('roots(set)', safe=True)
1776 def roots(repo, subset, x):
1807 def roots(repo, subset, x):
1777 """Changesets in set with no parent changeset in set.
1808 """Changesets in set with no parent changeset in set.
1778 """
1809 """
1779 s = getset(repo, fullreposet(repo), x)
1810 s = getset(repo, fullreposet(repo), x)
1780 parents = repo.changelog.parentrevs
1811 parents = repo.changelog.parentrevs
1781 def filter(r):
1812 def filter(r):
1782 for p in parents(r):
1813 for p in parents(r):
1783 if 0 <= p and p in s:
1814 if 0 <= p and p in s:
1784 return False
1815 return False
1785 return True
1816 return True
1786 return subset & s.filter(filter, condrepr='<roots>')
1817 return subset & s.filter(filter, condrepr='<roots>')
1787
1818
1788 _sortkeyfuncs = {
1819 _sortkeyfuncs = {
1789 'rev': lambda c: c.rev(),
1820 'rev': lambda c: c.rev(),
1790 'branch': lambda c: c.branch(),
1821 'branch': lambda c: c.branch(),
1791 'desc': lambda c: c.description(),
1822 'desc': lambda c: c.description(),
1792 'user': lambda c: c.user(),
1823 'user': lambda c: c.user(),
1793 'author': lambda c: c.user(),
1824 'author': lambda c: c.user(),
1794 'date': lambda c: c.date()[0],
1825 'date': lambda c: c.date()[0],
1795 }
1826 }
1796
1827
1797 def _getsortargs(x):
1828 def _getsortargs(x):
1798 """Parse sort options into (set, [(key, reverse)], opts)"""
1829 """Parse sort options into (set, [(key, reverse)], opts)"""
1799 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1830 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1800 if 'set' not in args:
1831 if 'set' not in args:
1801 # i18n: "sort" is a keyword
1832 # i18n: "sort" is a keyword
1802 raise error.ParseError(_('sort requires one or two arguments'))
1833 raise error.ParseError(_('sort requires one or two arguments'))
1803 keys = "rev"
1834 keys = "rev"
1804 if 'keys' in args:
1835 if 'keys' in args:
1805 # i18n: "sort" is a keyword
1836 # i18n: "sort" is a keyword
1806 keys = getstring(args['keys'], _("sort spec must be a string"))
1837 keys = getstring(args['keys'], _("sort spec must be a string"))
1807
1838
1808 keyflags = []
1839 keyflags = []
1809 for k in keys.split():
1840 for k in keys.split():
1810 fk = k
1841 fk = k
1811 reverse = (k[0] == '-')
1842 reverse = (k[0] == '-')
1812 if reverse:
1843 if reverse:
1813 k = k[1:]
1844 k = k[1:]
1814 if k not in _sortkeyfuncs and k != 'topo':
1845 if k not in _sortkeyfuncs and k != 'topo':
1815 raise error.ParseError(_("unknown sort key %r") % fk)
1846 raise error.ParseError(_("unknown sort key %r") % fk)
1816 keyflags.append((k, reverse))
1847 keyflags.append((k, reverse))
1817
1848
1818 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1849 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1819 # i18n: "topo" is a keyword
1850 # i18n: "topo" is a keyword
1820 raise error.ParseError(_('topo sort order cannot be combined '
1851 raise error.ParseError(_('topo sort order cannot be combined '
1821 'with other sort keys'))
1852 'with other sort keys'))
1822
1853
1823 opts = {}
1854 opts = {}
1824 if 'topo.firstbranch' in args:
1855 if 'topo.firstbranch' in args:
1825 if any(k == 'topo' for k, reverse in keyflags):
1856 if any(k == 'topo' for k, reverse in keyflags):
1826 opts['topo.firstbranch'] = args['topo.firstbranch']
1857 opts['topo.firstbranch'] = args['topo.firstbranch']
1827 else:
1858 else:
1828 # i18n: "topo" and "topo.firstbranch" are keywords
1859 # i18n: "topo" and "topo.firstbranch" are keywords
1829 raise error.ParseError(_('topo.firstbranch can only be used '
1860 raise error.ParseError(_('topo.firstbranch can only be used '
1830 'when using the topo sort key'))
1861 'when using the topo sort key'))
1831
1862
1832 return args['set'], keyflags, opts
1863 return args['set'], keyflags, opts
1833
1864
1834 @predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True)
1865 @predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True)
1835 def sort(repo, subset, x, order):
1866 def sort(repo, subset, x, order):
1836 """Sort set by keys. The default sort order is ascending, specify a key
1867 """Sort set by keys. The default sort order is ascending, specify a key
1837 as ``-key`` to sort in descending order.
1868 as ``-key`` to sort in descending order.
1838
1869
1839 The keys can be:
1870 The keys can be:
1840
1871
1841 - ``rev`` for the revision number,
1872 - ``rev`` for the revision number,
1842 - ``branch`` for the branch name,
1873 - ``branch`` for the branch name,
1843 - ``desc`` for the commit message (description),
1874 - ``desc`` for the commit message (description),
1844 - ``user`` for user name (``author`` can be used as an alias),
1875 - ``user`` for user name (``author`` can be used as an alias),
1845 - ``date`` for the commit date
1876 - ``date`` for the commit date
1846 - ``topo`` for a reverse topographical sort
1877 - ``topo`` for a reverse topographical sort
1847
1878
1848 The ``topo`` sort order cannot be combined with other sort keys. This sort
1879 The ``topo`` sort order cannot be combined with other sort keys. This sort
1849 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1880 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1850 specifies what topographical branches to prioritize in the sort.
1881 specifies what topographical branches to prioritize in the sort.
1851
1882
1852 """
1883 """
1853 s, keyflags, opts = _getsortargs(x)
1884 s, keyflags, opts = _getsortargs(x)
1854 revs = getset(repo, subset, s, order)
1885 revs = getset(repo, subset, s, order)
1855
1886
1856 if not keyflags or order != defineorder:
1887 if not keyflags or order != defineorder:
1857 return revs
1888 return revs
1858 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1889 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1859 revs.sort(reverse=keyflags[0][1])
1890 revs.sort(reverse=keyflags[0][1])
1860 return revs
1891 return revs
1861 elif keyflags[0][0] == "topo":
1892 elif keyflags[0][0] == "topo":
1862 firstbranch = ()
1893 firstbranch = ()
1863 if 'topo.firstbranch' in opts:
1894 if 'topo.firstbranch' in opts:
1864 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1895 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1865 revs = baseset(dagop.toposort(revs, repo.changelog.parentrevs,
1896 revs = baseset(dagop.toposort(revs, repo.changelog.parentrevs,
1866 firstbranch),
1897 firstbranch),
1867 istopo=True)
1898 istopo=True)
1868 if keyflags[0][1]:
1899 if keyflags[0][1]:
1869 revs.reverse()
1900 revs.reverse()
1870 return revs
1901 return revs
1871
1902
1872 # sort() is guaranteed to be stable
1903 # sort() is guaranteed to be stable
1873 ctxs = [repo[r] for r in revs]
1904 ctxs = [repo[r] for r in revs]
1874 for k, reverse in reversed(keyflags):
1905 for k, reverse in reversed(keyflags):
1875 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1906 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1876 return baseset([c.rev() for c in ctxs])
1907 return baseset([c.rev() for c in ctxs])
1877
1908
1878 @predicate('subrepo([pattern])')
1909 @predicate('subrepo([pattern])')
1879 def subrepo(repo, subset, x):
1910 def subrepo(repo, subset, x):
1880 """Changesets that add, modify or remove the given subrepo. If no subrepo
1911 """Changesets that add, modify or remove the given subrepo. If no subrepo
1881 pattern is named, any subrepo changes are returned.
1912 pattern is named, any subrepo changes are returned.
1882 """
1913 """
1883 # i18n: "subrepo" is a keyword
1914 # i18n: "subrepo" is a keyword
1884 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
1915 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
1885 pat = None
1916 pat = None
1886 if len(args) != 0:
1917 if len(args) != 0:
1887 pat = getstring(args[0], _("subrepo requires a pattern"))
1918 pat = getstring(args[0], _("subrepo requires a pattern"))
1888
1919
1889 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
1920 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
1890
1921
1891 def submatches(names):
1922 def submatches(names):
1892 k, p, m = util.stringmatcher(pat)
1923 k, p, m = util.stringmatcher(pat)
1893 for name in names:
1924 for name in names:
1894 if m(name):
1925 if m(name):
1895 yield name
1926 yield name
1896
1927
1897 def matches(x):
1928 def matches(x):
1898 c = repo[x]
1929 c = repo[x]
1899 s = repo.status(c.p1().node(), c.node(), match=m)
1930 s = repo.status(c.p1().node(), c.node(), match=m)
1900
1931
1901 if pat is None:
1932 if pat is None:
1902 return s.added or s.modified or s.removed
1933 return s.added or s.modified or s.removed
1903
1934
1904 if s.added:
1935 if s.added:
1905 return any(submatches(c.substate.keys()))
1936 return any(submatches(c.substate.keys()))
1906
1937
1907 if s.modified:
1938 if s.modified:
1908 subs = set(c.p1().substate.keys())
1939 subs = set(c.p1().substate.keys())
1909 subs.update(c.substate.keys())
1940 subs.update(c.substate.keys())
1910
1941
1911 for path in submatches(subs):
1942 for path in submatches(subs):
1912 if c.p1().substate.get(path) != c.substate.get(path):
1943 if c.p1().substate.get(path) != c.substate.get(path):
1913 return True
1944 return True
1914
1945
1915 if s.removed:
1946 if s.removed:
1916 return any(submatches(c.p1().substate.keys()))
1947 return any(submatches(c.p1().substate.keys()))
1917
1948
1918 return False
1949 return False
1919
1950
1920 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
1951 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
1921
1952
1922 def _mapbynodefunc(repo, s, f):
1953 def _mapbynodefunc(repo, s, f):
1923 """(repo, smartset, [node] -> [node]) -> smartset
1954 """(repo, smartset, [node] -> [node]) -> smartset
1924
1955
1925 Helper method to map a smartset to another smartset given a function only
1956 Helper method to map a smartset to another smartset given a function only
1926 talking about nodes. Handles converting between rev numbers and nodes, and
1957 talking about nodes. Handles converting between rev numbers and nodes, and
1927 filtering.
1958 filtering.
1928 """
1959 """
1929 cl = repo.unfiltered().changelog
1960 cl = repo.unfiltered().changelog
1930 torev = cl.rev
1961 torev = cl.rev
1931 tonode = cl.node
1962 tonode = cl.node
1932 nodemap = cl.nodemap
1963 nodemap = cl.nodemap
1933 result = set(torev(n) for n in f(tonode(r) for r in s) if n in nodemap)
1964 result = set(torev(n) for n in f(tonode(r) for r in s) if n in nodemap)
1934 return smartset.baseset(result - repo.changelog.filteredrevs)
1965 return smartset.baseset(result - repo.changelog.filteredrevs)
1935
1966
1936 @predicate('successors(set)', safe=True)
1967 @predicate('successors(set)', safe=True)
1937 def successors(repo, subset, x):
1968 def successors(repo, subset, x):
1938 """All successors for set, including the given set themselves"""
1969 """All successors for set, including the given set themselves"""
1939 s = getset(repo, fullreposet(repo), x)
1970 s = getset(repo, fullreposet(repo), x)
1940 f = lambda nodes: obsutil.allsuccessors(repo.obsstore, nodes)
1971 f = lambda nodes: obsutil.allsuccessors(repo.obsstore, nodes)
1941 d = _mapbynodefunc(repo, s, f)
1972 d = _mapbynodefunc(repo, s, f)
1942 return subset & d
1973 return subset & d
1943
1974
1944 def _substringmatcher(pattern, casesensitive=True):
1975 def _substringmatcher(pattern, casesensitive=True):
1945 kind, pattern, matcher = util.stringmatcher(pattern,
1976 kind, pattern, matcher = util.stringmatcher(pattern,
1946 casesensitive=casesensitive)
1977 casesensitive=casesensitive)
1947 if kind == 'literal':
1978 if kind == 'literal':
1948 if not casesensitive:
1979 if not casesensitive:
1949 pattern = encoding.lower(pattern)
1980 pattern = encoding.lower(pattern)
1950 matcher = lambda s: pattern in encoding.lower(s)
1981 matcher = lambda s: pattern in encoding.lower(s)
1951 else:
1982 else:
1952 matcher = lambda s: pattern in s
1983 matcher = lambda s: pattern in s
1953 return kind, pattern, matcher
1984 return kind, pattern, matcher
1954
1985
1955 @predicate('tag([name])', safe=True)
1986 @predicate('tag([name])', safe=True)
1956 def tag(repo, subset, x):
1987 def tag(repo, subset, x):
1957 """The specified tag by name, or all tagged revisions if no name is given.
1988 """The specified tag by name, or all tagged revisions if no name is given.
1958
1989
1959 Pattern matching is supported for `name`. See
1990 Pattern matching is supported for `name`. See
1960 :hg:`help revisions.patterns`.
1991 :hg:`help revisions.patterns`.
1961 """
1992 """
1962 # i18n: "tag" is a keyword
1993 # i18n: "tag" is a keyword
1963 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1994 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1964 cl = repo.changelog
1995 cl = repo.changelog
1965 if args:
1996 if args:
1966 pattern = getstring(args[0],
1997 pattern = getstring(args[0],
1967 # i18n: "tag" is a keyword
1998 # i18n: "tag" is a keyword
1968 _('the argument to tag must be a string'))
1999 _('the argument to tag must be a string'))
1969 kind, pattern, matcher = util.stringmatcher(pattern)
2000 kind, pattern, matcher = util.stringmatcher(pattern)
1970 if kind == 'literal':
2001 if kind == 'literal':
1971 # avoid resolving all tags
2002 # avoid resolving all tags
1972 tn = repo._tagscache.tags.get(pattern, None)
2003 tn = repo._tagscache.tags.get(pattern, None)
1973 if tn is None:
2004 if tn is None:
1974 raise error.RepoLookupError(_("tag '%s' does not exist")
2005 raise error.RepoLookupError(_("tag '%s' does not exist")
1975 % pattern)
2006 % pattern)
1976 s = {repo[tn].rev()}
2007 s = {repo[tn].rev()}
1977 else:
2008 else:
1978 s = {cl.rev(n) for t, n in repo.tagslist() if matcher(t)}
2009 s = {cl.rev(n) for t, n in repo.tagslist() if matcher(t)}
1979 else:
2010 else:
1980 s = {cl.rev(n) for t, n in repo.tagslist() if t != 'tip'}
2011 s = {cl.rev(n) for t, n in repo.tagslist() if t != 'tip'}
1981 return subset & s
2012 return subset & s
1982
2013
1983 @predicate('tagged', safe=True)
2014 @predicate('tagged', safe=True)
1984 def tagged(repo, subset, x):
2015 def tagged(repo, subset, x):
1985 return tag(repo, subset, x)
2016 return tag(repo, subset, x)
1986
2017
1987 @predicate('unstable()', safe=True)
2018 @predicate('unstable()', safe=True)
1988 def unstable(repo, subset, x):
2019 def unstable(repo, subset, x):
1989 msg = ("'unstable()' is deprecated, "
2020 msg = ("'unstable()' is deprecated, "
1990 "use 'orphan()'")
2021 "use 'orphan()'")
1991 repo.ui.deprecwarn(msg, '4.4')
2022 repo.ui.deprecwarn(msg, '4.4')
1992
2023
1993 return orphan(repo, subset, x)
2024 return orphan(repo, subset, x)
1994
2025
1995 @predicate('orphan()', safe=True)
2026 @predicate('orphan()', safe=True)
1996 def orphan(repo, subset, x):
2027 def orphan(repo, subset, x):
1997 """Non-obsolete changesets with obsolete ancestors. (EXPERIMENTAL)
2028 """Non-obsolete changesets with obsolete ancestors. (EXPERIMENTAL)
1998 """
2029 """
1999 # i18n: "orphan" is a keyword
2030 # i18n: "orphan" is a keyword
2000 getargs(x, 0, 0, _("orphan takes no arguments"))
2031 getargs(x, 0, 0, _("orphan takes no arguments"))
2001 orphan = obsmod.getrevs(repo, 'orphan')
2032 orphan = obsmod.getrevs(repo, 'orphan')
2002 return subset & orphan
2033 return subset & orphan
2003
2034
2004
2035
2005 @predicate('user(string)', safe=True)
2036 @predicate('user(string)', safe=True)
2006 def user(repo, subset, x):
2037 def user(repo, subset, x):
2007 """User name contains string. The match is case-insensitive.
2038 """User name contains string. The match is case-insensitive.
2008
2039
2009 Pattern matching is supported for `string`. See
2040 Pattern matching is supported for `string`. See
2010 :hg:`help revisions.patterns`.
2041 :hg:`help revisions.patterns`.
2011 """
2042 """
2012 return author(repo, subset, x)
2043 return author(repo, subset, x)
2013
2044
2014 @predicate('wdir()', safe=True)
2045 @predicate('wdir()', safe=True)
2015 def wdir(repo, subset, x):
2046 def wdir(repo, subset, x):
2016 """Working directory. (EXPERIMENTAL)"""
2047 """Working directory. (EXPERIMENTAL)"""
2017 # i18n: "wdir" is a keyword
2048 # i18n: "wdir" is a keyword
2018 getargs(x, 0, 0, _("wdir takes no arguments"))
2049 getargs(x, 0, 0, _("wdir takes no arguments"))
2019 if node.wdirrev in subset or isinstance(subset, fullreposet):
2050 if node.wdirrev in subset or isinstance(subset, fullreposet):
2020 return baseset([node.wdirrev])
2051 return baseset([node.wdirrev])
2021 return baseset()
2052 return baseset()
2022
2053
2023 def _orderedlist(repo, subset, x):
2054 def _orderedlist(repo, subset, x):
2024 s = getstring(x, "internal error")
2055 s = getstring(x, "internal error")
2025 if not s:
2056 if not s:
2026 return baseset()
2057 return baseset()
2027 # remove duplicates here. it's difficult for caller to deduplicate sets
2058 # remove duplicates here. it's difficult for caller to deduplicate sets
2028 # because different symbols can point to the same rev.
2059 # because different symbols can point to the same rev.
2029 cl = repo.changelog
2060 cl = repo.changelog
2030 ls = []
2061 ls = []
2031 seen = set()
2062 seen = set()
2032 for t in s.split('\0'):
2063 for t in s.split('\0'):
2033 try:
2064 try:
2034 # fast path for integer revision
2065 # fast path for integer revision
2035 r = int(t)
2066 r = int(t)
2036 if str(r) != t or r not in cl:
2067 if str(r) != t or r not in cl:
2037 raise ValueError
2068 raise ValueError
2038 revs = [r]
2069 revs = [r]
2039 except ValueError:
2070 except ValueError:
2040 revs = stringset(repo, subset, t, defineorder)
2071 revs = stringset(repo, subset, t, defineorder)
2041
2072
2042 for r in revs:
2073 for r in revs:
2043 if r in seen:
2074 if r in seen:
2044 continue
2075 continue
2045 if (r in subset
2076 if (r in subset
2046 or r == node.nullrev and isinstance(subset, fullreposet)):
2077 or r == node.nullrev and isinstance(subset, fullreposet)):
2047 ls.append(r)
2078 ls.append(r)
2048 seen.add(r)
2079 seen.add(r)
2049 return baseset(ls)
2080 return baseset(ls)
2050
2081
2051 # for internal use
2082 # for internal use
2052 @predicate('_list', safe=True, takeorder=True)
2083 @predicate('_list', safe=True, takeorder=True)
2053 def _list(repo, subset, x, order):
2084 def _list(repo, subset, x, order):
2054 if order == followorder:
2085 if order == followorder:
2055 # slow path to take the subset order
2086 # slow path to take the subset order
2056 return subset & _orderedlist(repo, fullreposet(repo), x)
2087 return subset & _orderedlist(repo, fullreposet(repo), x)
2057 else:
2088 else:
2058 return _orderedlist(repo, subset, x)
2089 return _orderedlist(repo, subset, x)
2059
2090
2060 def _orderedintlist(repo, subset, x):
2091 def _orderedintlist(repo, subset, x):
2061 s = getstring(x, "internal error")
2092 s = getstring(x, "internal error")
2062 if not s:
2093 if not s:
2063 return baseset()
2094 return baseset()
2064 ls = [int(r) for r in s.split('\0')]
2095 ls = [int(r) for r in s.split('\0')]
2065 s = subset
2096 s = subset
2066 return baseset([r for r in ls if r in s])
2097 return baseset([r for r in ls if r in s])
2067
2098
2068 # for internal use
2099 # for internal use
2069 @predicate('_intlist', safe=True, takeorder=True)
2100 @predicate('_intlist', safe=True, takeorder=True)
2070 def _intlist(repo, subset, x, order):
2101 def _intlist(repo, subset, x, order):
2071 if order == followorder:
2102 if order == followorder:
2072 # slow path to take the subset order
2103 # slow path to take the subset order
2073 return subset & _orderedintlist(repo, fullreposet(repo), x)
2104 return subset & _orderedintlist(repo, fullreposet(repo), x)
2074 else:
2105 else:
2075 return _orderedintlist(repo, subset, x)
2106 return _orderedintlist(repo, subset, x)
2076
2107
2077 def _orderedhexlist(repo, subset, x):
2108 def _orderedhexlist(repo, subset, x):
2078 s = getstring(x, "internal error")
2109 s = getstring(x, "internal error")
2079 if not s:
2110 if not s:
2080 return baseset()
2111 return baseset()
2081 cl = repo.changelog
2112 cl = repo.changelog
2082 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2113 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2083 s = subset
2114 s = subset
2084 return baseset([r for r in ls if r in s])
2115 return baseset([r for r in ls if r in s])
2085
2116
2086 # for internal use
2117 # for internal use
2087 @predicate('_hexlist', safe=True, takeorder=True)
2118 @predicate('_hexlist', safe=True, takeorder=True)
2088 def _hexlist(repo, subset, x, order):
2119 def _hexlist(repo, subset, x, order):
2089 if order == followorder:
2120 if order == followorder:
2090 # slow path to take the subset order
2121 # slow path to take the subset order
2091 return subset & _orderedhexlist(repo, fullreposet(repo), x)
2122 return subset & _orderedhexlist(repo, fullreposet(repo), x)
2092 else:
2123 else:
2093 return _orderedhexlist(repo, subset, x)
2124 return _orderedhexlist(repo, subset, x)
2094
2125
2095 methods = {
2126 methods = {
2096 "range": rangeset,
2127 "range": rangeset,
2097 "rangeall": rangeall,
2128 "rangeall": rangeall,
2098 "rangepre": rangepre,
2129 "rangepre": rangepre,
2099 "rangepost": rangepost,
2130 "rangepost": rangepost,
2100 "dagrange": dagrange,
2131 "dagrange": dagrange,
2101 "string": stringset,
2132 "string": stringset,
2102 "symbol": stringset,
2133 "symbol": stringset,
2103 "and": andset,
2134 "and": andset,
2104 "andsmally": andsmallyset,
2135 "andsmally": andsmallyset,
2105 "or": orset,
2136 "or": orset,
2106 "not": notset,
2137 "not": notset,
2107 "difference": differenceset,
2138 "difference": differenceset,
2108 "relation": relationset,
2139 "relation": relationset,
2109 "relsubscript": relsubscriptset,
2140 "relsubscript": relsubscriptset,
2110 "subscript": subscriptset,
2141 "subscript": subscriptset,
2111 "list": listset,
2142 "list": listset,
2112 "keyvalue": keyvaluepair,
2143 "keyvalue": keyvaluepair,
2113 "func": func,
2144 "func": func,
2114 "ancestor": ancestorspec,
2145 "ancestor": ancestorspec,
2115 "parent": parentspec,
2146 "parent": parentspec,
2116 "parentpost": parentpost,
2147 "parentpost": parentpost,
2117 }
2148 }
2118
2149
2119 def posttreebuilthook(tree, repo):
2150 def posttreebuilthook(tree, repo):
2120 # hook for extensions to execute code on the optimized tree
2151 # hook for extensions to execute code on the optimized tree
2121 pass
2152 pass
2122
2153
2123 def match(ui, spec, repo=None):
2154 def match(ui, spec, repo=None):
2124 """Create a matcher for a single revision spec"""
2155 """Create a matcher for a single revision spec"""
2125 return matchany(ui, [spec], repo=repo)
2156 return matchany(ui, [spec], repo=repo)
2126
2157
2127 def matchany(ui, specs, repo=None, localalias=None):
2158 def matchany(ui, specs, repo=None, localalias=None):
2128 """Create a matcher that will include any revisions matching one of the
2159 """Create a matcher that will include any revisions matching one of the
2129 given specs
2160 given specs
2130
2161
2131 If localalias is not None, it is a dict {name: definitionstring}. It takes
2162 If localalias is not None, it is a dict {name: definitionstring}. It takes
2132 precedence over [revsetalias] config section.
2163 precedence over [revsetalias] config section.
2133 """
2164 """
2134 if not specs:
2165 if not specs:
2135 def mfunc(repo, subset=None):
2166 def mfunc(repo, subset=None):
2136 return baseset()
2167 return baseset()
2137 return mfunc
2168 return mfunc
2138 if not all(specs):
2169 if not all(specs):
2139 raise error.ParseError(_("empty query"))
2170 raise error.ParseError(_("empty query"))
2140 lookup = None
2171 lookup = None
2141 if repo:
2172 if repo:
2142 lookup = repo.__contains__
2173 lookup = repo.__contains__
2143 if len(specs) == 1:
2174 if len(specs) == 1:
2144 tree = revsetlang.parse(specs[0], lookup)
2175 tree = revsetlang.parse(specs[0], lookup)
2145 else:
2176 else:
2146 tree = ('or',
2177 tree = ('or',
2147 ('list',) + tuple(revsetlang.parse(s, lookup) for s in specs))
2178 ('list',) + tuple(revsetlang.parse(s, lookup) for s in specs))
2148
2179
2149 aliases = []
2180 aliases = []
2150 warn = None
2181 warn = None
2151 if ui:
2182 if ui:
2152 aliases.extend(ui.configitems('revsetalias'))
2183 aliases.extend(ui.configitems('revsetalias'))
2153 warn = ui.warn
2184 warn = ui.warn
2154 if localalias:
2185 if localalias:
2155 aliases.extend(localalias.items())
2186 aliases.extend(localalias.items())
2156 if aliases:
2187 if aliases:
2157 tree = revsetlang.expandaliases(tree, aliases, warn=warn)
2188 tree = revsetlang.expandaliases(tree, aliases, warn=warn)
2158 tree = revsetlang.foldconcat(tree)
2189 tree = revsetlang.foldconcat(tree)
2159 tree = revsetlang.analyze(tree)
2190 tree = revsetlang.analyze(tree)
2160 tree = revsetlang.optimize(tree)
2191 tree = revsetlang.optimize(tree)
2161 posttreebuilthook(tree, repo)
2192 posttreebuilthook(tree, repo)
2162 return makematcher(tree)
2193 return makematcher(tree)
2163
2194
2164 def makematcher(tree):
2195 def makematcher(tree):
2165 """Create a matcher from an evaluatable tree"""
2196 """Create a matcher from an evaluatable tree"""
2166 def mfunc(repo, subset=None, order=None):
2197 def mfunc(repo, subset=None, order=None):
2167 if order is None:
2198 if order is None:
2168 if subset is None:
2199 if subset is None:
2169 order = defineorder # 'x'
2200 order = defineorder # 'x'
2170 else:
2201 else:
2171 order = followorder # 'subset & x'
2202 order = followorder # 'subset & x'
2172 if subset is None:
2203 if subset is None:
2173 subset = fullreposet(repo)
2204 subset = fullreposet(repo)
2174 return getset(repo, subset, tree, order)
2205 return getset(repo, subset, tree, order)
2175 return mfunc
2206 return mfunc
2176
2207
2177 def loadpredicate(ui, extname, registrarobj):
2208 def loadpredicate(ui, extname, registrarobj):
2178 """Load revset predicates from specified registrarobj
2209 """Load revset predicates from specified registrarobj
2179 """
2210 """
2180 for name, func in registrarobj._table.iteritems():
2211 for name, func in registrarobj._table.iteritems():
2181 symbols[name] = func
2212 symbols[name] = func
2182 if func._safe:
2213 if func._safe:
2183 safesymbols.add(name)
2214 safesymbols.add(name)
2184
2215
2185 # load built-in predicates explicitly to setup safesymbols
2216 # load built-in predicates explicitly to setup safesymbols
2186 loadpredicate(None, None, predicate)
2217 loadpredicate(None, None, predicate)
2187
2218
2188 # tell hggettext to extract docstrings from these functions:
2219 # tell hggettext to extract docstrings from these functions:
2189 i18nfunctions = symbols.values()
2220 i18nfunctions = symbols.values()
@@ -1,674 +1,679 b''
1 # revsetlang.py - parser, tokenizer and utility for revision set language
1 # revsetlang.py - parser, tokenizer and utility for revision set language
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import string
10 import string
11
11
12 from .i18n import _
12 from .i18n import _
13 from . import (
13 from . import (
14 error,
14 error,
15 node,
15 node,
16 parser,
16 parser,
17 pycompat,
17 pycompat,
18 util,
18 util,
19 )
19 )
20
20
21 elements = {
21 elements = {
22 # token-type: binding-strength, primary, prefix, infix, suffix
22 # token-type: binding-strength, primary, prefix, infix, suffix
23 "(": (21, None, ("group", 1, ")"), ("func", 1, ")"), None),
23 "(": (21, None, ("group", 1, ")"), ("func", 1, ")"), None),
24 "[": (21, None, None, ("subscript", 1, "]"), None),
24 "[": (21, None, None, ("subscript", 1, "]"), None),
25 "#": (21, None, None, ("relation", 21), None),
25 "#": (21, None, None, ("relation", 21), None),
26 "##": (20, None, None, ("_concat", 20), None),
26 "##": (20, None, None, ("_concat", 20), None),
27 "~": (18, None, None, ("ancestor", 18), None),
27 "~": (18, None, None, ("ancestor", 18), None),
28 "^": (18, None, None, ("parent", 18), "parentpost"),
28 "^": (18, None, None, ("parent", 18), "parentpost"),
29 "-": (5, None, ("negate", 19), ("minus", 5), None),
29 "-": (5, None, ("negate", 19), ("minus", 5), None),
30 "::": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
30 "::": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
31 "..": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
31 "..": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
32 ":": (15, "rangeall", ("rangepre", 15), ("range", 15), "rangepost"),
32 ":": (15, "rangeall", ("rangepre", 15), ("range", 15), "rangepost"),
33 "not": (10, None, ("not", 10), None, None),
33 "not": (10, None, ("not", 10), None, None),
34 "!": (10, None, ("not", 10), None, None),
34 "!": (10, None, ("not", 10), None, None),
35 "and": (5, None, None, ("and", 5), None),
35 "and": (5, None, None, ("and", 5), None),
36 "&": (5, None, None, ("and", 5), None),
36 "&": (5, None, None, ("and", 5), None),
37 "%": (5, None, None, ("only", 5), "onlypost"),
37 "%": (5, None, None, ("only", 5), "onlypost"),
38 "or": (4, None, None, ("or", 4), None),
38 "or": (4, None, None, ("or", 4), None),
39 "|": (4, None, None, ("or", 4), None),
39 "|": (4, None, None, ("or", 4), None),
40 "+": (4, None, None, ("or", 4), None),
40 "+": (4, None, None, ("or", 4), None),
41 "=": (3, None, None, ("keyvalue", 3), None),
41 "=": (3, None, None, ("keyvalue", 3), None),
42 ",": (2, None, None, ("list", 2), None),
42 ",": (2, None, None, ("list", 2), None),
43 ")": (0, None, None, None, None),
43 ")": (0, None, None, None, None),
44 "]": (0, None, None, None, None),
44 "]": (0, None, None, None, None),
45 "symbol": (0, "symbol", None, None, None),
45 "symbol": (0, "symbol", None, None, None),
46 "string": (0, "string", None, None, None),
46 "string": (0, "string", None, None, None),
47 "end": (0, None, None, None, None),
47 "end": (0, None, None, None, None),
48 }
48 }
49
49
50 keywords = {'and', 'or', 'not'}
50 keywords = {'and', 'or', 'not'}
51
51
52 _quoteletters = {'"', "'"}
52 _quoteletters = {'"', "'"}
53 _simpleopletters = set(pycompat.iterbytestr("()[]#:=,-|&+!~^%"))
53 _simpleopletters = set(pycompat.iterbytestr("()[]#:=,-|&+!~^%"))
54
54
55 # default set of valid characters for the initial letter of symbols
55 # default set of valid characters for the initial letter of symbols
56 _syminitletters = set(pycompat.iterbytestr(
56 _syminitletters = set(pycompat.iterbytestr(
57 string.ascii_letters.encode('ascii') +
57 string.ascii_letters.encode('ascii') +
58 string.digits.encode('ascii') +
58 string.digits.encode('ascii') +
59 '._@')) | set(map(pycompat.bytechr, xrange(128, 256)))
59 '._@')) | set(map(pycompat.bytechr, xrange(128, 256)))
60
60
61 # default set of valid characters for non-initial letters of symbols
61 # default set of valid characters for non-initial letters of symbols
62 _symletters = _syminitletters | set(pycompat.iterbytestr('-/'))
62 _symletters = _syminitletters | set(pycompat.iterbytestr('-/'))
63
63
64 def tokenize(program, lookup=None, syminitletters=None, symletters=None):
64 def tokenize(program, lookup=None, syminitletters=None, symletters=None):
65 '''
65 '''
66 Parse a revset statement into a stream of tokens
66 Parse a revset statement into a stream of tokens
67
67
68 ``syminitletters`` is the set of valid characters for the initial
68 ``syminitletters`` is the set of valid characters for the initial
69 letter of symbols.
69 letter of symbols.
70
70
71 By default, character ``c`` is recognized as valid for initial
71 By default, character ``c`` is recognized as valid for initial
72 letter of symbols, if ``c.isalnum() or c in '._@' or ord(c) > 127``.
72 letter of symbols, if ``c.isalnum() or c in '._@' or ord(c) > 127``.
73
73
74 ``symletters`` is the set of valid characters for non-initial
74 ``symletters`` is the set of valid characters for non-initial
75 letters of symbols.
75 letters of symbols.
76
76
77 By default, character ``c`` is recognized as valid for non-initial
77 By default, character ``c`` is recognized as valid for non-initial
78 letters of symbols, if ``c.isalnum() or c in '-._/@' or ord(c) > 127``.
78 letters of symbols, if ``c.isalnum() or c in '-._/@' or ord(c) > 127``.
79
79
80 Check that @ is a valid unquoted token character (issue3686):
80 Check that @ is a valid unquoted token character (issue3686):
81 >>> list(tokenize("@::"))
81 >>> list(tokenize("@::"))
82 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
82 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
83
83
84 '''
84 '''
85 program = pycompat.bytestr(program)
85 program = pycompat.bytestr(program)
86 if syminitletters is None:
86 if syminitletters is None:
87 syminitletters = _syminitletters
87 syminitletters = _syminitletters
88 if symletters is None:
88 if symletters is None:
89 symletters = _symletters
89 symletters = _symletters
90
90
91 if program and lookup:
91 if program and lookup:
92 # attempt to parse old-style ranges first to deal with
92 # attempt to parse old-style ranges first to deal with
93 # things like old-tag which contain query metacharacters
93 # things like old-tag which contain query metacharacters
94 parts = program.split(':', 1)
94 parts = program.split(':', 1)
95 if all(lookup(sym) for sym in parts if sym):
95 if all(lookup(sym) for sym in parts if sym):
96 if parts[0]:
96 if parts[0]:
97 yield ('symbol', parts[0], 0)
97 yield ('symbol', parts[0], 0)
98 if len(parts) > 1:
98 if len(parts) > 1:
99 s = len(parts[0])
99 s = len(parts[0])
100 yield (':', None, s)
100 yield (':', None, s)
101 if parts[1]:
101 if parts[1]:
102 yield ('symbol', parts[1], s + 1)
102 yield ('symbol', parts[1], s + 1)
103 yield ('end', None, len(program))
103 yield ('end', None, len(program))
104 return
104 return
105
105
106 pos, l = 0, len(program)
106 pos, l = 0, len(program)
107 while pos < l:
107 while pos < l:
108 c = program[pos]
108 c = program[pos]
109 if c.isspace(): # skip inter-token whitespace
109 if c.isspace(): # skip inter-token whitespace
110 pass
110 pass
111 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
111 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
112 yield ('::', None, pos)
112 yield ('::', None, pos)
113 pos += 1 # skip ahead
113 pos += 1 # skip ahead
114 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
114 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
115 yield ('..', None, pos)
115 yield ('..', None, pos)
116 pos += 1 # skip ahead
116 pos += 1 # skip ahead
117 elif c == '#' and program[pos:pos + 2] == '##': # look ahead carefully
117 elif c == '#' and program[pos:pos + 2] == '##': # look ahead carefully
118 yield ('##', None, pos)
118 yield ('##', None, pos)
119 pos += 1 # skip ahead
119 pos += 1 # skip ahead
120 elif c in _simpleopletters: # handle simple operators
120 elif c in _simpleopletters: # handle simple operators
121 yield (c, None, pos)
121 yield (c, None, pos)
122 elif (c in _quoteletters or c == 'r' and
122 elif (c in _quoteletters or c == 'r' and
123 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
123 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
124 if c == 'r':
124 if c == 'r':
125 pos += 1
125 pos += 1
126 c = program[pos]
126 c = program[pos]
127 decode = lambda x: x
127 decode = lambda x: x
128 else:
128 else:
129 decode = parser.unescapestr
129 decode = parser.unescapestr
130 pos += 1
130 pos += 1
131 s = pos
131 s = pos
132 while pos < l: # find closing quote
132 while pos < l: # find closing quote
133 d = program[pos]
133 d = program[pos]
134 if d == '\\': # skip over escaped characters
134 if d == '\\': # skip over escaped characters
135 pos += 2
135 pos += 2
136 continue
136 continue
137 if d == c:
137 if d == c:
138 yield ('string', decode(program[s:pos]), s)
138 yield ('string', decode(program[s:pos]), s)
139 break
139 break
140 pos += 1
140 pos += 1
141 else:
141 else:
142 raise error.ParseError(_("unterminated string"), s)
142 raise error.ParseError(_("unterminated string"), s)
143 # gather up a symbol/keyword
143 # gather up a symbol/keyword
144 elif c in syminitletters:
144 elif c in syminitletters:
145 s = pos
145 s = pos
146 pos += 1
146 pos += 1
147 while pos < l: # find end of symbol
147 while pos < l: # find end of symbol
148 d = program[pos]
148 d = program[pos]
149 if d not in symletters:
149 if d not in symletters:
150 break
150 break
151 if d == '.' and program[pos - 1] == '.': # special case for ..
151 if d == '.' and program[pos - 1] == '.': # special case for ..
152 pos -= 1
152 pos -= 1
153 break
153 break
154 pos += 1
154 pos += 1
155 sym = program[s:pos]
155 sym = program[s:pos]
156 if sym in keywords: # operator keywords
156 if sym in keywords: # operator keywords
157 yield (sym, None, s)
157 yield (sym, None, s)
158 elif '-' in sym:
158 elif '-' in sym:
159 # some jerk gave us foo-bar-baz, try to check if it's a symbol
159 # some jerk gave us foo-bar-baz, try to check if it's a symbol
160 if lookup and lookup(sym):
160 if lookup and lookup(sym):
161 # looks like a real symbol
161 # looks like a real symbol
162 yield ('symbol', sym, s)
162 yield ('symbol', sym, s)
163 else:
163 else:
164 # looks like an expression
164 # looks like an expression
165 parts = sym.split('-')
165 parts = sym.split('-')
166 for p in parts[:-1]:
166 for p in parts[:-1]:
167 if p: # possible consecutive -
167 if p: # possible consecutive -
168 yield ('symbol', p, s)
168 yield ('symbol', p, s)
169 s += len(p)
169 s += len(p)
170 yield ('-', None, pos)
170 yield ('-', None, pos)
171 s += 1
171 s += 1
172 if parts[-1]: # possible trailing -
172 if parts[-1]: # possible trailing -
173 yield ('symbol', parts[-1], s)
173 yield ('symbol', parts[-1], s)
174 else:
174 else:
175 yield ('symbol', sym, s)
175 yield ('symbol', sym, s)
176 pos -= 1
176 pos -= 1
177 else:
177 else:
178 raise error.ParseError(_("syntax error in revset '%s'") %
178 raise error.ParseError(_("syntax error in revset '%s'") %
179 program, pos)
179 program, pos)
180 pos += 1
180 pos += 1
181 yield ('end', None, pos)
181 yield ('end', None, pos)
182
182
183 # helpers
183 # helpers
184
184
185 _notset = object()
185 _notset = object()
186
186
187 def getsymbol(x):
187 def getsymbol(x):
188 if x and x[0] == 'symbol':
188 if x and x[0] == 'symbol':
189 return x[1]
189 return x[1]
190 raise error.ParseError(_('not a symbol'))
190 raise error.ParseError(_('not a symbol'))
191
191
192 def getstring(x, err):
192 def getstring(x, err):
193 if x and (x[0] == 'string' or x[0] == 'symbol'):
193 if x and (x[0] == 'string' or x[0] == 'symbol'):
194 return x[1]
194 return x[1]
195 raise error.ParseError(err)
195 raise error.ParseError(err)
196
196
197 def getinteger(x, err, default=_notset):
197 def getinteger(x, err, default=_notset):
198 if not x and default is not _notset:
198 if not x and default is not _notset:
199 return default
199 return default
200 try:
200 try:
201 return int(getstring(x, err))
201 return int(getstring(x, err))
202 except ValueError:
202 except ValueError:
203 raise error.ParseError(err)
203 raise error.ParseError(err)
204
204
205 def getboolean(x, err):
205 def getboolean(x, err):
206 value = util.parsebool(getsymbol(x))
206 value = util.parsebool(getsymbol(x))
207 if value is not None:
207 if value is not None:
208 return value
208 return value
209 raise error.ParseError(err)
209 raise error.ParseError(err)
210
210
211 def getlist(x):
211 def getlist(x):
212 if not x:
212 if not x:
213 return []
213 return []
214 if x[0] == 'list':
214 if x[0] == 'list':
215 return list(x[1:])
215 return list(x[1:])
216 return [x]
216 return [x]
217
217
218 def getrange(x, err):
218 def getrange(x, err):
219 if not x:
219 if not x:
220 raise error.ParseError(err)
220 raise error.ParseError(err)
221 op = x[0]
221 op = x[0]
222 if op == 'range':
222 if op == 'range':
223 return x[1], x[2]
223 return x[1], x[2]
224 elif op == 'rangepre':
224 elif op == 'rangepre':
225 return None, x[1]
225 return None, x[1]
226 elif op == 'rangepost':
226 elif op == 'rangepost':
227 return x[1], None
227 return x[1], None
228 elif op == 'rangeall':
228 elif op == 'rangeall':
229 return None, None
229 return None, None
230 raise error.ParseError(err)
230 raise error.ParseError(err)
231
231
232 def getargs(x, min, max, err):
232 def getargs(x, min, max, err):
233 l = getlist(x)
233 l = getlist(x)
234 if len(l) < min or (max >= 0 and len(l) > max):
234 if len(l) < min or (max >= 0 and len(l) > max):
235 raise error.ParseError(err)
235 raise error.ParseError(err)
236 return l
236 return l
237
237
238 def getargsdict(x, funcname, keys):
238 def getargsdict(x, funcname, keys):
239 return parser.buildargsdict(getlist(x), funcname, parser.splitargspec(keys),
239 return parser.buildargsdict(getlist(x), funcname, parser.splitargspec(keys),
240 keyvaluenode='keyvalue', keynode='symbol')
240 keyvaluenode='keyvalue', keynode='symbol')
241
241
242 # cache of {spec: raw parsed tree} built internally
242 # cache of {spec: raw parsed tree} built internally
243 _treecache = {}
243 _treecache = {}
244
244
245 def _cachedtree(spec):
245 def _cachedtree(spec):
246 # thread safe because parse() is reentrant and dict.__setitem__() is atomic
246 # thread safe because parse() is reentrant and dict.__setitem__() is atomic
247 tree = _treecache.get(spec)
247 tree = _treecache.get(spec)
248 if tree is None:
248 if tree is None:
249 _treecache[spec] = tree = parse(spec)
249 _treecache[spec] = tree = parse(spec)
250 return tree
250 return tree
251
251
252 def _build(tmplspec, *repls):
252 def _build(tmplspec, *repls):
253 """Create raw parsed tree from a template revset statement
253 """Create raw parsed tree from a template revset statement
254
254
255 >>> _build('f(_) and _', ('string', '1'), ('symbol', '2'))
255 >>> _build('f(_) and _', ('string', '1'), ('symbol', '2'))
256 ('and', ('func', ('symbol', 'f'), ('string', '1')), ('symbol', '2'))
256 ('and', ('func', ('symbol', 'f'), ('string', '1')), ('symbol', '2'))
257 """
257 """
258 template = _cachedtree(tmplspec)
258 template = _cachedtree(tmplspec)
259 return parser.buildtree(template, ('symbol', '_'), *repls)
259 return parser.buildtree(template, ('symbol', '_'), *repls)
260
260
261 def _match(patspec, tree):
261 def _match(patspec, tree):
262 """Test if a tree matches the given pattern statement; return the matches
262 """Test if a tree matches the given pattern statement; return the matches
263
263
264 >>> _match('f(_)', parse('f()'))
264 >>> _match('f(_)', parse('f()'))
265 >>> _match('f(_)', parse('f(1)'))
265 >>> _match('f(_)', parse('f(1)'))
266 [('func', ('symbol', 'f'), ('symbol', '1')), ('symbol', '1')]
266 [('func', ('symbol', 'f'), ('symbol', '1')), ('symbol', '1')]
267 >>> _match('f(_)', parse('f(1, 2)'))
267 >>> _match('f(_)', parse('f(1, 2)'))
268 """
268 """
269 pattern = _cachedtree(patspec)
269 pattern = _cachedtree(patspec)
270 return parser.matchtree(pattern, tree, ('symbol', '_'),
270 return parser.matchtree(pattern, tree, ('symbol', '_'),
271 {'keyvalue', 'list'})
271 {'keyvalue', 'list'})
272
272
273 def _matchonly(revs, bases):
273 def _matchonly(revs, bases):
274 return _match('ancestors(_) and not ancestors(_)', ('and', revs, bases))
274 return _match('ancestors(_) and not ancestors(_)', ('and', revs, bases))
275
275
276 def _fixops(x):
276 def _fixops(x):
277 """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be
277 """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be
278 handled well by our simple top-down parser"""
278 handled well by our simple top-down parser"""
279 if not isinstance(x, tuple):
279 if not isinstance(x, tuple):
280 return x
280 return x
281
281
282 op = x[0]
282 op = x[0]
283 if op == 'parent':
283 if op == 'parent':
284 # x^:y means (x^) : y, not x ^ (:y)
284 # x^:y means (x^) : y, not x ^ (:y)
285 # x^: means (x^) :, not x ^ (:)
285 # x^: means (x^) :, not x ^ (:)
286 post = ('parentpost', x[1])
286 post = ('parentpost', x[1])
287 if x[2][0] == 'dagrangepre':
287 if x[2][0] == 'dagrangepre':
288 return _fixops(('dagrange', post, x[2][1]))
288 return _fixops(('dagrange', post, x[2][1]))
289 elif x[2][0] == 'rangepre':
289 elif x[2][0] == 'rangepre':
290 return _fixops(('range', post, x[2][1]))
290 return _fixops(('range', post, x[2][1]))
291 elif x[2][0] == 'rangeall':
291 elif x[2][0] == 'rangeall':
292 return _fixops(('rangepost', post))
292 return _fixops(('rangepost', post))
293 elif op == 'or':
293 elif op == 'or':
294 # make number of arguments deterministic:
294 # make number of arguments deterministic:
295 # x + y + z -> (or x y z) -> (or (list x y z))
295 # x + y + z -> (or x y z) -> (or (list x y z))
296 return (op, _fixops(('list',) + x[1:]))
296 return (op, _fixops(('list',) + x[1:]))
297 elif op == 'subscript' and x[1][0] == 'relation':
297 elif op == 'subscript' and x[1][0] == 'relation':
298 # x#y[z] ternary
298 # x#y[z] ternary
299 return _fixops(('relsubscript', x[1][1], x[1][2], x[2]))
299 return _fixops(('relsubscript', x[1][1], x[1][2], x[2]))
300
300
301 return (op,) + tuple(_fixops(y) for y in x[1:])
301 return (op,) + tuple(_fixops(y) for y in x[1:])
302
302
303 def _analyze(x):
303 def _analyze(x):
304 if x is None:
304 if x is None:
305 return x
305 return x
306
306
307 op = x[0]
307 op = x[0]
308 if op == 'minus':
308 if op == 'minus':
309 return _analyze(_build('_ and not _', *x[1:]))
309 return _analyze(_build('_ and not _', *x[1:]))
310 elif op == 'only':
310 elif op == 'only':
311 return _analyze(_build('only(_, _)', *x[1:]))
311 return _analyze(_build('only(_, _)', *x[1:]))
312 elif op == 'onlypost':
312 elif op == 'onlypost':
313 return _analyze(_build('only(_)', x[1]))
313 return _analyze(_build('only(_)', x[1]))
314 elif op == 'dagrangepre':
314 elif op == 'dagrangepre':
315 return _analyze(_build('ancestors(_)', x[1]))
315 return _analyze(_build('ancestors(_)', x[1]))
316 elif op == 'dagrangepost':
316 elif op == 'dagrangepost':
317 return _analyze(_build('descendants(_)', x[1]))
317 return _analyze(_build('descendants(_)', x[1]))
318 elif op == 'negate':
318 elif op == 'negate':
319 s = getstring(x[1], _("can't negate that"))
319 s = getstring(x[1], _("can't negate that"))
320 return _analyze(('string', '-' + s))
320 return _analyze(('string', '-' + s))
321 elif op in ('string', 'symbol'):
321 elif op in ('string', 'symbol'):
322 return x
322 return x
323 elif op == 'rangeall':
323 elif op == 'rangeall':
324 return (op, None)
324 return (op, None)
325 elif op in {'or', 'not', 'rangepre', 'rangepost', 'parentpost'}:
325 elif op in {'or', 'not', 'rangepre', 'rangepost', 'parentpost'}:
326 return (op, _analyze(x[1]))
326 return (op, _analyze(x[1]))
327 elif op == 'group':
327 elif op == 'group':
328 return _analyze(x[1])
328 return _analyze(x[1])
329 elif op in {'and', 'dagrange', 'range', 'parent', 'ancestor', 'relation',
329 elif op in {'and', 'dagrange', 'range', 'parent', 'ancestor', 'relation',
330 'subscript'}:
330 'subscript'}:
331 ta = _analyze(x[1])
331 ta = _analyze(x[1])
332 tb = _analyze(x[2])
332 tb = _analyze(x[2])
333 return (op, ta, tb)
333 return (op, ta, tb)
334 elif op == 'relsubscript':
334 elif op == 'relsubscript':
335 ta = _analyze(x[1])
335 ta = _analyze(x[1])
336 tb = _analyze(x[2])
336 tb = _analyze(x[2])
337 tc = _analyze(x[3])
337 tc = _analyze(x[3])
338 return (op, ta, tb, tc)
338 return (op, ta, tb, tc)
339 elif op == 'list':
339 elif op == 'list':
340 return (op,) + tuple(_analyze(y) for y in x[1:])
340 return (op,) + tuple(_analyze(y) for y in x[1:])
341 elif op == 'keyvalue':
341 elif op == 'keyvalue':
342 return (op, x[1], _analyze(x[2]))
342 return (op, x[1], _analyze(x[2]))
343 elif op == 'func':
343 elif op == 'func':
344 return (op, x[1], _analyze(x[2]))
344 return (op, x[1], _analyze(x[2]))
345 raise ValueError('invalid operator %r' % op)
345 raise ValueError('invalid operator %r' % op)
346
346
347 def analyze(x):
347 def analyze(x):
348 """Transform raw parsed tree to evaluatable tree which can be fed to
348 """Transform raw parsed tree to evaluatable tree which can be fed to
349 optimize() or getset()
349 optimize() or getset()
350
350
351 All pseudo operations should be mapped to real operations or functions
351 All pseudo operations should be mapped to real operations or functions
352 defined in methods or symbols table respectively.
352 defined in methods or symbols table respectively.
353 """
353 """
354 return _analyze(x)
354 return _analyze(x)
355
355
356 def _optimize(x, small):
356 def _optimize(x, small):
357 if x is None:
357 if x is None:
358 return 0, x
358 return 0, x
359
359
360 smallbonus = 1
360 smallbonus = 1
361 if small:
361 if small:
362 smallbonus = .5
362 smallbonus = .5
363
363
364 op = x[0]
364 op = x[0]
365 if op in ('string', 'symbol'):
365 if op in ('string', 'symbol'):
366 return smallbonus, x # single revisions are small
366 return smallbonus, x # single revisions are small
367 elif op == 'and':
367 elif op == 'and':
368 wa, ta = _optimize(x[1], True)
368 wa, ta = _optimize(x[1], True)
369 wb, tb = _optimize(x[2], True)
369 wb, tb = _optimize(x[2], True)
370 w = min(wa, wb)
370 w = min(wa, wb)
371
371
372 # (draft/secret/_notpublic() & ::x) have a fast path
373 m = _match('_() & ancestors(_)', ('and', ta, tb))
374 if m and getsymbol(m[1]) in {'draft', 'secret', '_notpublic'}:
375 return w, _build('_phaseandancestors(_, _)', m[1], m[2])
376
372 # (::x and not ::y)/(not ::y and ::x) have a fast path
377 # (::x and not ::y)/(not ::y and ::x) have a fast path
373 m = _matchonly(ta, tb) or _matchonly(tb, ta)
378 m = _matchonly(ta, tb) or _matchonly(tb, ta)
374 if m:
379 if m:
375 return w, _build('only(_, _)', *m[1:])
380 return w, _build('only(_, _)', *m[1:])
376
381
377 m = _match('not _', tb)
382 m = _match('not _', tb)
378 if m:
383 if m:
379 return wa, ('difference', ta, m[1])
384 return wa, ('difference', ta, m[1])
380 if wa > wb:
385 if wa > wb:
381 op = 'andsmally'
386 op = 'andsmally'
382 return w, (op, ta, tb)
387 return w, (op, ta, tb)
383 elif op == 'or':
388 elif op == 'or':
384 # fast path for machine-generated expression, that is likely to have
389 # fast path for machine-generated expression, that is likely to have
385 # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()'
390 # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()'
386 ws, ts, ss = [], [], []
391 ws, ts, ss = [], [], []
387 def flushss():
392 def flushss():
388 if not ss:
393 if not ss:
389 return
394 return
390 if len(ss) == 1:
395 if len(ss) == 1:
391 w, t = ss[0]
396 w, t = ss[0]
392 else:
397 else:
393 s = '\0'.join(t[1] for w, t in ss)
398 s = '\0'.join(t[1] for w, t in ss)
394 y = _build('_list(_)', ('string', s))
399 y = _build('_list(_)', ('string', s))
395 w, t = _optimize(y, False)
400 w, t = _optimize(y, False)
396 ws.append(w)
401 ws.append(w)
397 ts.append(t)
402 ts.append(t)
398 del ss[:]
403 del ss[:]
399 for y in getlist(x[1]):
404 for y in getlist(x[1]):
400 w, t = _optimize(y, False)
405 w, t = _optimize(y, False)
401 if t is not None and (t[0] == 'string' or t[0] == 'symbol'):
406 if t is not None and (t[0] == 'string' or t[0] == 'symbol'):
402 ss.append((w, t))
407 ss.append((w, t))
403 continue
408 continue
404 flushss()
409 flushss()
405 ws.append(w)
410 ws.append(w)
406 ts.append(t)
411 ts.append(t)
407 flushss()
412 flushss()
408 if len(ts) == 1:
413 if len(ts) == 1:
409 return ws[0], ts[0] # 'or' operation is fully optimized out
414 return ws[0], ts[0] # 'or' operation is fully optimized out
410 return max(ws), (op, ('list',) + tuple(ts))
415 return max(ws), (op, ('list',) + tuple(ts))
411 elif op == 'not':
416 elif op == 'not':
412 # Optimize not public() to _notpublic() because we have a fast version
417 # Optimize not public() to _notpublic() because we have a fast version
413 if _match('public()', x[1]):
418 if _match('public()', x[1]):
414 o = _optimize(_build('_notpublic()'), not small)
419 o = _optimize(_build('_notpublic()'), not small)
415 return o[0], o[1]
420 return o[0], o[1]
416 else:
421 else:
417 o = _optimize(x[1], not small)
422 o = _optimize(x[1], not small)
418 return o[0], (op, o[1])
423 return o[0], (op, o[1])
419 elif op == 'rangeall':
424 elif op == 'rangeall':
420 return smallbonus, x
425 return smallbonus, x
421 elif op in ('rangepre', 'rangepost', 'parentpost'):
426 elif op in ('rangepre', 'rangepost', 'parentpost'):
422 o = _optimize(x[1], small)
427 o = _optimize(x[1], small)
423 return o[0], (op, o[1])
428 return o[0], (op, o[1])
424 elif op in ('dagrange', 'range'):
429 elif op in ('dagrange', 'range'):
425 wa, ta = _optimize(x[1], small)
430 wa, ta = _optimize(x[1], small)
426 wb, tb = _optimize(x[2], small)
431 wb, tb = _optimize(x[2], small)
427 return wa + wb, (op, ta, tb)
432 return wa + wb, (op, ta, tb)
428 elif op in ('parent', 'ancestor', 'relation', 'subscript'):
433 elif op in ('parent', 'ancestor', 'relation', 'subscript'):
429 w, t = _optimize(x[1], small)
434 w, t = _optimize(x[1], small)
430 return w, (op, t, x[2])
435 return w, (op, t, x[2])
431 elif op == 'relsubscript':
436 elif op == 'relsubscript':
432 w, t = _optimize(x[1], small)
437 w, t = _optimize(x[1], small)
433 return w, (op, t, x[2], x[3])
438 return w, (op, t, x[2], x[3])
434 elif op == 'list':
439 elif op == 'list':
435 ws, ts = zip(*(_optimize(y, small) for y in x[1:]))
440 ws, ts = zip(*(_optimize(y, small) for y in x[1:]))
436 return sum(ws), (op,) + ts
441 return sum(ws), (op,) + ts
437 elif op == 'keyvalue':
442 elif op == 'keyvalue':
438 w, t = _optimize(x[2], small)
443 w, t = _optimize(x[2], small)
439 return w, (op, x[1], t)
444 return w, (op, x[1], t)
440 elif op == 'func':
445 elif op == 'func':
441 f = getsymbol(x[1])
446 f = getsymbol(x[1])
442 wa, ta = _optimize(x[2], small)
447 wa, ta = _optimize(x[2], small)
443 if f in ('author', 'branch', 'closed', 'date', 'desc', 'file', 'grep',
448 if f in ('author', 'branch', 'closed', 'date', 'desc', 'file', 'grep',
444 'keyword', 'outgoing', 'user', 'destination'):
449 'keyword', 'outgoing', 'user', 'destination'):
445 w = 10 # slow
450 w = 10 # slow
446 elif f in ('modifies', 'adds', 'removes'):
451 elif f in ('modifies', 'adds', 'removes'):
447 w = 30 # slower
452 w = 30 # slower
448 elif f == "contains":
453 elif f == "contains":
449 w = 100 # very slow
454 w = 100 # very slow
450 elif f == "ancestor":
455 elif f == "ancestor":
451 w = 1 * smallbonus
456 w = 1 * smallbonus
452 elif f in ('reverse', 'limit', 'first', 'wdir', '_intlist'):
457 elif f in ('reverse', 'limit', 'first', 'wdir', '_intlist'):
453 w = 0
458 w = 0
454 elif f == "sort":
459 elif f == "sort":
455 w = 10 # assume most sorts look at changelog
460 w = 10 # assume most sorts look at changelog
456 else:
461 else:
457 w = 1
462 w = 1
458 return w + wa, (op, x[1], ta)
463 return w + wa, (op, x[1], ta)
459 raise ValueError('invalid operator %r' % op)
464 raise ValueError('invalid operator %r' % op)
460
465
461 def optimize(tree):
466 def optimize(tree):
462 """Optimize evaluatable tree
467 """Optimize evaluatable tree
463
468
464 All pseudo operations should be transformed beforehand.
469 All pseudo operations should be transformed beforehand.
465 """
470 """
466 _weight, newtree = _optimize(tree, small=True)
471 _weight, newtree = _optimize(tree, small=True)
467 return newtree
472 return newtree
468
473
469 # the set of valid characters for the initial letter of symbols in
474 # the set of valid characters for the initial letter of symbols in
470 # alias declarations and definitions
475 # alias declarations and definitions
471 _aliassyminitletters = _syminitletters | set(pycompat.sysstr('$'))
476 _aliassyminitletters = _syminitletters | set(pycompat.sysstr('$'))
472
477
473 def _parsewith(spec, lookup=None, syminitletters=None):
478 def _parsewith(spec, lookup=None, syminitletters=None):
474 """Generate a parse tree of given spec with given tokenizing options
479 """Generate a parse tree of given spec with given tokenizing options
475
480
476 >>> _parsewith('foo($1)', syminitletters=_aliassyminitletters)
481 >>> _parsewith('foo($1)', syminitletters=_aliassyminitletters)
477 ('func', ('symbol', 'foo'), ('symbol', '$1'))
482 ('func', ('symbol', 'foo'), ('symbol', '$1'))
478 >>> _parsewith('$1')
483 >>> _parsewith('$1')
479 Traceback (most recent call last):
484 Traceback (most recent call last):
480 ...
485 ...
481 ParseError: ("syntax error in revset '$1'", 0)
486 ParseError: ("syntax error in revset '$1'", 0)
482 >>> _parsewith('foo bar')
487 >>> _parsewith('foo bar')
483 Traceback (most recent call last):
488 Traceback (most recent call last):
484 ...
489 ...
485 ParseError: ('invalid token', 4)
490 ParseError: ('invalid token', 4)
486 """
491 """
487 p = parser.parser(elements)
492 p = parser.parser(elements)
488 tree, pos = p.parse(tokenize(spec, lookup=lookup,
493 tree, pos = p.parse(tokenize(spec, lookup=lookup,
489 syminitletters=syminitletters))
494 syminitletters=syminitletters))
490 if pos != len(spec):
495 if pos != len(spec):
491 raise error.ParseError(_('invalid token'), pos)
496 raise error.ParseError(_('invalid token'), pos)
492 return _fixops(parser.simplifyinfixops(tree, ('list', 'or')))
497 return _fixops(parser.simplifyinfixops(tree, ('list', 'or')))
493
498
494 class _aliasrules(parser.basealiasrules):
499 class _aliasrules(parser.basealiasrules):
495 """Parsing and expansion rule set of revset aliases"""
500 """Parsing and expansion rule set of revset aliases"""
496 _section = _('revset alias')
501 _section = _('revset alias')
497
502
498 @staticmethod
503 @staticmethod
499 def _parse(spec):
504 def _parse(spec):
500 """Parse alias declaration/definition ``spec``
505 """Parse alias declaration/definition ``spec``
501
506
502 This allows symbol names to use also ``$`` as an initial letter
507 This allows symbol names to use also ``$`` as an initial letter
503 (for backward compatibility), and callers of this function should
508 (for backward compatibility), and callers of this function should
504 examine whether ``$`` is used also for unexpected symbols or not.
509 examine whether ``$`` is used also for unexpected symbols or not.
505 """
510 """
506 return _parsewith(spec, syminitletters=_aliassyminitletters)
511 return _parsewith(spec, syminitletters=_aliassyminitletters)
507
512
508 @staticmethod
513 @staticmethod
509 def _trygetfunc(tree):
514 def _trygetfunc(tree):
510 if tree[0] == 'func' and tree[1][0] == 'symbol':
515 if tree[0] == 'func' and tree[1][0] == 'symbol':
511 return tree[1][1], getlist(tree[2])
516 return tree[1][1], getlist(tree[2])
512
517
513 def expandaliases(tree, aliases, warn=None):
518 def expandaliases(tree, aliases, warn=None):
514 """Expand aliases in a tree, aliases is a list of (name, value) tuples"""
519 """Expand aliases in a tree, aliases is a list of (name, value) tuples"""
515 aliases = _aliasrules.buildmap(aliases)
520 aliases = _aliasrules.buildmap(aliases)
516 tree = _aliasrules.expand(aliases, tree)
521 tree = _aliasrules.expand(aliases, tree)
517 # warn about problematic (but not referred) aliases
522 # warn about problematic (but not referred) aliases
518 if warn is not None:
523 if warn is not None:
519 for name, alias in sorted(aliases.iteritems()):
524 for name, alias in sorted(aliases.iteritems()):
520 if alias.error and not alias.warned:
525 if alias.error and not alias.warned:
521 warn(_('warning: %s\n') % (alias.error))
526 warn(_('warning: %s\n') % (alias.error))
522 alias.warned = True
527 alias.warned = True
523 return tree
528 return tree
524
529
525 def foldconcat(tree):
530 def foldconcat(tree):
526 """Fold elements to be concatenated by `##`
531 """Fold elements to be concatenated by `##`
527 """
532 """
528 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
533 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
529 return tree
534 return tree
530 if tree[0] == '_concat':
535 if tree[0] == '_concat':
531 pending = [tree]
536 pending = [tree]
532 l = []
537 l = []
533 while pending:
538 while pending:
534 e = pending.pop()
539 e = pending.pop()
535 if e[0] == '_concat':
540 if e[0] == '_concat':
536 pending.extend(reversed(e[1:]))
541 pending.extend(reversed(e[1:]))
537 elif e[0] in ('string', 'symbol'):
542 elif e[0] in ('string', 'symbol'):
538 l.append(e[1])
543 l.append(e[1])
539 else:
544 else:
540 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
545 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
541 raise error.ParseError(msg)
546 raise error.ParseError(msg)
542 return ('string', ''.join(l))
547 return ('string', ''.join(l))
543 else:
548 else:
544 return tuple(foldconcat(t) for t in tree)
549 return tuple(foldconcat(t) for t in tree)
545
550
546 def parse(spec, lookup=None):
551 def parse(spec, lookup=None):
547 return _parsewith(spec, lookup=lookup)
552 return _parsewith(spec, lookup=lookup)
548
553
549 def _quote(s):
554 def _quote(s):
550 r"""Quote a value in order to make it safe for the revset engine.
555 r"""Quote a value in order to make it safe for the revset engine.
551
556
552 >>> _quote('asdf')
557 >>> _quote('asdf')
553 "'asdf'"
558 "'asdf'"
554 >>> _quote("asdf'\"")
559 >>> _quote("asdf'\"")
555 '\'asdf\\\'"\''
560 '\'asdf\\\'"\''
556 >>> _quote('asdf\'')
561 >>> _quote('asdf\'')
557 "'asdf\\''"
562 "'asdf\\''"
558 >>> _quote(1)
563 >>> _quote(1)
559 "'1'"
564 "'1'"
560 """
565 """
561 return "'%s'" % util.escapestr(pycompat.bytestr(s))
566 return "'%s'" % util.escapestr(pycompat.bytestr(s))
562
567
563 def formatspec(expr, *args):
568 def formatspec(expr, *args):
564 '''
569 '''
565 This is a convenience function for using revsets internally, and
570 This is a convenience function for using revsets internally, and
566 escapes arguments appropriately. Aliases are intentionally ignored
571 escapes arguments appropriately. Aliases are intentionally ignored
567 so that intended expression behavior isn't accidentally subverted.
572 so that intended expression behavior isn't accidentally subverted.
568
573
569 Supported arguments:
574 Supported arguments:
570
575
571 %r = revset expression, parenthesized
576 %r = revset expression, parenthesized
572 %d = int(arg), no quoting
577 %d = int(arg), no quoting
573 %s = string(arg), escaped and single-quoted
578 %s = string(arg), escaped and single-quoted
574 %b = arg.branch(), escaped and single-quoted
579 %b = arg.branch(), escaped and single-quoted
575 %n = hex(arg), single-quoted
580 %n = hex(arg), single-quoted
576 %% = a literal '%'
581 %% = a literal '%'
577
582
578 Prefixing the type with 'l' specifies a parenthesized list of that type.
583 Prefixing the type with 'l' specifies a parenthesized list of that type.
579
584
580 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
585 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
581 '(10 or 11):: and ((this()) or (that()))'
586 '(10 or 11):: and ((this()) or (that()))'
582 >>> formatspec('%d:: and not %d::', 10, 20)
587 >>> formatspec('%d:: and not %d::', 10, 20)
583 '10:: and not 20::'
588 '10:: and not 20::'
584 >>> formatspec('%ld or %ld', [], [1])
589 >>> formatspec('%ld or %ld', [], [1])
585 "_list('') or 1"
590 "_list('') or 1"
586 >>> formatspec('keyword(%s)', 'foo\\xe9')
591 >>> formatspec('keyword(%s)', 'foo\\xe9')
587 "keyword('foo\\\\xe9')"
592 "keyword('foo\\\\xe9')"
588 >>> b = lambda: 'default'
593 >>> b = lambda: 'default'
589 >>> b.branch = b
594 >>> b.branch = b
590 >>> formatspec('branch(%b)', b)
595 >>> formatspec('branch(%b)', b)
591 "branch('default')"
596 "branch('default')"
592 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
597 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
593 "root(_list('a\\x00b\\x00c\\x00d'))"
598 "root(_list('a\\x00b\\x00c\\x00d'))"
594 '''
599 '''
595
600
596 def argtype(c, arg):
601 def argtype(c, arg):
597 if c == 'd':
602 if c == 'd':
598 return '%d' % int(arg)
603 return '%d' % int(arg)
599 elif c == 's':
604 elif c == 's':
600 return _quote(arg)
605 return _quote(arg)
601 elif c == 'r':
606 elif c == 'r':
602 parse(arg) # make sure syntax errors are confined
607 parse(arg) # make sure syntax errors are confined
603 return '(%s)' % arg
608 return '(%s)' % arg
604 elif c == 'n':
609 elif c == 'n':
605 return _quote(node.hex(arg))
610 return _quote(node.hex(arg))
606 elif c == 'b':
611 elif c == 'b':
607 return _quote(arg.branch())
612 return _quote(arg.branch())
608
613
609 def listexp(s, t):
614 def listexp(s, t):
610 l = len(s)
615 l = len(s)
611 if l == 0:
616 if l == 0:
612 return "_list('')"
617 return "_list('')"
613 elif l == 1:
618 elif l == 1:
614 return argtype(t, s[0])
619 return argtype(t, s[0])
615 elif t == 'd':
620 elif t == 'd':
616 return "_intlist('%s')" % "\0".join('%d' % int(a) for a in s)
621 return "_intlist('%s')" % "\0".join('%d' % int(a) for a in s)
617 elif t == 's':
622 elif t == 's':
618 return "_list('%s')" % "\0".join(s)
623 return "_list('%s')" % "\0".join(s)
619 elif t == 'n':
624 elif t == 'n':
620 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
625 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
621 elif t == 'b':
626 elif t == 'b':
622 return "_list('%s')" % "\0".join(a.branch() for a in s)
627 return "_list('%s')" % "\0".join(a.branch() for a in s)
623
628
624 m = l // 2
629 m = l // 2
625 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
630 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
626
631
627 expr = pycompat.bytestr(expr)
632 expr = pycompat.bytestr(expr)
628 ret = ''
633 ret = ''
629 pos = 0
634 pos = 0
630 arg = 0
635 arg = 0
631 while pos < len(expr):
636 while pos < len(expr):
632 c = expr[pos]
637 c = expr[pos]
633 if c == '%':
638 if c == '%':
634 pos += 1
639 pos += 1
635 d = expr[pos]
640 d = expr[pos]
636 if d == '%':
641 if d == '%':
637 ret += d
642 ret += d
638 elif d in 'dsnbr':
643 elif d in 'dsnbr':
639 ret += argtype(d, args[arg])
644 ret += argtype(d, args[arg])
640 arg += 1
645 arg += 1
641 elif d == 'l':
646 elif d == 'l':
642 # a list of some type
647 # a list of some type
643 pos += 1
648 pos += 1
644 d = expr[pos]
649 d = expr[pos]
645 ret += listexp(list(args[arg]), d)
650 ret += listexp(list(args[arg]), d)
646 arg += 1
651 arg += 1
647 else:
652 else:
648 raise error.Abort(_('unexpected revspec format character %s')
653 raise error.Abort(_('unexpected revspec format character %s')
649 % d)
654 % d)
650 else:
655 else:
651 ret += c
656 ret += c
652 pos += 1
657 pos += 1
653
658
654 return ret
659 return ret
655
660
656 def prettyformat(tree):
661 def prettyformat(tree):
657 return parser.prettyformat(tree, ('string', 'symbol'))
662 return parser.prettyformat(tree, ('string', 'symbol'))
658
663
659 def depth(tree):
664 def depth(tree):
660 if isinstance(tree, tuple):
665 if isinstance(tree, tuple):
661 return max(map(depth, tree)) + 1
666 return max(map(depth, tree)) + 1
662 else:
667 else:
663 return 0
668 return 0
664
669
665 def funcsused(tree):
670 def funcsused(tree):
666 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
671 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
667 return set()
672 return set()
668 else:
673 else:
669 funcs = set()
674 funcs = set()
670 for s in tree[1:]:
675 for s in tree[1:]:
671 funcs |= funcsused(s)
676 funcs |= funcsused(s)
672 if tree[0] == 'func':
677 if tree[0] == 'func':
673 funcs.add(tree[1][1])
678 funcs.add(tree[1][1])
674 return funcs
679 return funcs
@@ -1,4323 +1,4467 b''
1 $ HGENCODING=utf-8
1 $ HGENCODING=utf-8
2 $ export HGENCODING
2 $ export HGENCODING
3 $ cat > testrevset.py << EOF
3 $ cat > testrevset.py << EOF
4 > import mercurial.revset
4 > import mercurial.revset
5 >
5 >
6 > baseset = mercurial.revset.baseset
6 > baseset = mercurial.revset.baseset
7 >
7 >
8 > def r3232(repo, subset, x):
8 > def r3232(repo, subset, x):
9 > """"simple revset that return [3,2,3,2]
9 > """"simple revset that return [3,2,3,2]
10 >
10 >
11 > revisions duplicated on purpose.
11 > revisions duplicated on purpose.
12 > """
12 > """
13 > if 3 not in subset:
13 > if 3 not in subset:
14 > if 2 in subset:
14 > if 2 in subset:
15 > return baseset([2,2])
15 > return baseset([2,2])
16 > return baseset()
16 > return baseset()
17 > return baseset([3,3,2,2])
17 > return baseset([3,3,2,2])
18 >
18 >
19 > mercurial.revset.symbols['r3232'] = r3232
19 > mercurial.revset.symbols['r3232'] = r3232
20 > EOF
20 > EOF
21 $ cat >> $HGRCPATH << EOF
21 $ cat >> $HGRCPATH << EOF
22 > [extensions]
22 > [extensions]
23 > drawdag=$TESTDIR/drawdag.py
23 > drawdag=$TESTDIR/drawdag.py
24 > testrevset=$TESTTMP/testrevset.py
24 > testrevset=$TESTTMP/testrevset.py
25 > EOF
25 > EOF
26
26
27 $ try() {
27 $ try() {
28 > hg debugrevspec --debug "$@"
28 > hg debugrevspec --debug "$@"
29 > }
29 > }
30
30
31 $ log() {
31 $ log() {
32 > hg log --template '{rev}\n' -r "$1"
32 > hg log --template '{rev}\n' -r "$1"
33 > }
33 > }
34
34
35 extension to build '_intlist()' and '_hexlist()', which is necessary because
35 extension to build '_intlist()' and '_hexlist()', which is necessary because
36 these predicates use '\0' as a separator:
36 these predicates use '\0' as a separator:
37
37
38 $ cat <<EOF > debugrevlistspec.py
38 $ cat <<EOF > debugrevlistspec.py
39 > from __future__ import absolute_import
39 > from __future__ import absolute_import
40 > from mercurial import (
40 > from mercurial import (
41 > node as nodemod,
41 > node as nodemod,
42 > registrar,
42 > registrar,
43 > revset,
43 > revset,
44 > revsetlang,
44 > revsetlang,
45 > smartset,
45 > smartset,
46 > )
46 > )
47 > cmdtable = {}
47 > cmdtable = {}
48 > command = registrar.command(cmdtable)
48 > command = registrar.command(cmdtable)
49 > @command(b'debugrevlistspec',
49 > @command(b'debugrevlistspec',
50 > [('', 'optimize', None, 'print parsed tree after optimizing'),
50 > [('', 'optimize', None, 'print parsed tree after optimizing'),
51 > ('', 'bin', None, 'unhexlify arguments')])
51 > ('', 'bin', None, 'unhexlify arguments')])
52 > def debugrevlistspec(ui, repo, fmt, *args, **opts):
52 > def debugrevlistspec(ui, repo, fmt, *args, **opts):
53 > if opts['bin']:
53 > if opts['bin']:
54 > args = map(nodemod.bin, args)
54 > args = map(nodemod.bin, args)
55 > expr = revsetlang.formatspec(fmt, list(args))
55 > expr = revsetlang.formatspec(fmt, list(args))
56 > if ui.verbose:
56 > if ui.verbose:
57 > tree = revsetlang.parse(expr, lookup=repo.__contains__)
57 > tree = revsetlang.parse(expr, lookup=repo.__contains__)
58 > ui.note(revsetlang.prettyformat(tree), "\n")
58 > ui.note(revsetlang.prettyformat(tree), "\n")
59 > if opts["optimize"]:
59 > if opts["optimize"]:
60 > opttree = revsetlang.optimize(revsetlang.analyze(tree))
60 > opttree = revsetlang.optimize(revsetlang.analyze(tree))
61 > ui.note("* optimized:\n", revsetlang.prettyformat(opttree),
61 > ui.note("* optimized:\n", revsetlang.prettyformat(opttree),
62 > "\n")
62 > "\n")
63 > func = revset.match(ui, expr, repo)
63 > func = revset.match(ui, expr, repo)
64 > revs = func(repo)
64 > revs = func(repo)
65 > if ui.verbose:
65 > if ui.verbose:
66 > ui.note("* set:\n", smartset.prettyformat(revs), "\n")
66 > ui.note("* set:\n", smartset.prettyformat(revs), "\n")
67 > for c in revs:
67 > for c in revs:
68 > ui.write("%s\n" % c)
68 > ui.write("%s\n" % c)
69 > EOF
69 > EOF
70 $ cat <<EOF >> $HGRCPATH
70 $ cat <<EOF >> $HGRCPATH
71 > [extensions]
71 > [extensions]
72 > debugrevlistspec = $TESTTMP/debugrevlistspec.py
72 > debugrevlistspec = $TESTTMP/debugrevlistspec.py
73 > EOF
73 > EOF
74 $ trylist() {
74 $ trylist() {
75 > hg debugrevlistspec --debug "$@"
75 > hg debugrevlistspec --debug "$@"
76 > }
76 > }
77
77
78 $ hg init repo
78 $ hg init repo
79 $ cd repo
79 $ cd repo
80
80
81 $ echo a > a
81 $ echo a > a
82 $ hg branch a
82 $ hg branch a
83 marked working directory as branch a
83 marked working directory as branch a
84 (branches are permanent and global, did you want a bookmark?)
84 (branches are permanent and global, did you want a bookmark?)
85 $ hg ci -Aqm0
85 $ hg ci -Aqm0
86
86
87 $ echo b > b
87 $ echo b > b
88 $ hg branch b
88 $ hg branch b
89 marked working directory as branch b
89 marked working directory as branch b
90 $ hg ci -Aqm1
90 $ hg ci -Aqm1
91
91
92 $ rm a
92 $ rm a
93 $ hg branch a-b-c-
93 $ hg branch a-b-c-
94 marked working directory as branch a-b-c-
94 marked working directory as branch a-b-c-
95 $ hg ci -Aqm2 -u Bob
95 $ hg ci -Aqm2 -u Bob
96
96
97 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
97 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
98 2
98 2
99 $ hg log -r "extra('branch')" --template '{rev}\n'
99 $ hg log -r "extra('branch')" --template '{rev}\n'
100 0
100 0
101 1
101 1
102 2
102 2
103 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
103 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
104 0 a
104 0 a
105 2 a-b-c-
105 2 a-b-c-
106
106
107 $ hg co 1
107 $ hg co 1
108 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
108 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
109 $ hg branch +a+b+c+
109 $ hg branch +a+b+c+
110 marked working directory as branch +a+b+c+
110 marked working directory as branch +a+b+c+
111 $ hg ci -Aqm3
111 $ hg ci -Aqm3
112
112
113 $ hg co 2 # interleave
113 $ hg co 2 # interleave
114 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
114 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
115 $ echo bb > b
115 $ echo bb > b
116 $ hg branch -- -a-b-c-
116 $ hg branch -- -a-b-c-
117 marked working directory as branch -a-b-c-
117 marked working directory as branch -a-b-c-
118 $ hg ci -Aqm4 -d "May 12 2005"
118 $ hg ci -Aqm4 -d "May 12 2005"
119
119
120 $ hg co 3
120 $ hg co 3
121 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
121 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
122 $ hg branch !a/b/c/
122 $ hg branch !a/b/c/
123 marked working directory as branch !a/b/c/
123 marked working directory as branch !a/b/c/
124 $ hg ci -Aqm"5 bug"
124 $ hg ci -Aqm"5 bug"
125
125
126 $ hg merge 4
126 $ hg merge 4
127 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
127 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
128 (branch merge, don't forget to commit)
128 (branch merge, don't forget to commit)
129 $ hg branch _a_b_c_
129 $ hg branch _a_b_c_
130 marked working directory as branch _a_b_c_
130 marked working directory as branch _a_b_c_
131 $ hg ci -Aqm"6 issue619"
131 $ hg ci -Aqm"6 issue619"
132
132
133 $ hg branch .a.b.c.
133 $ hg branch .a.b.c.
134 marked working directory as branch .a.b.c.
134 marked working directory as branch .a.b.c.
135 $ hg ci -Aqm7
135 $ hg ci -Aqm7
136
136
137 $ hg branch all
137 $ hg branch all
138 marked working directory as branch all
138 marked working directory as branch all
139
139
140 $ hg co 4
140 $ hg co 4
141 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
141 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
142 $ hg branch Γ©
142 $ hg branch Γ©
143 marked working directory as branch \xc3\xa9 (esc)
143 marked working directory as branch \xc3\xa9 (esc)
144 $ hg ci -Aqm9
144 $ hg ci -Aqm9
145
145
146 $ hg tag -r6 1.0
146 $ hg tag -r6 1.0
147 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
147 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
148
148
149 $ hg clone --quiet -U -r 7 . ../remote1
149 $ hg clone --quiet -U -r 7 . ../remote1
150 $ hg clone --quiet -U -r 8 . ../remote2
150 $ hg clone --quiet -U -r 8 . ../remote2
151 $ echo "[paths]" >> .hg/hgrc
151 $ echo "[paths]" >> .hg/hgrc
152 $ echo "default = ../remote1" >> .hg/hgrc
152 $ echo "default = ../remote1" >> .hg/hgrc
153
153
154 trivial
154 trivial
155
155
156 $ try 0:1
156 $ try 0:1
157 (range
157 (range
158 ('symbol', '0')
158 ('symbol', '0')
159 ('symbol', '1'))
159 ('symbol', '1'))
160 * set:
160 * set:
161 <spanset+ 0:2>
161 <spanset+ 0:2>
162 0
162 0
163 1
163 1
164 $ try --optimize :
164 $ try --optimize :
165 (rangeall
165 (rangeall
166 None)
166 None)
167 * optimized:
167 * optimized:
168 (rangeall
168 (rangeall
169 None)
169 None)
170 * set:
170 * set:
171 <spanset+ 0:10>
171 <spanset+ 0:10>
172 0
172 0
173 1
173 1
174 2
174 2
175 3
175 3
176 4
176 4
177 5
177 5
178 6
178 6
179 7
179 7
180 8
180 8
181 9
181 9
182 $ try 3::6
182 $ try 3::6
183 (dagrange
183 (dagrange
184 ('symbol', '3')
184 ('symbol', '3')
185 ('symbol', '6'))
185 ('symbol', '6'))
186 * set:
186 * set:
187 <baseset+ [3, 5, 6]>
187 <baseset+ [3, 5, 6]>
188 3
188 3
189 5
189 5
190 6
190 6
191 $ try '0|1|2'
191 $ try '0|1|2'
192 (or
192 (or
193 (list
193 (list
194 ('symbol', '0')
194 ('symbol', '0')
195 ('symbol', '1')
195 ('symbol', '1')
196 ('symbol', '2')))
196 ('symbol', '2')))
197 * set:
197 * set:
198 <baseset [0, 1, 2]>
198 <baseset [0, 1, 2]>
199 0
199 0
200 1
200 1
201 2
201 2
202
202
203 names that should work without quoting
203 names that should work without quoting
204
204
205 $ try a
205 $ try a
206 ('symbol', 'a')
206 ('symbol', 'a')
207 * set:
207 * set:
208 <baseset [0]>
208 <baseset [0]>
209 0
209 0
210 $ try b-a
210 $ try b-a
211 (minus
211 (minus
212 ('symbol', 'b')
212 ('symbol', 'b')
213 ('symbol', 'a'))
213 ('symbol', 'a'))
214 * set:
214 * set:
215 <filteredset
215 <filteredset
216 <baseset [1]>,
216 <baseset [1]>,
217 <not
217 <not
218 <baseset [0]>>>
218 <baseset [0]>>>
219 1
219 1
220 $ try _a_b_c_
220 $ try _a_b_c_
221 ('symbol', '_a_b_c_')
221 ('symbol', '_a_b_c_')
222 * set:
222 * set:
223 <baseset [6]>
223 <baseset [6]>
224 6
224 6
225 $ try _a_b_c_-a
225 $ try _a_b_c_-a
226 (minus
226 (minus
227 ('symbol', '_a_b_c_')
227 ('symbol', '_a_b_c_')
228 ('symbol', 'a'))
228 ('symbol', 'a'))
229 * set:
229 * set:
230 <filteredset
230 <filteredset
231 <baseset [6]>,
231 <baseset [6]>,
232 <not
232 <not
233 <baseset [0]>>>
233 <baseset [0]>>>
234 6
234 6
235 $ try .a.b.c.
235 $ try .a.b.c.
236 ('symbol', '.a.b.c.')
236 ('symbol', '.a.b.c.')
237 * set:
237 * set:
238 <baseset [7]>
238 <baseset [7]>
239 7
239 7
240 $ try .a.b.c.-a
240 $ try .a.b.c.-a
241 (minus
241 (minus
242 ('symbol', '.a.b.c.')
242 ('symbol', '.a.b.c.')
243 ('symbol', 'a'))
243 ('symbol', 'a'))
244 * set:
244 * set:
245 <filteredset
245 <filteredset
246 <baseset [7]>,
246 <baseset [7]>,
247 <not
247 <not
248 <baseset [0]>>>
248 <baseset [0]>>>
249 7
249 7
250
250
251 names that should be caught by fallback mechanism
251 names that should be caught by fallback mechanism
252
252
253 $ try -- '-a-b-c-'
253 $ try -- '-a-b-c-'
254 ('symbol', '-a-b-c-')
254 ('symbol', '-a-b-c-')
255 * set:
255 * set:
256 <baseset [4]>
256 <baseset [4]>
257 4
257 4
258 $ log -a-b-c-
258 $ log -a-b-c-
259 4
259 4
260 $ try '+a+b+c+'
260 $ try '+a+b+c+'
261 ('symbol', '+a+b+c+')
261 ('symbol', '+a+b+c+')
262 * set:
262 * set:
263 <baseset [3]>
263 <baseset [3]>
264 3
264 3
265 $ try '+a+b+c+:'
265 $ try '+a+b+c+:'
266 (rangepost
266 (rangepost
267 ('symbol', '+a+b+c+'))
267 ('symbol', '+a+b+c+'))
268 * set:
268 * set:
269 <spanset+ 3:10>
269 <spanset+ 3:10>
270 3
270 3
271 4
271 4
272 5
272 5
273 6
273 6
274 7
274 7
275 8
275 8
276 9
276 9
277 $ try ':+a+b+c+'
277 $ try ':+a+b+c+'
278 (rangepre
278 (rangepre
279 ('symbol', '+a+b+c+'))
279 ('symbol', '+a+b+c+'))
280 * set:
280 * set:
281 <spanset+ 0:4>
281 <spanset+ 0:4>
282 0
282 0
283 1
283 1
284 2
284 2
285 3
285 3
286 $ try -- '-a-b-c-:+a+b+c+'
286 $ try -- '-a-b-c-:+a+b+c+'
287 (range
287 (range
288 ('symbol', '-a-b-c-')
288 ('symbol', '-a-b-c-')
289 ('symbol', '+a+b+c+'))
289 ('symbol', '+a+b+c+'))
290 * set:
290 * set:
291 <spanset- 3:5>
291 <spanset- 3:5>
292 4
292 4
293 3
293 3
294 $ log '-a-b-c-:+a+b+c+'
294 $ log '-a-b-c-:+a+b+c+'
295 4
295 4
296 3
296 3
297
297
298 $ try -- -a-b-c--a # complains
298 $ try -- -a-b-c--a # complains
299 (minus
299 (minus
300 (minus
300 (minus
301 (minus
301 (minus
302 (negate
302 (negate
303 ('symbol', 'a'))
303 ('symbol', 'a'))
304 ('symbol', 'b'))
304 ('symbol', 'b'))
305 ('symbol', 'c'))
305 ('symbol', 'c'))
306 (negate
306 (negate
307 ('symbol', 'a')))
307 ('symbol', 'a')))
308 abort: unknown revision '-a'!
308 abort: unknown revision '-a'!
309 [255]
309 [255]
310 $ try Γ©
310 $ try Γ©
311 ('symbol', '\xc3\xa9')
311 ('symbol', '\xc3\xa9')
312 * set:
312 * set:
313 <baseset [9]>
313 <baseset [9]>
314 9
314 9
315
315
316 no quoting needed
316 no quoting needed
317
317
318 $ log ::a-b-c-
318 $ log ::a-b-c-
319 0
319 0
320 1
320 1
321 2
321 2
322
322
323 quoting needed
323 quoting needed
324
324
325 $ try '"-a-b-c-"-a'
325 $ try '"-a-b-c-"-a'
326 (minus
326 (minus
327 ('string', '-a-b-c-')
327 ('string', '-a-b-c-')
328 ('symbol', 'a'))
328 ('symbol', 'a'))
329 * set:
329 * set:
330 <filteredset
330 <filteredset
331 <baseset [4]>,
331 <baseset [4]>,
332 <not
332 <not
333 <baseset [0]>>>
333 <baseset [0]>>>
334 4
334 4
335
335
336 $ log '1 or 2'
336 $ log '1 or 2'
337 1
337 1
338 2
338 2
339 $ log '1|2'
339 $ log '1|2'
340 1
340 1
341 2
341 2
342 $ log '1 and 2'
342 $ log '1 and 2'
343 $ log '1&2'
343 $ log '1&2'
344 $ try '1&2|3' # precedence - and is higher
344 $ try '1&2|3' # precedence - and is higher
345 (or
345 (or
346 (list
346 (list
347 (and
347 (and
348 ('symbol', '1')
348 ('symbol', '1')
349 ('symbol', '2'))
349 ('symbol', '2'))
350 ('symbol', '3')))
350 ('symbol', '3')))
351 * set:
351 * set:
352 <addset
352 <addset
353 <baseset []>,
353 <baseset []>,
354 <baseset [3]>>
354 <baseset [3]>>
355 3
355 3
356 $ try '1|2&3'
356 $ try '1|2&3'
357 (or
357 (or
358 (list
358 (list
359 ('symbol', '1')
359 ('symbol', '1')
360 (and
360 (and
361 ('symbol', '2')
361 ('symbol', '2')
362 ('symbol', '3'))))
362 ('symbol', '3'))))
363 * set:
363 * set:
364 <addset
364 <addset
365 <baseset [1]>,
365 <baseset [1]>,
366 <baseset []>>
366 <baseset []>>
367 1
367 1
368 $ try '1&2&3' # associativity
368 $ try '1&2&3' # associativity
369 (and
369 (and
370 (and
370 (and
371 ('symbol', '1')
371 ('symbol', '1')
372 ('symbol', '2'))
372 ('symbol', '2'))
373 ('symbol', '3'))
373 ('symbol', '3'))
374 * set:
374 * set:
375 <baseset []>
375 <baseset []>
376 $ try '1|(2|3)'
376 $ try '1|(2|3)'
377 (or
377 (or
378 (list
378 (list
379 ('symbol', '1')
379 ('symbol', '1')
380 (group
380 (group
381 (or
381 (or
382 (list
382 (list
383 ('symbol', '2')
383 ('symbol', '2')
384 ('symbol', '3'))))))
384 ('symbol', '3'))))))
385 * set:
385 * set:
386 <addset
386 <addset
387 <baseset [1]>,
387 <baseset [1]>,
388 <baseset [2, 3]>>
388 <baseset [2, 3]>>
389 1
389 1
390 2
390 2
391 3
391 3
392 $ log '1.0' # tag
392 $ log '1.0' # tag
393 6
393 6
394 $ log 'a' # branch
394 $ log 'a' # branch
395 0
395 0
396 $ log '2785f51ee'
396 $ log '2785f51ee'
397 0
397 0
398 $ log 'date(2005)'
398 $ log 'date(2005)'
399 4
399 4
400 $ log 'date(this is a test)'
400 $ log 'date(this is a test)'
401 hg: parse error at 10: unexpected token: symbol
401 hg: parse error at 10: unexpected token: symbol
402 [255]
402 [255]
403 $ log 'date()'
403 $ log 'date()'
404 hg: parse error: date requires a string
404 hg: parse error: date requires a string
405 [255]
405 [255]
406 $ log 'date'
406 $ log 'date'
407 abort: unknown revision 'date'!
407 abort: unknown revision 'date'!
408 [255]
408 [255]
409 $ log 'date('
409 $ log 'date('
410 hg: parse error at 5: not a prefix: end
410 hg: parse error at 5: not a prefix: end
411 [255]
411 [255]
412 $ log 'date("\xy")'
412 $ log 'date("\xy")'
413 hg: parse error: invalid \x escape
413 hg: parse error: invalid \x escape
414 [255]
414 [255]
415 $ log 'date(tip)'
415 $ log 'date(tip)'
416 hg: parse error: invalid date: 'tip'
416 hg: parse error: invalid date: 'tip'
417 [255]
417 [255]
418 $ log '0:date'
418 $ log '0:date'
419 abort: unknown revision 'date'!
419 abort: unknown revision 'date'!
420 [255]
420 [255]
421 $ log '::"date"'
421 $ log '::"date"'
422 abort: unknown revision 'date'!
422 abort: unknown revision 'date'!
423 [255]
423 [255]
424 $ hg book date -r 4
424 $ hg book date -r 4
425 $ log '0:date'
425 $ log '0:date'
426 0
426 0
427 1
427 1
428 2
428 2
429 3
429 3
430 4
430 4
431 $ log '::date'
431 $ log '::date'
432 0
432 0
433 1
433 1
434 2
434 2
435 4
435 4
436 $ log '::"date"'
436 $ log '::"date"'
437 0
437 0
438 1
438 1
439 2
439 2
440 4
440 4
441 $ log 'date(2005) and 1::'
441 $ log 'date(2005) and 1::'
442 4
442 4
443 $ hg book -d date
443 $ hg book -d date
444
444
445 function name should be a symbol
445 function name should be a symbol
446
446
447 $ log '"date"(2005)'
447 $ log '"date"(2005)'
448 hg: parse error: not a symbol
448 hg: parse error: not a symbol
449 [255]
449 [255]
450
450
451 keyword arguments
451 keyword arguments
452
452
453 $ log 'extra(branch, value=a)'
453 $ log 'extra(branch, value=a)'
454 0
454 0
455
455
456 $ log 'extra(branch, a, b)'
456 $ log 'extra(branch, a, b)'
457 hg: parse error: extra takes at most 2 positional arguments
457 hg: parse error: extra takes at most 2 positional arguments
458 [255]
458 [255]
459 $ log 'extra(a, label=b)'
459 $ log 'extra(a, label=b)'
460 hg: parse error: extra got multiple values for keyword argument 'label'
460 hg: parse error: extra got multiple values for keyword argument 'label'
461 [255]
461 [255]
462 $ log 'extra(label=branch, default)'
462 $ log 'extra(label=branch, default)'
463 hg: parse error: extra got an invalid argument
463 hg: parse error: extra got an invalid argument
464 [255]
464 [255]
465 $ log 'extra(branch, foo+bar=baz)'
465 $ log 'extra(branch, foo+bar=baz)'
466 hg: parse error: extra got an invalid argument
466 hg: parse error: extra got an invalid argument
467 [255]
467 [255]
468 $ log 'extra(unknown=branch)'
468 $ log 'extra(unknown=branch)'
469 hg: parse error: extra got an unexpected keyword argument 'unknown'
469 hg: parse error: extra got an unexpected keyword argument 'unknown'
470 [255]
470 [255]
471
471
472 $ try 'foo=bar|baz'
472 $ try 'foo=bar|baz'
473 (keyvalue
473 (keyvalue
474 ('symbol', 'foo')
474 ('symbol', 'foo')
475 (or
475 (or
476 (list
476 (list
477 ('symbol', 'bar')
477 ('symbol', 'bar')
478 ('symbol', 'baz'))))
478 ('symbol', 'baz'))))
479 hg: parse error: can't use a key-value pair in this context
479 hg: parse error: can't use a key-value pair in this context
480 [255]
480 [255]
481
481
482 right-hand side should be optimized recursively
482 right-hand side should be optimized recursively
483
483
484 $ try --optimize 'foo=(not public())'
484 $ try --optimize 'foo=(not public())'
485 (keyvalue
485 (keyvalue
486 ('symbol', 'foo')
486 ('symbol', 'foo')
487 (group
487 (group
488 (not
488 (not
489 (func
489 (func
490 ('symbol', 'public')
490 ('symbol', 'public')
491 None))))
491 None))))
492 * optimized:
492 * optimized:
493 (keyvalue
493 (keyvalue
494 ('symbol', 'foo')
494 ('symbol', 'foo')
495 (func
495 (func
496 ('symbol', '_notpublic')
496 ('symbol', '_notpublic')
497 None))
497 None))
498 hg: parse error: can't use a key-value pair in this context
498 hg: parse error: can't use a key-value pair in this context
499 [255]
499 [255]
500
500
501 relation-subscript operator has the highest binding strength (as function call):
501 relation-subscript operator has the highest binding strength (as function call):
502
502
503 $ hg debugrevspec -p parsed 'tip:tip^#generations[-1]'
503 $ hg debugrevspec -p parsed 'tip:tip^#generations[-1]'
504 * parsed:
504 * parsed:
505 (range
505 (range
506 ('symbol', 'tip')
506 ('symbol', 'tip')
507 (relsubscript
507 (relsubscript
508 (parentpost
508 (parentpost
509 ('symbol', 'tip'))
509 ('symbol', 'tip'))
510 ('symbol', 'generations')
510 ('symbol', 'generations')
511 (negate
511 (negate
512 ('symbol', '1'))))
512 ('symbol', '1'))))
513 9
513 9
514 8
514 8
515 7
515 7
516 6
516 6
517 5
517 5
518 4
518 4
519
519
520 $ hg debugrevspec -p parsed --no-show-revs 'not public()#generations[0]'
520 $ hg debugrevspec -p parsed --no-show-revs 'not public()#generations[0]'
521 * parsed:
521 * parsed:
522 (not
522 (not
523 (relsubscript
523 (relsubscript
524 (func
524 (func
525 ('symbol', 'public')
525 ('symbol', 'public')
526 None)
526 None)
527 ('symbol', 'generations')
527 ('symbol', 'generations')
528 ('symbol', '0')))
528 ('symbol', '0')))
529
529
530 left-hand side of relation-subscript operator should be optimized recursively:
530 left-hand side of relation-subscript operator should be optimized recursively:
531
531
532 $ hg debugrevspec -p analyzed -p optimized --no-show-revs \
532 $ hg debugrevspec -p analyzed -p optimized --no-show-revs \
533 > '(not public())#generations[0]'
533 > '(not public())#generations[0]'
534 * analyzed:
534 * analyzed:
535 (relsubscript
535 (relsubscript
536 (not
536 (not
537 (func
537 (func
538 ('symbol', 'public')
538 ('symbol', 'public')
539 None))
539 None))
540 ('symbol', 'generations')
540 ('symbol', 'generations')
541 ('symbol', '0'))
541 ('symbol', '0'))
542 * optimized:
542 * optimized:
543 (relsubscript
543 (relsubscript
544 (func
544 (func
545 ('symbol', '_notpublic')
545 ('symbol', '_notpublic')
546 None)
546 None)
547 ('symbol', 'generations')
547 ('symbol', 'generations')
548 ('symbol', '0'))
548 ('symbol', '0'))
549
549
550 resolution of subscript and relation-subscript ternary operators:
550 resolution of subscript and relation-subscript ternary operators:
551
551
552 $ hg debugrevspec -p analyzed 'tip[0]'
552 $ hg debugrevspec -p analyzed 'tip[0]'
553 * analyzed:
553 * analyzed:
554 (subscript
554 (subscript
555 ('symbol', 'tip')
555 ('symbol', 'tip')
556 ('symbol', '0'))
556 ('symbol', '0'))
557 hg: parse error: can't use a subscript in this context
557 hg: parse error: can't use a subscript in this context
558 [255]
558 [255]
559
559
560 $ hg debugrevspec -p analyzed 'tip#rel[0]'
560 $ hg debugrevspec -p analyzed 'tip#rel[0]'
561 * analyzed:
561 * analyzed:
562 (relsubscript
562 (relsubscript
563 ('symbol', 'tip')
563 ('symbol', 'tip')
564 ('symbol', 'rel')
564 ('symbol', 'rel')
565 ('symbol', '0'))
565 ('symbol', '0'))
566 hg: parse error: unknown identifier: rel
566 hg: parse error: unknown identifier: rel
567 [255]
567 [255]
568
568
569 $ hg debugrevspec -p analyzed '(tip#rel)[0]'
569 $ hg debugrevspec -p analyzed '(tip#rel)[0]'
570 * analyzed:
570 * analyzed:
571 (subscript
571 (subscript
572 (relation
572 (relation
573 ('symbol', 'tip')
573 ('symbol', 'tip')
574 ('symbol', 'rel'))
574 ('symbol', 'rel'))
575 ('symbol', '0'))
575 ('symbol', '0'))
576 hg: parse error: can't use a subscript in this context
576 hg: parse error: can't use a subscript in this context
577 [255]
577 [255]
578
578
579 $ hg debugrevspec -p analyzed 'tip#rel[0][1]'
579 $ hg debugrevspec -p analyzed 'tip#rel[0][1]'
580 * analyzed:
580 * analyzed:
581 (subscript
581 (subscript
582 (relsubscript
582 (relsubscript
583 ('symbol', 'tip')
583 ('symbol', 'tip')
584 ('symbol', 'rel')
584 ('symbol', 'rel')
585 ('symbol', '0'))
585 ('symbol', '0'))
586 ('symbol', '1'))
586 ('symbol', '1'))
587 hg: parse error: can't use a subscript in this context
587 hg: parse error: can't use a subscript in this context
588 [255]
588 [255]
589
589
590 $ hg debugrevspec -p analyzed 'tip#rel0#rel1[1]'
590 $ hg debugrevspec -p analyzed 'tip#rel0#rel1[1]'
591 * analyzed:
591 * analyzed:
592 (relsubscript
592 (relsubscript
593 (relation
593 (relation
594 ('symbol', 'tip')
594 ('symbol', 'tip')
595 ('symbol', 'rel0'))
595 ('symbol', 'rel0'))
596 ('symbol', 'rel1')
596 ('symbol', 'rel1')
597 ('symbol', '1'))
597 ('symbol', '1'))
598 hg: parse error: unknown identifier: rel1
598 hg: parse error: unknown identifier: rel1
599 [255]
599 [255]
600
600
601 $ hg debugrevspec -p analyzed 'tip#rel0[0]#rel1[1]'
601 $ hg debugrevspec -p analyzed 'tip#rel0[0]#rel1[1]'
602 * analyzed:
602 * analyzed:
603 (relsubscript
603 (relsubscript
604 (relsubscript
604 (relsubscript
605 ('symbol', 'tip')
605 ('symbol', 'tip')
606 ('symbol', 'rel0')
606 ('symbol', 'rel0')
607 ('symbol', '0'))
607 ('symbol', '0'))
608 ('symbol', 'rel1')
608 ('symbol', 'rel1')
609 ('symbol', '1'))
609 ('symbol', '1'))
610 hg: parse error: unknown identifier: rel1
610 hg: parse error: unknown identifier: rel1
611 [255]
611 [255]
612
612
613 parse errors of relation, subscript and relation-subscript operators:
613 parse errors of relation, subscript and relation-subscript operators:
614
614
615 $ hg debugrevspec '[0]'
615 $ hg debugrevspec '[0]'
616 hg: parse error at 0: not a prefix: [
616 hg: parse error at 0: not a prefix: [
617 [255]
617 [255]
618 $ hg debugrevspec '.#'
618 $ hg debugrevspec '.#'
619 hg: parse error at 2: not a prefix: end
619 hg: parse error at 2: not a prefix: end
620 [255]
620 [255]
621 $ hg debugrevspec '#rel'
621 $ hg debugrevspec '#rel'
622 hg: parse error at 0: not a prefix: #
622 hg: parse error at 0: not a prefix: #
623 [255]
623 [255]
624 $ hg debugrevspec '.#rel[0'
624 $ hg debugrevspec '.#rel[0'
625 hg: parse error at 7: unexpected token: end
625 hg: parse error at 7: unexpected token: end
626 [255]
626 [255]
627 $ hg debugrevspec '.]'
627 $ hg debugrevspec '.]'
628 hg: parse error at 1: invalid token
628 hg: parse error at 1: invalid token
629 [255]
629 [255]
630
630
631 $ hg debugrevspec '.#generations[a]'
631 $ hg debugrevspec '.#generations[a]'
632 hg: parse error: relation subscript must be an integer
632 hg: parse error: relation subscript must be an integer
633 [255]
633 [255]
634 $ hg debugrevspec '.#generations[1-2]'
634 $ hg debugrevspec '.#generations[1-2]'
635 hg: parse error: relation subscript must be an integer
635 hg: parse error: relation subscript must be an integer
636 [255]
636 [255]
637
637
638 parsed tree at stages:
638 parsed tree at stages:
639
639
640 $ hg debugrevspec -p all '()'
640 $ hg debugrevspec -p all '()'
641 * parsed:
641 * parsed:
642 (group
642 (group
643 None)
643 None)
644 * expanded:
644 * expanded:
645 (group
645 (group
646 None)
646 None)
647 * concatenated:
647 * concatenated:
648 (group
648 (group
649 None)
649 None)
650 * analyzed:
650 * analyzed:
651 None
651 None
652 * optimized:
652 * optimized:
653 None
653 None
654 hg: parse error: missing argument
654 hg: parse error: missing argument
655 [255]
655 [255]
656
656
657 $ hg debugrevspec --no-optimized -p all '()'
657 $ hg debugrevspec --no-optimized -p all '()'
658 * parsed:
658 * parsed:
659 (group
659 (group
660 None)
660 None)
661 * expanded:
661 * expanded:
662 (group
662 (group
663 None)
663 None)
664 * concatenated:
664 * concatenated:
665 (group
665 (group
666 None)
666 None)
667 * analyzed:
667 * analyzed:
668 None
668 None
669 hg: parse error: missing argument
669 hg: parse error: missing argument
670 [255]
670 [255]
671
671
672 $ hg debugrevspec -p parsed -p analyzed -p optimized '(0|1)-1'
672 $ hg debugrevspec -p parsed -p analyzed -p optimized '(0|1)-1'
673 * parsed:
673 * parsed:
674 (minus
674 (minus
675 (group
675 (group
676 (or
676 (or
677 (list
677 (list
678 ('symbol', '0')
678 ('symbol', '0')
679 ('symbol', '1'))))
679 ('symbol', '1'))))
680 ('symbol', '1'))
680 ('symbol', '1'))
681 * analyzed:
681 * analyzed:
682 (and
682 (and
683 (or
683 (or
684 (list
684 (list
685 ('symbol', '0')
685 ('symbol', '0')
686 ('symbol', '1')))
686 ('symbol', '1')))
687 (not
687 (not
688 ('symbol', '1')))
688 ('symbol', '1')))
689 * optimized:
689 * optimized:
690 (difference
690 (difference
691 (func
691 (func
692 ('symbol', '_list')
692 ('symbol', '_list')
693 ('string', '0\x001'))
693 ('string', '0\x001'))
694 ('symbol', '1'))
694 ('symbol', '1'))
695 0
695 0
696
696
697 $ hg debugrevspec -p unknown '0'
697 $ hg debugrevspec -p unknown '0'
698 abort: invalid stage name: unknown
698 abort: invalid stage name: unknown
699 [255]
699 [255]
700
700
701 $ hg debugrevspec -p all --optimize '0'
701 $ hg debugrevspec -p all --optimize '0'
702 abort: cannot use --optimize with --show-stage
702 abort: cannot use --optimize with --show-stage
703 [255]
703 [255]
704
704
705 verify optimized tree:
705 verify optimized tree:
706
706
707 $ hg debugrevspec --verify '0|1'
707 $ hg debugrevspec --verify '0|1'
708
708
709 $ hg debugrevspec --verify -v -p analyzed -p optimized 'r3232() & 2'
709 $ hg debugrevspec --verify -v -p analyzed -p optimized 'r3232() & 2'
710 * analyzed:
710 * analyzed:
711 (and
711 (and
712 (func
712 (func
713 ('symbol', 'r3232')
713 ('symbol', 'r3232')
714 None)
714 None)
715 ('symbol', '2'))
715 ('symbol', '2'))
716 * optimized:
716 * optimized:
717 (andsmally
717 (andsmally
718 (func
718 (func
719 ('symbol', 'r3232')
719 ('symbol', 'r3232')
720 None)
720 None)
721 ('symbol', '2'))
721 ('symbol', '2'))
722 * analyzed set:
722 * analyzed set:
723 <baseset [2]>
723 <baseset [2]>
724 * optimized set:
724 * optimized set:
725 <baseset [2, 2]>
725 <baseset [2, 2]>
726 --- analyzed
726 --- analyzed
727 +++ optimized
727 +++ optimized
728 2
728 2
729 +2
729 +2
730 [1]
730 [1]
731
731
732 $ hg debugrevspec --no-optimized --verify-optimized '0'
732 $ hg debugrevspec --no-optimized --verify-optimized '0'
733 abort: cannot use --verify-optimized with --no-optimized
733 abort: cannot use --verify-optimized with --no-optimized
734 [255]
734 [255]
735
735
736 Test that symbols only get parsed as functions if there's an opening
736 Test that symbols only get parsed as functions if there's an opening
737 parenthesis.
737 parenthesis.
738
738
739 $ hg book only -r 9
739 $ hg book only -r 9
740 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
740 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
741 8
741 8
742 9
742 9
743
743
744 ':y' behaves like '0:y', but can't be rewritten as such since the revision '0'
744 ':y' behaves like '0:y', but can't be rewritten as such since the revision '0'
745 may be hidden (issue5385)
745 may be hidden (issue5385)
746
746
747 $ try -p parsed -p analyzed ':'
747 $ try -p parsed -p analyzed ':'
748 * parsed:
748 * parsed:
749 (rangeall
749 (rangeall
750 None)
750 None)
751 * analyzed:
751 * analyzed:
752 (rangeall
752 (rangeall
753 None)
753 None)
754 * set:
754 * set:
755 <spanset+ 0:10>
755 <spanset+ 0:10>
756 0
756 0
757 1
757 1
758 2
758 2
759 3
759 3
760 4
760 4
761 5
761 5
762 6
762 6
763 7
763 7
764 8
764 8
765 9
765 9
766 $ try -p analyzed ':1'
766 $ try -p analyzed ':1'
767 * analyzed:
767 * analyzed:
768 (rangepre
768 (rangepre
769 ('symbol', '1'))
769 ('symbol', '1'))
770 * set:
770 * set:
771 <spanset+ 0:2>
771 <spanset+ 0:2>
772 0
772 0
773 1
773 1
774 $ try -p analyzed ':(1|2)'
774 $ try -p analyzed ':(1|2)'
775 * analyzed:
775 * analyzed:
776 (rangepre
776 (rangepre
777 (or
777 (or
778 (list
778 (list
779 ('symbol', '1')
779 ('symbol', '1')
780 ('symbol', '2'))))
780 ('symbol', '2'))))
781 * set:
781 * set:
782 <spanset+ 0:3>
782 <spanset+ 0:3>
783 0
783 0
784 1
784 1
785 2
785 2
786 $ try -p analyzed ':(1&2)'
786 $ try -p analyzed ':(1&2)'
787 * analyzed:
787 * analyzed:
788 (rangepre
788 (rangepre
789 (and
789 (and
790 ('symbol', '1')
790 ('symbol', '1')
791 ('symbol', '2')))
791 ('symbol', '2')))
792 * set:
792 * set:
793 <baseset []>
793 <baseset []>
794
794
795 infix/suffix resolution of ^ operator (issue2884):
795 infix/suffix resolution of ^ operator (issue2884):
796
796
797 x^:y means (x^):y
797 x^:y means (x^):y
798
798
799 $ try '1^:2'
799 $ try '1^:2'
800 (range
800 (range
801 (parentpost
801 (parentpost
802 ('symbol', '1'))
802 ('symbol', '1'))
803 ('symbol', '2'))
803 ('symbol', '2'))
804 * set:
804 * set:
805 <spanset+ 0:3>
805 <spanset+ 0:3>
806 0
806 0
807 1
807 1
808 2
808 2
809
809
810 $ try '1^::2'
810 $ try '1^::2'
811 (dagrange
811 (dagrange
812 (parentpost
812 (parentpost
813 ('symbol', '1'))
813 ('symbol', '1'))
814 ('symbol', '2'))
814 ('symbol', '2'))
815 * set:
815 * set:
816 <baseset+ [0, 1, 2]>
816 <baseset+ [0, 1, 2]>
817 0
817 0
818 1
818 1
819 2
819 2
820
820
821 $ try '9^:'
821 $ try '9^:'
822 (rangepost
822 (rangepost
823 (parentpost
823 (parentpost
824 ('symbol', '9')))
824 ('symbol', '9')))
825 * set:
825 * set:
826 <spanset+ 8:10>
826 <spanset+ 8:10>
827 8
827 8
828 9
828 9
829
829
830 x^:y should be resolved before omitting group operators
830 x^:y should be resolved before omitting group operators
831
831
832 $ try '1^(:2)'
832 $ try '1^(:2)'
833 (parent
833 (parent
834 ('symbol', '1')
834 ('symbol', '1')
835 (group
835 (group
836 (rangepre
836 (rangepre
837 ('symbol', '2'))))
837 ('symbol', '2'))))
838 hg: parse error: ^ expects a number 0, 1, or 2
838 hg: parse error: ^ expects a number 0, 1, or 2
839 [255]
839 [255]
840
840
841 x^:y should be resolved recursively
841 x^:y should be resolved recursively
842
842
843 $ try 'sort(1^:2)'
843 $ try 'sort(1^:2)'
844 (func
844 (func
845 ('symbol', 'sort')
845 ('symbol', 'sort')
846 (range
846 (range
847 (parentpost
847 (parentpost
848 ('symbol', '1'))
848 ('symbol', '1'))
849 ('symbol', '2')))
849 ('symbol', '2')))
850 * set:
850 * set:
851 <spanset+ 0:3>
851 <spanset+ 0:3>
852 0
852 0
853 1
853 1
854 2
854 2
855
855
856 $ try '(3^:4)^:2'
856 $ try '(3^:4)^:2'
857 (range
857 (range
858 (parentpost
858 (parentpost
859 (group
859 (group
860 (range
860 (range
861 (parentpost
861 (parentpost
862 ('symbol', '3'))
862 ('symbol', '3'))
863 ('symbol', '4'))))
863 ('symbol', '4'))))
864 ('symbol', '2'))
864 ('symbol', '2'))
865 * set:
865 * set:
866 <spanset+ 0:3>
866 <spanset+ 0:3>
867 0
867 0
868 1
868 1
869 2
869 2
870
870
871 $ try '(3^::4)^::2'
871 $ try '(3^::4)^::2'
872 (dagrange
872 (dagrange
873 (parentpost
873 (parentpost
874 (group
874 (group
875 (dagrange
875 (dagrange
876 (parentpost
876 (parentpost
877 ('symbol', '3'))
877 ('symbol', '3'))
878 ('symbol', '4'))))
878 ('symbol', '4'))))
879 ('symbol', '2'))
879 ('symbol', '2'))
880 * set:
880 * set:
881 <baseset+ [0, 1, 2]>
881 <baseset+ [0, 1, 2]>
882 0
882 0
883 1
883 1
884 2
884 2
885
885
886 $ try '(9^:)^:'
886 $ try '(9^:)^:'
887 (rangepost
887 (rangepost
888 (parentpost
888 (parentpost
889 (group
889 (group
890 (rangepost
890 (rangepost
891 (parentpost
891 (parentpost
892 ('symbol', '9'))))))
892 ('symbol', '9'))))))
893 * set:
893 * set:
894 <spanset+ 4:10>
894 <spanset+ 4:10>
895 4
895 4
896 5
896 5
897 6
897 6
898 7
898 7
899 8
899 8
900 9
900 9
901
901
902 x^ in alias should also be resolved
902 x^ in alias should also be resolved
903
903
904 $ try 'A' --config 'revsetalias.A=1^:2'
904 $ try 'A' --config 'revsetalias.A=1^:2'
905 ('symbol', 'A')
905 ('symbol', 'A')
906 * expanded:
906 * expanded:
907 (range
907 (range
908 (parentpost
908 (parentpost
909 ('symbol', '1'))
909 ('symbol', '1'))
910 ('symbol', '2'))
910 ('symbol', '2'))
911 * set:
911 * set:
912 <spanset+ 0:3>
912 <spanset+ 0:3>
913 0
913 0
914 1
914 1
915 2
915 2
916
916
917 $ try 'A:2' --config 'revsetalias.A=1^'
917 $ try 'A:2' --config 'revsetalias.A=1^'
918 (range
918 (range
919 ('symbol', 'A')
919 ('symbol', 'A')
920 ('symbol', '2'))
920 ('symbol', '2'))
921 * expanded:
921 * expanded:
922 (range
922 (range
923 (parentpost
923 (parentpost
924 ('symbol', '1'))
924 ('symbol', '1'))
925 ('symbol', '2'))
925 ('symbol', '2'))
926 * set:
926 * set:
927 <spanset+ 0:3>
927 <spanset+ 0:3>
928 0
928 0
929 1
929 1
930 2
930 2
931
931
932 but not beyond the boundary of alias expansion, because the resolution should
932 but not beyond the boundary of alias expansion, because the resolution should
933 be made at the parsing stage
933 be made at the parsing stage
934
934
935 $ try '1^A' --config 'revsetalias.A=:2'
935 $ try '1^A' --config 'revsetalias.A=:2'
936 (parent
936 (parent
937 ('symbol', '1')
937 ('symbol', '1')
938 ('symbol', 'A'))
938 ('symbol', 'A'))
939 * expanded:
939 * expanded:
940 (parent
940 (parent
941 ('symbol', '1')
941 ('symbol', '1')
942 (rangepre
942 (rangepre
943 ('symbol', '2')))
943 ('symbol', '2')))
944 hg: parse error: ^ expects a number 0, 1, or 2
944 hg: parse error: ^ expects a number 0, 1, or 2
945 [255]
945 [255]
946
946
947 ancestor can accept 0 or more arguments
947 ancestor can accept 0 or more arguments
948
948
949 $ log 'ancestor()'
949 $ log 'ancestor()'
950 $ log 'ancestor(1)'
950 $ log 'ancestor(1)'
951 1
951 1
952 $ log 'ancestor(4,5)'
952 $ log 'ancestor(4,5)'
953 1
953 1
954 $ log 'ancestor(4,5) and 4'
954 $ log 'ancestor(4,5) and 4'
955 $ log 'ancestor(0,0,1,3)'
955 $ log 'ancestor(0,0,1,3)'
956 0
956 0
957 $ log 'ancestor(3,1,5,3,5,1)'
957 $ log 'ancestor(3,1,5,3,5,1)'
958 1
958 1
959 $ log 'ancestor(0,1,3,5)'
959 $ log 'ancestor(0,1,3,5)'
960 0
960 0
961 $ log 'ancestor(1,2,3,4,5)'
961 $ log 'ancestor(1,2,3,4,5)'
962 1
962 1
963
963
964 test ancestors
964 test ancestors
965
965
966 $ hg log -G -T '{rev}\n' --config experimental.graphshorten=True
966 $ hg log -G -T '{rev}\n' --config experimental.graphshorten=True
967 @ 9
967 @ 9
968 o 8
968 o 8
969 | o 7
969 | o 7
970 | o 6
970 | o 6
971 |/|
971 |/|
972 | o 5
972 | o 5
973 o | 4
973 o | 4
974 | o 3
974 | o 3
975 o | 2
975 o | 2
976 |/
976 |/
977 o 1
977 o 1
978 o 0
978 o 0
979
979
980 $ log 'ancestors(5)'
980 $ log 'ancestors(5)'
981 0
981 0
982 1
982 1
983 3
983 3
984 5
984 5
985 $ log 'ancestor(ancestors(5))'
985 $ log 'ancestor(ancestors(5))'
986 0
986 0
987 $ log '::r3232()'
987 $ log '::r3232()'
988 0
988 0
989 1
989 1
990 2
990 2
991 3
991 3
992
992
993 test ancestors with depth limit
993 test ancestors with depth limit
994
994
995 (depth=0 selects the node itself)
995 (depth=0 selects the node itself)
996
996
997 $ log 'reverse(ancestors(9, depth=0))'
997 $ log 'reverse(ancestors(9, depth=0))'
998 9
998 9
999
999
1000 (interleaved: '4' would be missing if heap queue were higher depth first)
1000 (interleaved: '4' would be missing if heap queue were higher depth first)
1001
1001
1002 $ log 'reverse(ancestors(8:9, depth=1))'
1002 $ log 'reverse(ancestors(8:9, depth=1))'
1003 9
1003 9
1004 8
1004 8
1005 4
1005 4
1006
1006
1007 (interleaved: '2' would be missing if heap queue were higher depth first)
1007 (interleaved: '2' would be missing if heap queue were higher depth first)
1008
1008
1009 $ log 'reverse(ancestors(7+8, depth=2))'
1009 $ log 'reverse(ancestors(7+8, depth=2))'
1010 8
1010 8
1011 7
1011 7
1012 6
1012 6
1013 5
1013 5
1014 4
1014 4
1015 2
1015 2
1016
1016
1017 (walk example above by separate queries)
1017 (walk example above by separate queries)
1018
1018
1019 $ log 'reverse(ancestors(8, depth=2)) + reverse(ancestors(7, depth=2))'
1019 $ log 'reverse(ancestors(8, depth=2)) + reverse(ancestors(7, depth=2))'
1020 8
1020 8
1021 4
1021 4
1022 2
1022 2
1023 7
1023 7
1024 6
1024 6
1025 5
1025 5
1026
1026
1027 (walk 2nd and 3rd ancestors)
1027 (walk 2nd and 3rd ancestors)
1028
1028
1029 $ log 'reverse(ancestors(7, depth=3, startdepth=2))'
1029 $ log 'reverse(ancestors(7, depth=3, startdepth=2))'
1030 5
1030 5
1031 4
1031 4
1032 3
1032 3
1033 2
1033 2
1034
1034
1035 (interleaved: '4' would be missing if higher-depth ancestors weren't scanned)
1035 (interleaved: '4' would be missing if higher-depth ancestors weren't scanned)
1036
1036
1037 $ log 'reverse(ancestors(7+8, depth=2, startdepth=2))'
1037 $ log 'reverse(ancestors(7+8, depth=2, startdepth=2))'
1038 5
1038 5
1039 4
1039 4
1040 2
1040 2
1041
1041
1042 (note that 'ancestors(x, depth=y, startdepth=z)' does not identical to
1042 (note that 'ancestors(x, depth=y, startdepth=z)' does not identical to
1043 'ancestors(x, depth=y) - ancestors(x, depth=z-1)' because a node may have
1043 'ancestors(x, depth=y) - ancestors(x, depth=z-1)' because a node may have
1044 multiple depths)
1044 multiple depths)
1045
1045
1046 $ log 'reverse(ancestors(7+8, depth=2) - ancestors(7+8, depth=1))'
1046 $ log 'reverse(ancestors(7+8, depth=2) - ancestors(7+8, depth=1))'
1047 5
1047 5
1048 2
1048 2
1049
1049
1050 test bad arguments passed to ancestors()
1050 test bad arguments passed to ancestors()
1051
1051
1052 $ log 'ancestors(., depth=-1)'
1052 $ log 'ancestors(., depth=-1)'
1053 hg: parse error: negative depth
1053 hg: parse error: negative depth
1054 [255]
1054 [255]
1055 $ log 'ancestors(., depth=foo)'
1055 $ log 'ancestors(., depth=foo)'
1056 hg: parse error: ancestors expects an integer depth
1056 hg: parse error: ancestors expects an integer depth
1057 [255]
1057 [255]
1058
1058
1059 test descendants
1059 test descendants
1060
1060
1061 $ hg log -G -T '{rev}\n' --config experimental.graphshorten=True
1061 $ hg log -G -T '{rev}\n' --config experimental.graphshorten=True
1062 @ 9
1062 @ 9
1063 o 8
1063 o 8
1064 | o 7
1064 | o 7
1065 | o 6
1065 | o 6
1066 |/|
1066 |/|
1067 | o 5
1067 | o 5
1068 o | 4
1068 o | 4
1069 | o 3
1069 | o 3
1070 o | 2
1070 o | 2
1071 |/
1071 |/
1072 o 1
1072 o 1
1073 o 0
1073 o 0
1074
1074
1075 (null is ultimate root and has optimized path)
1075 (null is ultimate root and has optimized path)
1076
1076
1077 $ log 'null:4 & descendants(null)'
1077 $ log 'null:4 & descendants(null)'
1078 -1
1078 -1
1079 0
1079 0
1080 1
1080 1
1081 2
1081 2
1082 3
1082 3
1083 4
1083 4
1084
1084
1085 (including merge)
1085 (including merge)
1086
1086
1087 $ log ':8 & descendants(2)'
1087 $ log ':8 & descendants(2)'
1088 2
1088 2
1089 4
1089 4
1090 6
1090 6
1091 7
1091 7
1092 8
1092 8
1093
1093
1094 (multiple roots)
1094 (multiple roots)
1095
1095
1096 $ log ':8 & descendants(2+5)'
1096 $ log ':8 & descendants(2+5)'
1097 2
1097 2
1098 4
1098 4
1099 5
1099 5
1100 6
1100 6
1101 7
1101 7
1102 8
1102 8
1103
1103
1104 test descendants with depth limit
1104 test descendants with depth limit
1105
1105
1106 (depth=0 selects the node itself)
1106 (depth=0 selects the node itself)
1107
1107
1108 $ log 'descendants(0, depth=0)'
1108 $ log 'descendants(0, depth=0)'
1109 0
1109 0
1110 $ log 'null: & descendants(null, depth=0)'
1110 $ log 'null: & descendants(null, depth=0)'
1111 -1
1111 -1
1112
1112
1113 (p2 = null should be ignored)
1113 (p2 = null should be ignored)
1114
1114
1115 $ log 'null: & descendants(null, depth=2)'
1115 $ log 'null: & descendants(null, depth=2)'
1116 -1
1116 -1
1117 0
1117 0
1118 1
1118 1
1119
1119
1120 (multiple paths: depth(6) = (2, 3))
1120 (multiple paths: depth(6) = (2, 3))
1121
1121
1122 $ log 'descendants(1+3, depth=2)'
1122 $ log 'descendants(1+3, depth=2)'
1123 1
1123 1
1124 2
1124 2
1125 3
1125 3
1126 4
1126 4
1127 5
1127 5
1128 6
1128 6
1129
1129
1130 (multiple paths: depth(5) = (1, 2), depth(6) = (2, 3))
1130 (multiple paths: depth(5) = (1, 2), depth(6) = (2, 3))
1131
1131
1132 $ log 'descendants(3+1, depth=2, startdepth=2)'
1132 $ log 'descendants(3+1, depth=2, startdepth=2)'
1133 4
1133 4
1134 5
1134 5
1135 6
1135 6
1136
1136
1137 (multiple depths: depth(6) = (0, 2, 4), search for depth=2)
1137 (multiple depths: depth(6) = (0, 2, 4), search for depth=2)
1138
1138
1139 $ log 'descendants(0+3+6, depth=3, startdepth=1)'
1139 $ log 'descendants(0+3+6, depth=3, startdepth=1)'
1140 1
1140 1
1141 2
1141 2
1142 3
1142 3
1143 4
1143 4
1144 5
1144 5
1145 6
1145 6
1146 7
1146 7
1147
1147
1148 (multiple depths: depth(6) = (0, 4), no match)
1148 (multiple depths: depth(6) = (0, 4), no match)
1149
1149
1150 $ log 'descendants(0+6, depth=3, startdepth=1)'
1150 $ log 'descendants(0+6, depth=3, startdepth=1)'
1151 1
1151 1
1152 2
1152 2
1153 3
1153 3
1154 4
1154 4
1155 5
1155 5
1156 7
1156 7
1157
1157
1158 test ancestors/descendants relation subscript:
1158 test ancestors/descendants relation subscript:
1159
1159
1160 $ log 'tip#generations[0]'
1160 $ log 'tip#generations[0]'
1161 9
1161 9
1162 $ log '.#generations[-1]'
1162 $ log '.#generations[-1]'
1163 8
1163 8
1164 $ log '.#g[(-1)]'
1164 $ log '.#g[(-1)]'
1165 8
1165 8
1166
1166
1167 $ hg debugrevspec -p parsed 'roots(:)#g[2]'
1167 $ hg debugrevspec -p parsed 'roots(:)#g[2]'
1168 * parsed:
1168 * parsed:
1169 (relsubscript
1169 (relsubscript
1170 (func
1170 (func
1171 ('symbol', 'roots')
1171 ('symbol', 'roots')
1172 (rangeall
1172 (rangeall
1173 None))
1173 None))
1174 ('symbol', 'g')
1174 ('symbol', 'g')
1175 ('symbol', '2'))
1175 ('symbol', '2'))
1176 2
1176 2
1177 3
1177 3
1178
1178
1179 test author
1179 test author
1180
1180
1181 $ log 'author(bob)'
1181 $ log 'author(bob)'
1182 2
1182 2
1183 $ log 'author("re:bob|test")'
1183 $ log 'author("re:bob|test")'
1184 0
1184 0
1185 1
1185 1
1186 2
1186 2
1187 3
1187 3
1188 4
1188 4
1189 5
1189 5
1190 6
1190 6
1191 7
1191 7
1192 8
1192 8
1193 9
1193 9
1194 $ log 'author(r"re:\S")'
1194 $ log 'author(r"re:\S")'
1195 0
1195 0
1196 1
1196 1
1197 2
1197 2
1198 3
1198 3
1199 4
1199 4
1200 5
1200 5
1201 6
1201 6
1202 7
1202 7
1203 8
1203 8
1204 9
1204 9
1205 $ log 'branch(Γ©)'
1205 $ log 'branch(Γ©)'
1206 8
1206 8
1207 9
1207 9
1208 $ log 'branch(a)'
1208 $ log 'branch(a)'
1209 0
1209 0
1210 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
1210 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
1211 0 a
1211 0 a
1212 2 a-b-c-
1212 2 a-b-c-
1213 3 +a+b+c+
1213 3 +a+b+c+
1214 4 -a-b-c-
1214 4 -a-b-c-
1215 5 !a/b/c/
1215 5 !a/b/c/
1216 6 _a_b_c_
1216 6 _a_b_c_
1217 7 .a.b.c.
1217 7 .a.b.c.
1218 $ log 'children(ancestor(4,5))'
1218 $ log 'children(ancestor(4,5))'
1219 2
1219 2
1220 3
1220 3
1221
1221
1222 $ log 'children(4)'
1222 $ log 'children(4)'
1223 6
1223 6
1224 8
1224 8
1225 $ log 'children(null)'
1225 $ log 'children(null)'
1226 0
1226 0
1227
1227
1228 $ log 'closed()'
1228 $ log 'closed()'
1229 $ log 'contains(a)'
1229 $ log 'contains(a)'
1230 0
1230 0
1231 1
1231 1
1232 3
1232 3
1233 5
1233 5
1234 $ log 'contains("../repo/a")'
1234 $ log 'contains("../repo/a")'
1235 0
1235 0
1236 1
1236 1
1237 3
1237 3
1238 5
1238 5
1239 $ log 'desc(B)'
1239 $ log 'desc(B)'
1240 5
1240 5
1241 $ hg log -r 'desc(r"re:S?u")' --template "{rev} {desc|firstline}\n"
1241 $ hg log -r 'desc(r"re:S?u")' --template "{rev} {desc|firstline}\n"
1242 5 5 bug
1242 5 5 bug
1243 6 6 issue619
1243 6 6 issue619
1244 $ log 'descendants(2 or 3)'
1244 $ log 'descendants(2 or 3)'
1245 2
1245 2
1246 3
1246 3
1247 4
1247 4
1248 5
1248 5
1249 6
1249 6
1250 7
1250 7
1251 8
1251 8
1252 9
1252 9
1253 $ log 'file("b*")'
1253 $ log 'file("b*")'
1254 1
1254 1
1255 4
1255 4
1256 $ log 'filelog("b")'
1256 $ log 'filelog("b")'
1257 1
1257 1
1258 4
1258 4
1259 $ log 'filelog("../repo/b")'
1259 $ log 'filelog("../repo/b")'
1260 1
1260 1
1261 4
1261 4
1262 $ log 'follow()'
1262 $ log 'follow()'
1263 0
1263 0
1264 1
1264 1
1265 2
1265 2
1266 4
1266 4
1267 8
1267 8
1268 9
1268 9
1269 $ log 'grep("issue\d+")'
1269 $ log 'grep("issue\d+")'
1270 6
1270 6
1271 $ try 'grep("(")' # invalid regular expression
1271 $ try 'grep("(")' # invalid regular expression
1272 (func
1272 (func
1273 ('symbol', 'grep')
1273 ('symbol', 'grep')
1274 ('string', '('))
1274 ('string', '('))
1275 hg: parse error: invalid match pattern: unbalanced parenthesis
1275 hg: parse error: invalid match pattern: unbalanced parenthesis
1276 [255]
1276 [255]
1277 $ try 'grep("\bissue\d+")'
1277 $ try 'grep("\bissue\d+")'
1278 (func
1278 (func
1279 ('symbol', 'grep')
1279 ('symbol', 'grep')
1280 ('string', '\x08issue\\d+'))
1280 ('string', '\x08issue\\d+'))
1281 * set:
1281 * set:
1282 <filteredset
1282 <filteredset
1283 <fullreposet+ 0:10>,
1283 <fullreposet+ 0:10>,
1284 <grep '\x08issue\\d+'>>
1284 <grep '\x08issue\\d+'>>
1285 $ try 'grep(r"\bissue\d+")'
1285 $ try 'grep(r"\bissue\d+")'
1286 (func
1286 (func
1287 ('symbol', 'grep')
1287 ('symbol', 'grep')
1288 ('string', '\\bissue\\d+'))
1288 ('string', '\\bissue\\d+'))
1289 * set:
1289 * set:
1290 <filteredset
1290 <filteredset
1291 <fullreposet+ 0:10>,
1291 <fullreposet+ 0:10>,
1292 <grep '\\bissue\\d+'>>
1292 <grep '\\bissue\\d+'>>
1293 6
1293 6
1294 $ try 'grep(r"\")'
1294 $ try 'grep(r"\")'
1295 hg: parse error at 7: unterminated string
1295 hg: parse error at 7: unterminated string
1296 [255]
1296 [255]
1297 $ log 'head()'
1297 $ log 'head()'
1298 0
1298 0
1299 1
1299 1
1300 2
1300 2
1301 3
1301 3
1302 4
1302 4
1303 5
1303 5
1304 6
1304 6
1305 7
1305 7
1306 9
1306 9
1307 $ log 'heads(6::)'
1307 $ log 'heads(6::)'
1308 7
1308 7
1309 $ log 'keyword(issue)'
1309 $ log 'keyword(issue)'
1310 6
1310 6
1311 $ log 'keyword("test a")'
1311 $ log 'keyword("test a")'
1312
1312
1313 Test first (=limit) and last
1313 Test first (=limit) and last
1314
1314
1315 $ log 'limit(head(), 1)'
1315 $ log 'limit(head(), 1)'
1316 0
1316 0
1317 $ log 'limit(author("re:bob|test"), 3, 5)'
1317 $ log 'limit(author("re:bob|test"), 3, 5)'
1318 5
1318 5
1319 6
1319 6
1320 7
1320 7
1321 $ log 'limit(author("re:bob|test"), offset=6)'
1321 $ log 'limit(author("re:bob|test"), offset=6)'
1322 6
1322 6
1323 $ log 'limit(author("re:bob|test"), offset=10)'
1323 $ log 'limit(author("re:bob|test"), offset=10)'
1324 $ log 'limit(all(), 1, -1)'
1324 $ log 'limit(all(), 1, -1)'
1325 hg: parse error: negative offset
1325 hg: parse error: negative offset
1326 [255]
1326 [255]
1327 $ log 'limit(all(), -1)'
1327 $ log 'limit(all(), -1)'
1328 hg: parse error: negative number to select
1328 hg: parse error: negative number to select
1329 [255]
1329 [255]
1330 $ log 'limit(all(), 0)'
1330 $ log 'limit(all(), 0)'
1331
1331
1332 $ log 'last(all(), -1)'
1332 $ log 'last(all(), -1)'
1333 hg: parse error: negative number to select
1333 hg: parse error: negative number to select
1334 [255]
1334 [255]
1335 $ log 'last(all(), 0)'
1335 $ log 'last(all(), 0)'
1336 $ log 'last(all(), 1)'
1336 $ log 'last(all(), 1)'
1337 9
1337 9
1338 $ log 'last(all(), 2)'
1338 $ log 'last(all(), 2)'
1339 8
1339 8
1340 9
1340 9
1341
1341
1342 Test smartset.slice() by first/last()
1342 Test smartset.slice() by first/last()
1343
1343
1344 (using unoptimized set, filteredset as example)
1344 (using unoptimized set, filteredset as example)
1345
1345
1346 $ hg debugrevspec --no-show-revs -s '0:7 & branch("re:")'
1346 $ hg debugrevspec --no-show-revs -s '0:7 & branch("re:")'
1347 * set:
1347 * set:
1348 <filteredset
1348 <filteredset
1349 <spanset+ 0:8>,
1349 <spanset+ 0:8>,
1350 <branch 're:'>>
1350 <branch 're:'>>
1351 $ log 'limit(0:7 & branch("re:"), 3, 4)'
1351 $ log 'limit(0:7 & branch("re:"), 3, 4)'
1352 4
1352 4
1353 5
1353 5
1354 6
1354 6
1355 $ log 'limit(7:0 & branch("re:"), 3, 4)'
1355 $ log 'limit(7:0 & branch("re:"), 3, 4)'
1356 3
1356 3
1357 2
1357 2
1358 1
1358 1
1359 $ log 'last(0:7 & branch("re:"), 2)'
1359 $ log 'last(0:7 & branch("re:"), 2)'
1360 6
1360 6
1361 7
1361 7
1362
1362
1363 (using baseset)
1363 (using baseset)
1364
1364
1365 $ hg debugrevspec --no-show-revs -s 0+1+2+3+4+5+6+7
1365 $ hg debugrevspec --no-show-revs -s 0+1+2+3+4+5+6+7
1366 * set:
1366 * set:
1367 <baseset [0, 1, 2, 3, 4, 5, 6, 7]>
1367 <baseset [0, 1, 2, 3, 4, 5, 6, 7]>
1368 $ hg debugrevspec --no-show-revs -s 0::7
1368 $ hg debugrevspec --no-show-revs -s 0::7
1369 * set:
1369 * set:
1370 <baseset+ [0, 1, 2, 3, 4, 5, 6, 7]>
1370 <baseset+ [0, 1, 2, 3, 4, 5, 6, 7]>
1371 $ log 'limit(0+1+2+3+4+5+6+7, 3, 4)'
1371 $ log 'limit(0+1+2+3+4+5+6+7, 3, 4)'
1372 4
1372 4
1373 5
1373 5
1374 6
1374 6
1375 $ log 'limit(sort(0::7, rev), 3, 4)'
1375 $ log 'limit(sort(0::7, rev), 3, 4)'
1376 4
1376 4
1377 5
1377 5
1378 6
1378 6
1379 $ log 'limit(sort(0::7, -rev), 3, 4)'
1379 $ log 'limit(sort(0::7, -rev), 3, 4)'
1380 3
1380 3
1381 2
1381 2
1382 1
1382 1
1383 $ log 'last(sort(0::7, rev), 2)'
1383 $ log 'last(sort(0::7, rev), 2)'
1384 6
1384 6
1385 7
1385 7
1386 $ hg debugrevspec -s 'limit(sort(0::7, rev), 3, 6)'
1386 $ hg debugrevspec -s 'limit(sort(0::7, rev), 3, 6)'
1387 * set:
1387 * set:
1388 <baseset+ [6, 7]>
1388 <baseset+ [6, 7]>
1389 6
1389 6
1390 7
1390 7
1391 $ hg debugrevspec -s 'limit(sort(0::7, rev), 3, 9)'
1391 $ hg debugrevspec -s 'limit(sort(0::7, rev), 3, 9)'
1392 * set:
1392 * set:
1393 <baseset+ []>
1393 <baseset+ []>
1394 $ hg debugrevspec -s 'limit(sort(0::7, -rev), 3, 6)'
1394 $ hg debugrevspec -s 'limit(sort(0::7, -rev), 3, 6)'
1395 * set:
1395 * set:
1396 <baseset- [0, 1]>
1396 <baseset- [0, 1]>
1397 1
1397 1
1398 0
1398 0
1399 $ hg debugrevspec -s 'limit(sort(0::7, -rev), 3, 9)'
1399 $ hg debugrevspec -s 'limit(sort(0::7, -rev), 3, 9)'
1400 * set:
1400 * set:
1401 <baseset- []>
1401 <baseset- []>
1402 $ hg debugrevspec -s 'limit(0::7, 0)'
1402 $ hg debugrevspec -s 'limit(0::7, 0)'
1403 * set:
1403 * set:
1404 <baseset+ []>
1404 <baseset+ []>
1405
1405
1406 (using spanset)
1406 (using spanset)
1407
1407
1408 $ hg debugrevspec --no-show-revs -s 0:7
1408 $ hg debugrevspec --no-show-revs -s 0:7
1409 * set:
1409 * set:
1410 <spanset+ 0:8>
1410 <spanset+ 0:8>
1411 $ log 'limit(0:7, 3, 4)'
1411 $ log 'limit(0:7, 3, 4)'
1412 4
1412 4
1413 5
1413 5
1414 6
1414 6
1415 $ log 'limit(7:0, 3, 4)'
1415 $ log 'limit(7:0, 3, 4)'
1416 3
1416 3
1417 2
1417 2
1418 1
1418 1
1419 $ log 'limit(0:7, 3, 6)'
1419 $ log 'limit(0:7, 3, 6)'
1420 6
1420 6
1421 7
1421 7
1422 $ log 'limit(7:0, 3, 6)'
1422 $ log 'limit(7:0, 3, 6)'
1423 1
1423 1
1424 0
1424 0
1425 $ log 'last(0:7, 2)'
1425 $ log 'last(0:7, 2)'
1426 6
1426 6
1427 7
1427 7
1428 $ hg debugrevspec -s 'limit(0:7, 3, 6)'
1428 $ hg debugrevspec -s 'limit(0:7, 3, 6)'
1429 * set:
1429 * set:
1430 <spanset+ 6:8>
1430 <spanset+ 6:8>
1431 6
1431 6
1432 7
1432 7
1433 $ hg debugrevspec -s 'limit(0:7, 3, 9)'
1433 $ hg debugrevspec -s 'limit(0:7, 3, 9)'
1434 * set:
1434 * set:
1435 <spanset+ 8:8>
1435 <spanset+ 8:8>
1436 $ hg debugrevspec -s 'limit(7:0, 3, 6)'
1436 $ hg debugrevspec -s 'limit(7:0, 3, 6)'
1437 * set:
1437 * set:
1438 <spanset- 0:2>
1438 <spanset- 0:2>
1439 1
1439 1
1440 0
1440 0
1441 $ hg debugrevspec -s 'limit(7:0, 3, 9)'
1441 $ hg debugrevspec -s 'limit(7:0, 3, 9)'
1442 * set:
1442 * set:
1443 <spanset- 0:0>
1443 <spanset- 0:0>
1444 $ hg debugrevspec -s 'limit(0:7, 0)'
1444 $ hg debugrevspec -s 'limit(0:7, 0)'
1445 * set:
1445 * set:
1446 <spanset+ 0:0>
1446 <spanset+ 0:0>
1447
1447
1448 Test order of first/last revisions
1448 Test order of first/last revisions
1449
1449
1450 $ hg debugrevspec -s 'first(4:0, 3) & 3:'
1450 $ hg debugrevspec -s 'first(4:0, 3) & 3:'
1451 * set:
1451 * set:
1452 <filteredset
1452 <filteredset
1453 <spanset- 2:5>,
1453 <spanset- 2:5>,
1454 <spanset+ 3:10>>
1454 <spanset+ 3:10>>
1455 4
1455 4
1456 3
1456 3
1457
1457
1458 $ hg debugrevspec -s '3: & first(4:0, 3)'
1458 $ hg debugrevspec -s '3: & first(4:0, 3)'
1459 * set:
1459 * set:
1460 <filteredset
1460 <filteredset
1461 <spanset+ 3:10>,
1461 <spanset+ 3:10>,
1462 <spanset- 2:5>>
1462 <spanset- 2:5>>
1463 3
1463 3
1464 4
1464 4
1465
1465
1466 $ hg debugrevspec -s 'last(4:0, 3) & :1'
1466 $ hg debugrevspec -s 'last(4:0, 3) & :1'
1467 * set:
1467 * set:
1468 <filteredset
1468 <filteredset
1469 <spanset- 0:3>,
1469 <spanset- 0:3>,
1470 <spanset+ 0:2>>
1470 <spanset+ 0:2>>
1471 1
1471 1
1472 0
1472 0
1473
1473
1474 $ hg debugrevspec -s ':1 & last(4:0, 3)'
1474 $ hg debugrevspec -s ':1 & last(4:0, 3)'
1475 * set:
1475 * set:
1476 <filteredset
1476 <filteredset
1477 <spanset+ 0:2>,
1477 <spanset+ 0:2>,
1478 <spanset+ 0:3>>
1478 <spanset+ 0:3>>
1479 0
1479 0
1480 1
1480 1
1481
1481
1482 Test scmutil.revsingle() should return the last revision
1482 Test scmutil.revsingle() should return the last revision
1483
1483
1484 $ hg debugrevspec -s 'last(0::)'
1484 $ hg debugrevspec -s 'last(0::)'
1485 * set:
1485 * set:
1486 <baseset slice=0:1
1486 <baseset slice=0:1
1487 <generatorset->>
1487 <generatorset->>
1488 9
1488 9
1489 $ hg identify -r '0::' --num
1489 $ hg identify -r '0::' --num
1490 9
1490 9
1491
1491
1492 Test matching
1492 Test matching
1493
1493
1494 $ log 'matching(6)'
1494 $ log 'matching(6)'
1495 6
1495 6
1496 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
1496 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
1497 6
1497 6
1498 7
1498 7
1499
1499
1500 Testing min and max
1500 Testing min and max
1501
1501
1502 max: simple
1502 max: simple
1503
1503
1504 $ log 'max(contains(a))'
1504 $ log 'max(contains(a))'
1505 5
1505 5
1506
1506
1507 max: simple on unordered set)
1507 max: simple on unordered set)
1508
1508
1509 $ log 'max((4+0+2+5+7) and contains(a))'
1509 $ log 'max((4+0+2+5+7) and contains(a))'
1510 5
1510 5
1511
1511
1512 max: no result
1512 max: no result
1513
1513
1514 $ log 'max(contains(stringthatdoesnotappearanywhere))'
1514 $ log 'max(contains(stringthatdoesnotappearanywhere))'
1515
1515
1516 max: no result on unordered set
1516 max: no result on unordered set
1517
1517
1518 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1518 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1519
1519
1520 min: simple
1520 min: simple
1521
1521
1522 $ log 'min(contains(a))'
1522 $ log 'min(contains(a))'
1523 0
1523 0
1524
1524
1525 min: simple on unordered set
1525 min: simple on unordered set
1526
1526
1527 $ log 'min((4+0+2+5+7) and contains(a))'
1527 $ log 'min((4+0+2+5+7) and contains(a))'
1528 0
1528 0
1529
1529
1530 min: empty
1530 min: empty
1531
1531
1532 $ log 'min(contains(stringthatdoesnotappearanywhere))'
1532 $ log 'min(contains(stringthatdoesnotappearanywhere))'
1533
1533
1534 min: empty on unordered set
1534 min: empty on unordered set
1535
1535
1536 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1536 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1537
1537
1538
1538
1539 $ log 'merge()'
1539 $ log 'merge()'
1540 6
1540 6
1541 $ log 'branchpoint()'
1541 $ log 'branchpoint()'
1542 1
1542 1
1543 4
1543 4
1544 $ log 'modifies(b)'
1544 $ log 'modifies(b)'
1545 4
1545 4
1546 $ log 'modifies("path:b")'
1546 $ log 'modifies("path:b")'
1547 4
1547 4
1548 $ log 'modifies("*")'
1548 $ log 'modifies("*")'
1549 4
1549 4
1550 6
1550 6
1551 $ log 'modifies("set:modified()")'
1551 $ log 'modifies("set:modified()")'
1552 4
1552 4
1553 $ log 'id(5)'
1553 $ log 'id(5)'
1554 2
1554 2
1555 $ log 'only(9)'
1555 $ log 'only(9)'
1556 8
1556 8
1557 9
1557 9
1558 $ log 'only(8)'
1558 $ log 'only(8)'
1559 8
1559 8
1560 $ log 'only(9, 5)'
1560 $ log 'only(9, 5)'
1561 2
1561 2
1562 4
1562 4
1563 8
1563 8
1564 9
1564 9
1565 $ log 'only(7 + 9, 5 + 2)'
1565 $ log 'only(7 + 9, 5 + 2)'
1566 4
1566 4
1567 6
1567 6
1568 7
1568 7
1569 8
1569 8
1570 9
1570 9
1571
1571
1572 Test empty set input
1572 Test empty set input
1573 $ log 'only(p2())'
1573 $ log 'only(p2())'
1574 $ log 'only(p1(), p2())'
1574 $ log 'only(p1(), p2())'
1575 0
1575 0
1576 1
1576 1
1577 2
1577 2
1578 4
1578 4
1579 8
1579 8
1580 9
1580 9
1581
1581
1582 Test '%' operator
1582 Test '%' operator
1583
1583
1584 $ log '9%'
1584 $ log '9%'
1585 8
1585 8
1586 9
1586 9
1587 $ log '9%5'
1587 $ log '9%5'
1588 2
1588 2
1589 4
1589 4
1590 8
1590 8
1591 9
1591 9
1592 $ log '(7 + 9)%(5 + 2)'
1592 $ log '(7 + 9)%(5 + 2)'
1593 4
1593 4
1594 6
1594 6
1595 7
1595 7
1596 8
1596 8
1597 9
1597 9
1598
1598
1599 Test operand of '%' is optimized recursively (issue4670)
1599 Test operand of '%' is optimized recursively (issue4670)
1600
1600
1601 $ try --optimize '8:9-8%'
1601 $ try --optimize '8:9-8%'
1602 (onlypost
1602 (onlypost
1603 (minus
1603 (minus
1604 (range
1604 (range
1605 ('symbol', '8')
1605 ('symbol', '8')
1606 ('symbol', '9'))
1606 ('symbol', '9'))
1607 ('symbol', '8')))
1607 ('symbol', '8')))
1608 * optimized:
1608 * optimized:
1609 (func
1609 (func
1610 ('symbol', 'only')
1610 ('symbol', 'only')
1611 (difference
1611 (difference
1612 (range
1612 (range
1613 ('symbol', '8')
1613 ('symbol', '8')
1614 ('symbol', '9'))
1614 ('symbol', '9'))
1615 ('symbol', '8')))
1615 ('symbol', '8')))
1616 * set:
1616 * set:
1617 <baseset+ [8, 9]>
1617 <baseset+ [8, 9]>
1618 8
1618 8
1619 9
1619 9
1620 $ try --optimize '(9)%(5)'
1620 $ try --optimize '(9)%(5)'
1621 (only
1621 (only
1622 (group
1622 (group
1623 ('symbol', '9'))
1623 ('symbol', '9'))
1624 (group
1624 (group
1625 ('symbol', '5')))
1625 ('symbol', '5')))
1626 * optimized:
1626 * optimized:
1627 (func
1627 (func
1628 ('symbol', 'only')
1628 ('symbol', 'only')
1629 (list
1629 (list
1630 ('symbol', '9')
1630 ('symbol', '9')
1631 ('symbol', '5')))
1631 ('symbol', '5')))
1632 * set:
1632 * set:
1633 <baseset+ [2, 4, 8, 9]>
1633 <baseset+ [2, 4, 8, 9]>
1634 2
1634 2
1635 4
1635 4
1636 8
1636 8
1637 9
1637 9
1638
1638
1639 Test the order of operations
1639 Test the order of operations
1640
1640
1641 $ log '7 + 9%5 + 2'
1641 $ log '7 + 9%5 + 2'
1642 7
1642 7
1643 2
1643 2
1644 4
1644 4
1645 8
1645 8
1646 9
1646 9
1647
1647
1648 Test explicit numeric revision
1648 Test explicit numeric revision
1649 $ log 'rev(-2)'
1649 $ log 'rev(-2)'
1650 $ log 'rev(-1)'
1650 $ log 'rev(-1)'
1651 -1
1651 -1
1652 $ log 'rev(0)'
1652 $ log 'rev(0)'
1653 0
1653 0
1654 $ log 'rev(9)'
1654 $ log 'rev(9)'
1655 9
1655 9
1656 $ log 'rev(10)'
1656 $ log 'rev(10)'
1657 $ log 'rev(tip)'
1657 $ log 'rev(tip)'
1658 hg: parse error: rev expects a number
1658 hg: parse error: rev expects a number
1659 [255]
1659 [255]
1660
1660
1661 Test hexadecimal revision
1661 Test hexadecimal revision
1662 $ log 'id(2)'
1662 $ log 'id(2)'
1663 abort: 00changelog.i@2: ambiguous identifier!
1663 abort: 00changelog.i@2: ambiguous identifier!
1664 [255]
1664 [255]
1665 $ log 'id(23268)'
1665 $ log 'id(23268)'
1666 4
1666 4
1667 $ log 'id(2785f51eece)'
1667 $ log 'id(2785f51eece)'
1668 0
1668 0
1669 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
1669 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
1670 8
1670 8
1671 $ log 'id(d5d0dcbdc4a)'
1671 $ log 'id(d5d0dcbdc4a)'
1672 $ log 'id(d5d0dcbdc4w)'
1672 $ log 'id(d5d0dcbdc4w)'
1673 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
1673 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
1674 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
1674 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
1675 $ log 'id(1.0)'
1675 $ log 'id(1.0)'
1676 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
1676 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
1677
1677
1678 Test null revision
1678 Test null revision
1679 $ log '(null)'
1679 $ log '(null)'
1680 -1
1680 -1
1681 $ log '(null:0)'
1681 $ log '(null:0)'
1682 -1
1682 -1
1683 0
1683 0
1684 $ log '(0:null)'
1684 $ log '(0:null)'
1685 0
1685 0
1686 -1
1686 -1
1687 $ log 'null::0'
1687 $ log 'null::0'
1688 -1
1688 -1
1689 0
1689 0
1690 $ log 'null:tip - 0:'
1690 $ log 'null:tip - 0:'
1691 -1
1691 -1
1692 $ log 'null: and null::' | head -1
1692 $ log 'null: and null::' | head -1
1693 -1
1693 -1
1694 $ log 'null: or 0:' | head -2
1694 $ log 'null: or 0:' | head -2
1695 -1
1695 -1
1696 0
1696 0
1697 $ log 'ancestors(null)'
1697 $ log 'ancestors(null)'
1698 -1
1698 -1
1699 $ log 'reverse(null:)' | tail -2
1699 $ log 'reverse(null:)' | tail -2
1700 0
1700 0
1701 -1
1701 -1
1702 $ log 'first(null:)'
1702 $ log 'first(null:)'
1703 -1
1703 -1
1704 $ log 'min(null:)'
1704 $ log 'min(null:)'
1705 BROKEN: should be '-1'
1705 BROKEN: should be '-1'
1706 $ log 'tip:null and all()' | tail -2
1706 $ log 'tip:null and all()' | tail -2
1707 1
1707 1
1708 0
1708 0
1709
1709
1710 Test working-directory revision
1710 Test working-directory revision
1711 $ hg debugrevspec 'wdir()'
1711 $ hg debugrevspec 'wdir()'
1712 2147483647
1712 2147483647
1713 $ hg debugrevspec 'wdir()^'
1713 $ hg debugrevspec 'wdir()^'
1714 9
1714 9
1715 $ hg up 7
1715 $ hg up 7
1716 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1716 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1717 $ hg debugrevspec 'wdir()^'
1717 $ hg debugrevspec 'wdir()^'
1718 7
1718 7
1719 $ hg debugrevspec 'wdir()^0'
1719 $ hg debugrevspec 'wdir()^0'
1720 2147483647
1720 2147483647
1721 $ hg debugrevspec 'wdir()~3'
1721 $ hg debugrevspec 'wdir()~3'
1722 5
1722 5
1723 $ hg debugrevspec 'ancestors(wdir())'
1723 $ hg debugrevspec 'ancestors(wdir())'
1724 0
1724 0
1725 1
1725 1
1726 2
1726 2
1727 3
1727 3
1728 4
1728 4
1729 5
1729 5
1730 6
1730 6
1731 7
1731 7
1732 2147483647
1732 2147483647
1733 $ hg debugrevspec 'wdir()~0'
1733 $ hg debugrevspec 'wdir()~0'
1734 2147483647
1734 2147483647
1735 $ hg debugrevspec 'p1(wdir())'
1735 $ hg debugrevspec 'p1(wdir())'
1736 7
1736 7
1737 $ hg debugrevspec 'p2(wdir())'
1737 $ hg debugrevspec 'p2(wdir())'
1738 $ hg debugrevspec 'parents(wdir())'
1738 $ hg debugrevspec 'parents(wdir())'
1739 7
1739 7
1740 $ hg debugrevspec 'wdir()^1'
1740 $ hg debugrevspec 'wdir()^1'
1741 7
1741 7
1742 $ hg debugrevspec 'wdir()^2'
1742 $ hg debugrevspec 'wdir()^2'
1743 $ hg debugrevspec 'wdir()^3'
1743 $ hg debugrevspec 'wdir()^3'
1744 hg: parse error: ^ expects a number 0, 1, or 2
1744 hg: parse error: ^ expects a number 0, 1, or 2
1745 [255]
1745 [255]
1746 For tests consistency
1746 For tests consistency
1747 $ hg up 9
1747 $ hg up 9
1748 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1748 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1749 $ hg debugrevspec 'tip or wdir()'
1749 $ hg debugrevspec 'tip or wdir()'
1750 9
1750 9
1751 2147483647
1751 2147483647
1752 $ hg debugrevspec '0:tip and wdir()'
1752 $ hg debugrevspec '0:tip and wdir()'
1753 $ log '0:wdir()' | tail -3
1753 $ log '0:wdir()' | tail -3
1754 8
1754 8
1755 9
1755 9
1756 2147483647
1756 2147483647
1757 $ log 'wdir():0' | head -3
1757 $ log 'wdir():0' | head -3
1758 2147483647
1758 2147483647
1759 9
1759 9
1760 8
1760 8
1761 $ log 'wdir():wdir()'
1761 $ log 'wdir():wdir()'
1762 2147483647
1762 2147483647
1763 $ log '(all() + wdir()) & min(. + wdir())'
1763 $ log '(all() + wdir()) & min(. + wdir())'
1764 9
1764 9
1765 $ log '(all() + wdir()) & max(. + wdir())'
1765 $ log '(all() + wdir()) & max(. + wdir())'
1766 2147483647
1766 2147483647
1767 $ log 'first(wdir() + .)'
1767 $ log 'first(wdir() + .)'
1768 2147483647
1768 2147483647
1769 $ log 'last(. + wdir())'
1769 $ log 'last(. + wdir())'
1770 2147483647
1770 2147483647
1771
1771
1772 Test working-directory integer revision and node id
1772 Test working-directory integer revision and node id
1773 (BUG: '0:wdir()' is still needed to populate wdir revision)
1773 (BUG: '0:wdir()' is still needed to populate wdir revision)
1774
1774
1775 $ hg debugrevspec '0:wdir() & 2147483647'
1775 $ hg debugrevspec '0:wdir() & 2147483647'
1776 2147483647
1776 2147483647
1777 $ hg debugrevspec '0:wdir() & rev(2147483647)'
1777 $ hg debugrevspec '0:wdir() & rev(2147483647)'
1778 2147483647
1778 2147483647
1779 $ hg debugrevspec '0:wdir() & ffffffffffffffffffffffffffffffffffffffff'
1779 $ hg debugrevspec '0:wdir() & ffffffffffffffffffffffffffffffffffffffff'
1780 2147483647
1780 2147483647
1781 $ hg debugrevspec '0:wdir() & ffffffffffff'
1781 $ hg debugrevspec '0:wdir() & ffffffffffff'
1782 2147483647
1782 2147483647
1783 $ hg debugrevspec '0:wdir() & id(ffffffffffffffffffffffffffffffffffffffff)'
1783 $ hg debugrevspec '0:wdir() & id(ffffffffffffffffffffffffffffffffffffffff)'
1784 2147483647
1784 2147483647
1785 $ hg debugrevspec '0:wdir() & id(ffffffffffff)'
1785 $ hg debugrevspec '0:wdir() & id(ffffffffffff)'
1786 2147483647
1786 2147483647
1787
1787
1788 $ cd ..
1788 $ cd ..
1789
1789
1790 Test short 'ff...' hash collision
1790 Test short 'ff...' hash collision
1791 (BUG: '0:wdir()' is still needed to populate wdir revision)
1791 (BUG: '0:wdir()' is still needed to populate wdir revision)
1792
1792
1793 $ hg init wdir-hashcollision
1793 $ hg init wdir-hashcollision
1794 $ cd wdir-hashcollision
1794 $ cd wdir-hashcollision
1795 $ cat <<EOF >> .hg/hgrc
1795 $ cat <<EOF >> .hg/hgrc
1796 > [experimental]
1796 > [experimental]
1797 > stabilization = createmarkers
1797 > stabilization = createmarkers
1798 > EOF
1798 > EOF
1799 $ echo 0 > a
1799 $ echo 0 > a
1800 $ hg ci -qAm 0
1800 $ hg ci -qAm 0
1801 $ for i in 2463 2961 6726 78127; do
1801 $ for i in 2463 2961 6726 78127; do
1802 > hg up -q 0
1802 > hg up -q 0
1803 > echo $i > a
1803 > echo $i > a
1804 > hg ci -qm $i
1804 > hg ci -qm $i
1805 > done
1805 > done
1806 $ hg up -q null
1806 $ hg up -q null
1807 $ hg log -r '0:wdir()' -T '{rev}:{node} {shortest(node, 3)}\n'
1807 $ hg log -r '0:wdir()' -T '{rev}:{node} {shortest(node, 3)}\n'
1808 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a b4e
1808 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a b4e
1809 1:fffbae3886c8fbb2114296380d276fd37715d571 fffba
1809 1:fffbae3886c8fbb2114296380d276fd37715d571 fffba
1810 2:fffb6093b00943f91034b9bdad069402c834e572 fffb6
1810 2:fffb6093b00943f91034b9bdad069402c834e572 fffb6
1811 3:fff48a9b9de34a4d64120c29548214c67980ade3 fff4
1811 3:fff48a9b9de34a4d64120c29548214c67980ade3 fff4
1812 4:ffff85cff0ff78504fcdc3c0bc10de0c65379249 ffff8
1812 4:ffff85cff0ff78504fcdc3c0bc10de0c65379249 ffff8
1813 2147483647:ffffffffffffffffffffffffffffffffffffffff fffff
1813 2147483647:ffffffffffffffffffffffffffffffffffffffff fffff
1814 $ hg debugobsolete fffbae3886c8fbb2114296380d276fd37715d571
1814 $ hg debugobsolete fffbae3886c8fbb2114296380d276fd37715d571
1815 obsoleted 1 changesets
1815 obsoleted 1 changesets
1816
1816
1817 $ hg debugrevspec '0:wdir() & fff'
1817 $ hg debugrevspec '0:wdir() & fff'
1818 abort: 00changelog.i@fff: ambiguous identifier!
1818 abort: 00changelog.i@fff: ambiguous identifier!
1819 [255]
1819 [255]
1820 $ hg debugrevspec '0:wdir() & ffff'
1820 $ hg debugrevspec '0:wdir() & ffff'
1821 abort: 00changelog.i@ffff: ambiguous identifier!
1821 abort: 00changelog.i@ffff: ambiguous identifier!
1822 [255]
1822 [255]
1823 $ hg debugrevspec '0:wdir() & fffb'
1823 $ hg debugrevspec '0:wdir() & fffb'
1824 abort: 00changelog.i@fffb: ambiguous identifier!
1824 abort: 00changelog.i@fffb: ambiguous identifier!
1825 [255]
1825 [255]
1826 BROKEN should be '2' (node lookup uses unfiltered repo since dc25ed84bee8)
1826 BROKEN should be '2' (node lookup uses unfiltered repo since dc25ed84bee8)
1827 $ hg debugrevspec '0:wdir() & id(fffb)'
1827 $ hg debugrevspec '0:wdir() & id(fffb)'
1828 2
1828 2
1829 $ hg debugrevspec '0:wdir() & ffff8'
1829 $ hg debugrevspec '0:wdir() & ffff8'
1830 4
1830 4
1831 $ hg debugrevspec '0:wdir() & fffff'
1831 $ hg debugrevspec '0:wdir() & fffff'
1832 2147483647
1832 2147483647
1833
1833
1834 $ cd ..
1834 $ cd ..
1835
1835
1836 Test branch() with wdir()
1836 Test branch() with wdir()
1837
1837
1838 $ cd repo
1838 $ cd repo
1839
1839
1840 $ log '0:wdir() & branch("literal:Γ©")'
1840 $ log '0:wdir() & branch("literal:Γ©")'
1841 8
1841 8
1842 9
1842 9
1843 2147483647
1843 2147483647
1844 $ log '0:wdir() & branch("re:Γ©")'
1844 $ log '0:wdir() & branch("re:Γ©")'
1845 8
1845 8
1846 9
1846 9
1847 2147483647
1847 2147483647
1848 $ log '0:wdir() & branch("re:^a")'
1848 $ log '0:wdir() & branch("re:^a")'
1849 0
1849 0
1850 2
1850 2
1851 $ log '0:wdir() & branch(8)'
1851 $ log '0:wdir() & branch(8)'
1852 8
1852 8
1853 9
1853 9
1854 2147483647
1854 2147483647
1855
1855
1856 branch(wdir()) returns all revisions belonging to the working branch. The wdir
1856 branch(wdir()) returns all revisions belonging to the working branch. The wdir
1857 itself isn't returned unless it is explicitly populated.
1857 itself isn't returned unless it is explicitly populated.
1858
1858
1859 $ log 'branch(wdir())'
1859 $ log 'branch(wdir())'
1860 8
1860 8
1861 9
1861 9
1862 $ log '0:wdir() & branch(wdir())'
1862 $ log '0:wdir() & branch(wdir())'
1863 8
1863 8
1864 9
1864 9
1865 2147483647
1865 2147483647
1866
1866
1867 $ log 'outgoing()'
1867 $ log 'outgoing()'
1868 8
1868 8
1869 9
1869 9
1870 $ log 'outgoing("../remote1")'
1870 $ log 'outgoing("../remote1")'
1871 8
1871 8
1872 9
1872 9
1873 $ log 'outgoing("../remote2")'
1873 $ log 'outgoing("../remote2")'
1874 3
1874 3
1875 5
1875 5
1876 6
1876 6
1877 7
1877 7
1878 9
1878 9
1879 $ log 'p1(merge())'
1879 $ log 'p1(merge())'
1880 5
1880 5
1881 $ log 'p2(merge())'
1881 $ log 'p2(merge())'
1882 4
1882 4
1883 $ log 'parents(merge())'
1883 $ log 'parents(merge())'
1884 4
1884 4
1885 5
1885 5
1886 $ log 'p1(branchpoint())'
1886 $ log 'p1(branchpoint())'
1887 0
1887 0
1888 2
1888 2
1889 $ log 'p2(branchpoint())'
1889 $ log 'p2(branchpoint())'
1890 $ log 'parents(branchpoint())'
1890 $ log 'parents(branchpoint())'
1891 0
1891 0
1892 2
1892 2
1893 $ log 'removes(a)'
1893 $ log 'removes(a)'
1894 2
1894 2
1895 6
1895 6
1896 $ log 'roots(all())'
1896 $ log 'roots(all())'
1897 0
1897 0
1898 $ log 'reverse(2 or 3 or 4 or 5)'
1898 $ log 'reverse(2 or 3 or 4 or 5)'
1899 5
1899 5
1900 4
1900 4
1901 3
1901 3
1902 2
1902 2
1903 $ log 'reverse(all())'
1903 $ log 'reverse(all())'
1904 9
1904 9
1905 8
1905 8
1906 7
1906 7
1907 6
1907 6
1908 5
1908 5
1909 4
1909 4
1910 3
1910 3
1911 2
1911 2
1912 1
1912 1
1913 0
1913 0
1914 $ log 'reverse(all()) & filelog(b)'
1914 $ log 'reverse(all()) & filelog(b)'
1915 4
1915 4
1916 1
1916 1
1917 $ log 'rev(5)'
1917 $ log 'rev(5)'
1918 5
1918 5
1919 $ log 'sort(limit(reverse(all()), 3))'
1919 $ log 'sort(limit(reverse(all()), 3))'
1920 7
1920 7
1921 8
1921 8
1922 9
1922 9
1923 $ log 'sort(2 or 3 or 4 or 5, date)'
1923 $ log 'sort(2 or 3 or 4 or 5, date)'
1924 2
1924 2
1925 3
1925 3
1926 5
1926 5
1927 4
1927 4
1928 $ log 'tagged()'
1928 $ log 'tagged()'
1929 6
1929 6
1930 $ log 'tag()'
1930 $ log 'tag()'
1931 6
1931 6
1932 $ log 'tag(1.0)'
1932 $ log 'tag(1.0)'
1933 6
1933 6
1934 $ log 'tag(tip)'
1934 $ log 'tag(tip)'
1935 9
1935 9
1936
1936
1937 Test order of revisions in compound expression
1937 Test order of revisions in compound expression
1938 ----------------------------------------------
1938 ----------------------------------------------
1939
1939
1940 The general rule is that only the outermost (= leftmost) predicate can
1940 The general rule is that only the outermost (= leftmost) predicate can
1941 enforce its ordering requirement. The other predicates should take the
1941 enforce its ordering requirement. The other predicates should take the
1942 ordering defined by it.
1942 ordering defined by it.
1943
1943
1944 'A & B' should follow the order of 'A':
1944 'A & B' should follow the order of 'A':
1945
1945
1946 $ log '2:0 & 0::2'
1946 $ log '2:0 & 0::2'
1947 2
1947 2
1948 1
1948 1
1949 0
1949 0
1950
1950
1951 'head()' combines sets in right order:
1951 'head()' combines sets in right order:
1952
1952
1953 $ log '2:0 & head()'
1953 $ log '2:0 & head()'
1954 2
1954 2
1955 1
1955 1
1956 0
1956 0
1957
1957
1958 'x:y' takes ordering parameter into account:
1958 'x:y' takes ordering parameter into account:
1959
1959
1960 $ try -p optimized '3:0 & 0:3 & not 2:1'
1960 $ try -p optimized '3:0 & 0:3 & not 2:1'
1961 * optimized:
1961 * optimized:
1962 (difference
1962 (difference
1963 (and
1963 (and
1964 (range
1964 (range
1965 ('symbol', '3')
1965 ('symbol', '3')
1966 ('symbol', '0'))
1966 ('symbol', '0'))
1967 (range
1967 (range
1968 ('symbol', '0')
1968 ('symbol', '0')
1969 ('symbol', '3')))
1969 ('symbol', '3')))
1970 (range
1970 (range
1971 ('symbol', '2')
1971 ('symbol', '2')
1972 ('symbol', '1')))
1972 ('symbol', '1')))
1973 * set:
1973 * set:
1974 <filteredset
1974 <filteredset
1975 <filteredset
1975 <filteredset
1976 <spanset- 0:4>,
1976 <spanset- 0:4>,
1977 <spanset+ 0:4>>,
1977 <spanset+ 0:4>>,
1978 <not
1978 <not
1979 <spanset+ 1:3>>>
1979 <spanset+ 1:3>>>
1980 3
1980 3
1981 0
1981 0
1982
1982
1983 'a + b', which is optimized to '_list(a b)', should take the ordering of
1983 'a + b', which is optimized to '_list(a b)', should take the ordering of
1984 the left expression:
1984 the left expression:
1985
1985
1986 $ try --optimize '2:0 & (0 + 1 + 2)'
1986 $ try --optimize '2:0 & (0 + 1 + 2)'
1987 (and
1987 (and
1988 (range
1988 (range
1989 ('symbol', '2')
1989 ('symbol', '2')
1990 ('symbol', '0'))
1990 ('symbol', '0'))
1991 (group
1991 (group
1992 (or
1992 (or
1993 (list
1993 (list
1994 ('symbol', '0')
1994 ('symbol', '0')
1995 ('symbol', '1')
1995 ('symbol', '1')
1996 ('symbol', '2')))))
1996 ('symbol', '2')))))
1997 * optimized:
1997 * optimized:
1998 (and
1998 (and
1999 (range
1999 (range
2000 ('symbol', '2')
2000 ('symbol', '2')
2001 ('symbol', '0'))
2001 ('symbol', '0'))
2002 (func
2002 (func
2003 ('symbol', '_list')
2003 ('symbol', '_list')
2004 ('string', '0\x001\x002')))
2004 ('string', '0\x001\x002')))
2005 * set:
2005 * set:
2006 <filteredset
2006 <filteredset
2007 <spanset- 0:3>,
2007 <spanset- 0:3>,
2008 <baseset [0, 1, 2]>>
2008 <baseset [0, 1, 2]>>
2009 2
2009 2
2010 1
2010 1
2011 0
2011 0
2012
2012
2013 'A + B' should take the ordering of the left expression:
2013 'A + B' should take the ordering of the left expression:
2014
2014
2015 $ try --optimize '2:0 & (0:1 + 2)'
2015 $ try --optimize '2:0 & (0:1 + 2)'
2016 (and
2016 (and
2017 (range
2017 (range
2018 ('symbol', '2')
2018 ('symbol', '2')
2019 ('symbol', '0'))
2019 ('symbol', '0'))
2020 (group
2020 (group
2021 (or
2021 (or
2022 (list
2022 (list
2023 (range
2023 (range
2024 ('symbol', '0')
2024 ('symbol', '0')
2025 ('symbol', '1'))
2025 ('symbol', '1'))
2026 ('symbol', '2')))))
2026 ('symbol', '2')))))
2027 * optimized:
2027 * optimized:
2028 (and
2028 (and
2029 (range
2029 (range
2030 ('symbol', '2')
2030 ('symbol', '2')
2031 ('symbol', '0'))
2031 ('symbol', '0'))
2032 (or
2032 (or
2033 (list
2033 (list
2034 (range
2034 (range
2035 ('symbol', '0')
2035 ('symbol', '0')
2036 ('symbol', '1'))
2036 ('symbol', '1'))
2037 ('symbol', '2'))))
2037 ('symbol', '2'))))
2038 * set:
2038 * set:
2039 <filteredset
2039 <filteredset
2040 <spanset- 0:3>,
2040 <spanset- 0:3>,
2041 <addset
2041 <addset
2042 <spanset+ 0:2>,
2042 <spanset+ 0:2>,
2043 <baseset [2]>>>
2043 <baseset [2]>>>
2044 2
2044 2
2045 1
2045 1
2046 0
2046 0
2047
2047
2048 '_intlist(a b)' should behave like 'a + b':
2048 '_intlist(a b)' should behave like 'a + b':
2049
2049
2050 $ trylist --optimize '2:0 & %ld' 0 1 2
2050 $ trylist --optimize '2:0 & %ld' 0 1 2
2051 (and
2051 (and
2052 (range
2052 (range
2053 ('symbol', '2')
2053 ('symbol', '2')
2054 ('symbol', '0'))
2054 ('symbol', '0'))
2055 (func
2055 (func
2056 ('symbol', '_intlist')
2056 ('symbol', '_intlist')
2057 ('string', '0\x001\x002')))
2057 ('string', '0\x001\x002')))
2058 * optimized:
2058 * optimized:
2059 (andsmally
2059 (andsmally
2060 (range
2060 (range
2061 ('symbol', '2')
2061 ('symbol', '2')
2062 ('symbol', '0'))
2062 ('symbol', '0'))
2063 (func
2063 (func
2064 ('symbol', '_intlist')
2064 ('symbol', '_intlist')
2065 ('string', '0\x001\x002')))
2065 ('string', '0\x001\x002')))
2066 * set:
2066 * set:
2067 <filteredset
2067 <filteredset
2068 <spanset- 0:3>,
2068 <spanset- 0:3>,
2069 <baseset+ [0, 1, 2]>>
2069 <baseset+ [0, 1, 2]>>
2070 2
2070 2
2071 1
2071 1
2072 0
2072 0
2073
2073
2074 $ trylist --optimize '%ld & 2:0' 0 2 1
2074 $ trylist --optimize '%ld & 2:0' 0 2 1
2075 (and
2075 (and
2076 (func
2076 (func
2077 ('symbol', '_intlist')
2077 ('symbol', '_intlist')
2078 ('string', '0\x002\x001'))
2078 ('string', '0\x002\x001'))
2079 (range
2079 (range
2080 ('symbol', '2')
2080 ('symbol', '2')
2081 ('symbol', '0')))
2081 ('symbol', '0')))
2082 * optimized:
2082 * optimized:
2083 (and
2083 (and
2084 (func
2084 (func
2085 ('symbol', '_intlist')
2085 ('symbol', '_intlist')
2086 ('string', '0\x002\x001'))
2086 ('string', '0\x002\x001'))
2087 (range
2087 (range
2088 ('symbol', '2')
2088 ('symbol', '2')
2089 ('symbol', '0')))
2089 ('symbol', '0')))
2090 * set:
2090 * set:
2091 <filteredset
2091 <filteredset
2092 <baseset [0, 2, 1]>,
2092 <baseset [0, 2, 1]>,
2093 <spanset- 0:3>>
2093 <spanset- 0:3>>
2094 0
2094 0
2095 2
2095 2
2096 1
2096 1
2097
2097
2098 '_hexlist(a b)' should behave like 'a + b':
2098 '_hexlist(a b)' should behave like 'a + b':
2099
2099
2100 $ trylist --optimize --bin '2:0 & %ln' `hg log -T '{node} ' -r0:2`
2100 $ trylist --optimize --bin '2:0 & %ln' `hg log -T '{node} ' -r0:2`
2101 (and
2101 (and
2102 (range
2102 (range
2103 ('symbol', '2')
2103 ('symbol', '2')
2104 ('symbol', '0'))
2104 ('symbol', '0'))
2105 (func
2105 (func
2106 ('symbol', '_hexlist')
2106 ('symbol', '_hexlist')
2107 ('string', '*'))) (glob)
2107 ('string', '*'))) (glob)
2108 * optimized:
2108 * optimized:
2109 (and
2109 (and
2110 (range
2110 (range
2111 ('symbol', '2')
2111 ('symbol', '2')
2112 ('symbol', '0'))
2112 ('symbol', '0'))
2113 (func
2113 (func
2114 ('symbol', '_hexlist')
2114 ('symbol', '_hexlist')
2115 ('string', '*'))) (glob)
2115 ('string', '*'))) (glob)
2116 * set:
2116 * set:
2117 <filteredset
2117 <filteredset
2118 <spanset- 0:3>,
2118 <spanset- 0:3>,
2119 <baseset [0, 1, 2]>>
2119 <baseset [0, 1, 2]>>
2120 2
2120 2
2121 1
2121 1
2122 0
2122 0
2123
2123
2124 $ trylist --optimize --bin '%ln & 2:0' `hg log -T '{node} ' -r0+2+1`
2124 $ trylist --optimize --bin '%ln & 2:0' `hg log -T '{node} ' -r0+2+1`
2125 (and
2125 (and
2126 (func
2126 (func
2127 ('symbol', '_hexlist')
2127 ('symbol', '_hexlist')
2128 ('string', '*')) (glob)
2128 ('string', '*')) (glob)
2129 (range
2129 (range
2130 ('symbol', '2')
2130 ('symbol', '2')
2131 ('symbol', '0')))
2131 ('symbol', '0')))
2132 * optimized:
2132 * optimized:
2133 (andsmally
2133 (andsmally
2134 (func
2134 (func
2135 ('symbol', '_hexlist')
2135 ('symbol', '_hexlist')
2136 ('string', '*')) (glob)
2136 ('string', '*')) (glob)
2137 (range
2137 (range
2138 ('symbol', '2')
2138 ('symbol', '2')
2139 ('symbol', '0')))
2139 ('symbol', '0')))
2140 * set:
2140 * set:
2141 <baseset [0, 2, 1]>
2141 <baseset [0, 2, 1]>
2142 0
2142 0
2143 2
2143 2
2144 1
2144 1
2145
2145
2146 '_list' should not go through the slow follow-order path if order doesn't
2146 '_list' should not go through the slow follow-order path if order doesn't
2147 matter:
2147 matter:
2148
2148
2149 $ try -p optimized '2:0 & not (0 + 1)'
2149 $ try -p optimized '2:0 & not (0 + 1)'
2150 * optimized:
2150 * optimized:
2151 (difference
2151 (difference
2152 (range
2152 (range
2153 ('symbol', '2')
2153 ('symbol', '2')
2154 ('symbol', '0'))
2154 ('symbol', '0'))
2155 (func
2155 (func
2156 ('symbol', '_list')
2156 ('symbol', '_list')
2157 ('string', '0\x001')))
2157 ('string', '0\x001')))
2158 * set:
2158 * set:
2159 <filteredset
2159 <filteredset
2160 <spanset- 0:3>,
2160 <spanset- 0:3>,
2161 <not
2161 <not
2162 <baseset [0, 1]>>>
2162 <baseset [0, 1]>>>
2163 2
2163 2
2164
2164
2165 $ try -p optimized '2:0 & not (0:2 & (0 + 1))'
2165 $ try -p optimized '2:0 & not (0:2 & (0 + 1))'
2166 * optimized:
2166 * optimized:
2167 (difference
2167 (difference
2168 (range
2168 (range
2169 ('symbol', '2')
2169 ('symbol', '2')
2170 ('symbol', '0'))
2170 ('symbol', '0'))
2171 (and
2171 (and
2172 (range
2172 (range
2173 ('symbol', '0')
2173 ('symbol', '0')
2174 ('symbol', '2'))
2174 ('symbol', '2'))
2175 (func
2175 (func
2176 ('symbol', '_list')
2176 ('symbol', '_list')
2177 ('string', '0\x001'))))
2177 ('string', '0\x001'))))
2178 * set:
2178 * set:
2179 <filteredset
2179 <filteredset
2180 <spanset- 0:3>,
2180 <spanset- 0:3>,
2181 <not
2181 <not
2182 <baseset [0, 1]>>>
2182 <baseset [0, 1]>>>
2183 2
2183 2
2184
2184
2185 because 'present()' does nothing other than suppressing an error, the
2185 because 'present()' does nothing other than suppressing an error, the
2186 ordering requirement should be forwarded to the nested expression
2186 ordering requirement should be forwarded to the nested expression
2187
2187
2188 $ try -p optimized 'present(2 + 0 + 1)'
2188 $ try -p optimized 'present(2 + 0 + 1)'
2189 * optimized:
2189 * optimized:
2190 (func
2190 (func
2191 ('symbol', 'present')
2191 ('symbol', 'present')
2192 (func
2192 (func
2193 ('symbol', '_list')
2193 ('symbol', '_list')
2194 ('string', '2\x000\x001')))
2194 ('string', '2\x000\x001')))
2195 * set:
2195 * set:
2196 <baseset [2, 0, 1]>
2196 <baseset [2, 0, 1]>
2197 2
2197 2
2198 0
2198 0
2199 1
2199 1
2200
2200
2201 $ try --optimize '2:0 & present(0 + 1 + 2)'
2201 $ try --optimize '2:0 & present(0 + 1 + 2)'
2202 (and
2202 (and
2203 (range
2203 (range
2204 ('symbol', '2')
2204 ('symbol', '2')
2205 ('symbol', '0'))
2205 ('symbol', '0'))
2206 (func
2206 (func
2207 ('symbol', 'present')
2207 ('symbol', 'present')
2208 (or
2208 (or
2209 (list
2209 (list
2210 ('symbol', '0')
2210 ('symbol', '0')
2211 ('symbol', '1')
2211 ('symbol', '1')
2212 ('symbol', '2')))))
2212 ('symbol', '2')))))
2213 * optimized:
2213 * optimized:
2214 (and
2214 (and
2215 (range
2215 (range
2216 ('symbol', '2')
2216 ('symbol', '2')
2217 ('symbol', '0'))
2217 ('symbol', '0'))
2218 (func
2218 (func
2219 ('symbol', 'present')
2219 ('symbol', 'present')
2220 (func
2220 (func
2221 ('symbol', '_list')
2221 ('symbol', '_list')
2222 ('string', '0\x001\x002'))))
2222 ('string', '0\x001\x002'))))
2223 * set:
2223 * set:
2224 <filteredset
2224 <filteredset
2225 <spanset- 0:3>,
2225 <spanset- 0:3>,
2226 <baseset [0, 1, 2]>>
2226 <baseset [0, 1, 2]>>
2227 2
2227 2
2228 1
2228 1
2229 0
2229 0
2230
2230
2231 'reverse()' should take effect only if it is the outermost expression:
2231 'reverse()' should take effect only if it is the outermost expression:
2232
2232
2233 $ try --optimize '0:2 & reverse(all())'
2233 $ try --optimize '0:2 & reverse(all())'
2234 (and
2234 (and
2235 (range
2235 (range
2236 ('symbol', '0')
2236 ('symbol', '0')
2237 ('symbol', '2'))
2237 ('symbol', '2'))
2238 (func
2238 (func
2239 ('symbol', 'reverse')
2239 ('symbol', 'reverse')
2240 (func
2240 (func
2241 ('symbol', 'all')
2241 ('symbol', 'all')
2242 None)))
2242 None)))
2243 * optimized:
2243 * optimized:
2244 (and
2244 (and
2245 (range
2245 (range
2246 ('symbol', '0')
2246 ('symbol', '0')
2247 ('symbol', '2'))
2247 ('symbol', '2'))
2248 (func
2248 (func
2249 ('symbol', 'reverse')
2249 ('symbol', 'reverse')
2250 (func
2250 (func
2251 ('symbol', 'all')
2251 ('symbol', 'all')
2252 None)))
2252 None)))
2253 * set:
2253 * set:
2254 <filteredset
2254 <filteredset
2255 <spanset+ 0:3>,
2255 <spanset+ 0:3>,
2256 <spanset+ 0:10>>
2256 <spanset+ 0:10>>
2257 0
2257 0
2258 1
2258 1
2259 2
2259 2
2260
2260
2261 'sort()' should take effect only if it is the outermost expression:
2261 'sort()' should take effect only if it is the outermost expression:
2262
2262
2263 $ try --optimize '0:2 & sort(all(), -rev)'
2263 $ try --optimize '0:2 & sort(all(), -rev)'
2264 (and
2264 (and
2265 (range
2265 (range
2266 ('symbol', '0')
2266 ('symbol', '0')
2267 ('symbol', '2'))
2267 ('symbol', '2'))
2268 (func
2268 (func
2269 ('symbol', 'sort')
2269 ('symbol', 'sort')
2270 (list
2270 (list
2271 (func
2271 (func
2272 ('symbol', 'all')
2272 ('symbol', 'all')
2273 None)
2273 None)
2274 (negate
2274 (negate
2275 ('symbol', 'rev')))))
2275 ('symbol', 'rev')))))
2276 * optimized:
2276 * optimized:
2277 (and
2277 (and
2278 (range
2278 (range
2279 ('symbol', '0')
2279 ('symbol', '0')
2280 ('symbol', '2'))
2280 ('symbol', '2'))
2281 (func
2281 (func
2282 ('symbol', 'sort')
2282 ('symbol', 'sort')
2283 (list
2283 (list
2284 (func
2284 (func
2285 ('symbol', 'all')
2285 ('symbol', 'all')
2286 None)
2286 None)
2287 ('string', '-rev'))))
2287 ('string', '-rev'))))
2288 * set:
2288 * set:
2289 <filteredset
2289 <filteredset
2290 <spanset+ 0:3>,
2290 <spanset+ 0:3>,
2291 <spanset+ 0:10>>
2291 <spanset+ 0:10>>
2292 0
2292 0
2293 1
2293 1
2294 2
2294 2
2295
2295
2296 invalid argument passed to noop sort():
2296 invalid argument passed to noop sort():
2297
2297
2298 $ log '0:2 & sort()'
2298 $ log '0:2 & sort()'
2299 hg: parse error: sort requires one or two arguments
2299 hg: parse error: sort requires one or two arguments
2300 [255]
2300 [255]
2301 $ log '0:2 & sort(all(), -invalid)'
2301 $ log '0:2 & sort(all(), -invalid)'
2302 hg: parse error: unknown sort key '-invalid'
2302 hg: parse error: unknown sort key '-invalid'
2303 [255]
2303 [255]
2304
2304
2305 for 'A & f(B)', 'B' should not be affected by the order of 'A':
2305 for 'A & f(B)', 'B' should not be affected by the order of 'A':
2306
2306
2307 $ try --optimize '2:0 & first(1 + 0 + 2)'
2307 $ try --optimize '2:0 & first(1 + 0 + 2)'
2308 (and
2308 (and
2309 (range
2309 (range
2310 ('symbol', '2')
2310 ('symbol', '2')
2311 ('symbol', '0'))
2311 ('symbol', '0'))
2312 (func
2312 (func
2313 ('symbol', 'first')
2313 ('symbol', 'first')
2314 (or
2314 (or
2315 (list
2315 (list
2316 ('symbol', '1')
2316 ('symbol', '1')
2317 ('symbol', '0')
2317 ('symbol', '0')
2318 ('symbol', '2')))))
2318 ('symbol', '2')))))
2319 * optimized:
2319 * optimized:
2320 (and
2320 (and
2321 (range
2321 (range
2322 ('symbol', '2')
2322 ('symbol', '2')
2323 ('symbol', '0'))
2323 ('symbol', '0'))
2324 (func
2324 (func
2325 ('symbol', 'first')
2325 ('symbol', 'first')
2326 (func
2326 (func
2327 ('symbol', '_list')
2327 ('symbol', '_list')
2328 ('string', '1\x000\x002'))))
2328 ('string', '1\x000\x002'))))
2329 * set:
2329 * set:
2330 <filteredset
2330 <filteredset
2331 <baseset [1]>,
2331 <baseset [1]>,
2332 <spanset- 0:3>>
2332 <spanset- 0:3>>
2333 1
2333 1
2334
2334
2335 $ try --optimize '2:0 & not last(0 + 2 + 1)'
2335 $ try --optimize '2:0 & not last(0 + 2 + 1)'
2336 (and
2336 (and
2337 (range
2337 (range
2338 ('symbol', '2')
2338 ('symbol', '2')
2339 ('symbol', '0'))
2339 ('symbol', '0'))
2340 (not
2340 (not
2341 (func
2341 (func
2342 ('symbol', 'last')
2342 ('symbol', 'last')
2343 (or
2343 (or
2344 (list
2344 (list
2345 ('symbol', '0')
2345 ('symbol', '0')
2346 ('symbol', '2')
2346 ('symbol', '2')
2347 ('symbol', '1'))))))
2347 ('symbol', '1'))))))
2348 * optimized:
2348 * optimized:
2349 (difference
2349 (difference
2350 (range
2350 (range
2351 ('symbol', '2')
2351 ('symbol', '2')
2352 ('symbol', '0'))
2352 ('symbol', '0'))
2353 (func
2353 (func
2354 ('symbol', 'last')
2354 ('symbol', 'last')
2355 (func
2355 (func
2356 ('symbol', '_list')
2356 ('symbol', '_list')
2357 ('string', '0\x002\x001'))))
2357 ('string', '0\x002\x001'))))
2358 * set:
2358 * set:
2359 <filteredset
2359 <filteredset
2360 <spanset- 0:3>,
2360 <spanset- 0:3>,
2361 <not
2361 <not
2362 <baseset [1]>>>
2362 <baseset [1]>>>
2363 2
2363 2
2364 0
2364 0
2365
2365
2366 for 'A & (op)(B)', 'B' should not be affected by the order of 'A':
2366 for 'A & (op)(B)', 'B' should not be affected by the order of 'A':
2367
2367
2368 $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)'
2368 $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)'
2369 (and
2369 (and
2370 (range
2370 (range
2371 ('symbol', '2')
2371 ('symbol', '2')
2372 ('symbol', '0'))
2372 ('symbol', '0'))
2373 (range
2373 (range
2374 (group
2374 (group
2375 (or
2375 (or
2376 (list
2376 (list
2377 ('symbol', '1')
2377 ('symbol', '1')
2378 ('symbol', '0')
2378 ('symbol', '0')
2379 ('symbol', '2'))))
2379 ('symbol', '2'))))
2380 (group
2380 (group
2381 (or
2381 (or
2382 (list
2382 (list
2383 ('symbol', '0')
2383 ('symbol', '0')
2384 ('symbol', '2')
2384 ('symbol', '2')
2385 ('symbol', '1'))))))
2385 ('symbol', '1'))))))
2386 * optimized:
2386 * optimized:
2387 (and
2387 (and
2388 (range
2388 (range
2389 ('symbol', '2')
2389 ('symbol', '2')
2390 ('symbol', '0'))
2390 ('symbol', '0'))
2391 (range
2391 (range
2392 (func
2392 (func
2393 ('symbol', '_list')
2393 ('symbol', '_list')
2394 ('string', '1\x000\x002'))
2394 ('string', '1\x000\x002'))
2395 (func
2395 (func
2396 ('symbol', '_list')
2396 ('symbol', '_list')
2397 ('string', '0\x002\x001'))))
2397 ('string', '0\x002\x001'))))
2398 * set:
2398 * set:
2399 <filteredset
2399 <filteredset
2400 <spanset- 0:3>,
2400 <spanset- 0:3>,
2401 <baseset [1]>>
2401 <baseset [1]>>
2402 1
2402 1
2403
2403
2404 'A & B' can be rewritten as 'flipand(B, A)' by weight.
2404 'A & B' can be rewritten as 'flipand(B, A)' by weight.
2405
2405
2406 $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
2406 $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
2407 (and
2407 (and
2408 (func
2408 (func
2409 ('symbol', 'contains')
2409 ('symbol', 'contains')
2410 ('string', 'glob:*'))
2410 ('string', 'glob:*'))
2411 (group
2411 (group
2412 (or
2412 (or
2413 (list
2413 (list
2414 ('symbol', '2')
2414 ('symbol', '2')
2415 ('symbol', '0')
2415 ('symbol', '0')
2416 ('symbol', '1')))))
2416 ('symbol', '1')))))
2417 * optimized:
2417 * optimized:
2418 (andsmally
2418 (andsmally
2419 (func
2419 (func
2420 ('symbol', 'contains')
2420 ('symbol', 'contains')
2421 ('string', 'glob:*'))
2421 ('string', 'glob:*'))
2422 (func
2422 (func
2423 ('symbol', '_list')
2423 ('symbol', '_list')
2424 ('string', '2\x000\x001')))
2424 ('string', '2\x000\x001')))
2425 * set:
2425 * set:
2426 <filteredset
2426 <filteredset
2427 <baseset+ [0, 1, 2]>,
2427 <baseset+ [0, 1, 2]>,
2428 <contains 'glob:*'>>
2428 <contains 'glob:*'>>
2429 0
2429 0
2430 1
2430 1
2431 2
2431 2
2432
2432
2433 and in this example, 'A & B' is rewritten as 'B & A', but 'A' overrides
2433 and in this example, 'A & B' is rewritten as 'B & A', but 'A' overrides
2434 the order appropriately:
2434 the order appropriately:
2435
2435
2436 $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)'
2436 $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)'
2437 (and
2437 (and
2438 (func
2438 (func
2439 ('symbol', 'reverse')
2439 ('symbol', 'reverse')
2440 (func
2440 (func
2441 ('symbol', 'contains')
2441 ('symbol', 'contains')
2442 ('string', 'glob:*')))
2442 ('string', 'glob:*')))
2443 (group
2443 (group
2444 (or
2444 (or
2445 (list
2445 (list
2446 ('symbol', '0')
2446 ('symbol', '0')
2447 ('symbol', '2')
2447 ('symbol', '2')
2448 ('symbol', '1')))))
2448 ('symbol', '1')))))
2449 * optimized:
2449 * optimized:
2450 (andsmally
2450 (andsmally
2451 (func
2451 (func
2452 ('symbol', 'reverse')
2452 ('symbol', 'reverse')
2453 (func
2453 (func
2454 ('symbol', 'contains')
2454 ('symbol', 'contains')
2455 ('string', 'glob:*')))
2455 ('string', 'glob:*')))
2456 (func
2456 (func
2457 ('symbol', '_list')
2457 ('symbol', '_list')
2458 ('string', '0\x002\x001')))
2458 ('string', '0\x002\x001')))
2459 * set:
2459 * set:
2460 <filteredset
2460 <filteredset
2461 <baseset- [0, 1, 2]>,
2461 <baseset- [0, 1, 2]>,
2462 <contains 'glob:*'>>
2462 <contains 'glob:*'>>
2463 2
2463 2
2464 1
2464 1
2465 0
2465 0
2466
2466
2467 test sort revset
2467 test sort revset
2468 --------------------------------------------
2468 --------------------------------------------
2469
2469
2470 test when adding two unordered revsets
2470 test when adding two unordered revsets
2471
2471
2472 $ log 'sort(keyword(issue) or modifies(b))'
2472 $ log 'sort(keyword(issue) or modifies(b))'
2473 4
2473 4
2474 6
2474 6
2475
2475
2476 test when sorting a reversed collection in the same way it is
2476 test when sorting a reversed collection in the same way it is
2477
2477
2478 $ log 'sort(reverse(all()), -rev)'
2478 $ log 'sort(reverse(all()), -rev)'
2479 9
2479 9
2480 8
2480 8
2481 7
2481 7
2482 6
2482 6
2483 5
2483 5
2484 4
2484 4
2485 3
2485 3
2486 2
2486 2
2487 1
2487 1
2488 0
2488 0
2489
2489
2490 test when sorting a reversed collection
2490 test when sorting a reversed collection
2491
2491
2492 $ log 'sort(reverse(all()), rev)'
2492 $ log 'sort(reverse(all()), rev)'
2493 0
2493 0
2494 1
2494 1
2495 2
2495 2
2496 3
2496 3
2497 4
2497 4
2498 5
2498 5
2499 6
2499 6
2500 7
2500 7
2501 8
2501 8
2502 9
2502 9
2503
2503
2504
2504
2505 test sorting two sorted collections in different orders
2505 test sorting two sorted collections in different orders
2506
2506
2507 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
2507 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
2508 2
2508 2
2509 6
2509 6
2510 8
2510 8
2511 9
2511 9
2512
2512
2513 test sorting two sorted collections in different orders backwards
2513 test sorting two sorted collections in different orders backwards
2514
2514
2515 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
2515 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
2516 9
2516 9
2517 8
2517 8
2518 6
2518 6
2519 2
2519 2
2520
2520
2521 test empty sort key which is noop
2521 test empty sort key which is noop
2522
2522
2523 $ log 'sort(0 + 2 + 1, "")'
2523 $ log 'sort(0 + 2 + 1, "")'
2524 0
2524 0
2525 2
2525 2
2526 1
2526 1
2527
2527
2528 test invalid sort keys
2528 test invalid sort keys
2529
2529
2530 $ log 'sort(all(), -invalid)'
2530 $ log 'sort(all(), -invalid)'
2531 hg: parse error: unknown sort key '-invalid'
2531 hg: parse error: unknown sort key '-invalid'
2532 [255]
2532 [255]
2533
2533
2534 $ cd ..
2534 $ cd ..
2535
2535
2536 test sorting by multiple keys including variable-length strings
2536 test sorting by multiple keys including variable-length strings
2537
2537
2538 $ hg init sorting
2538 $ hg init sorting
2539 $ cd sorting
2539 $ cd sorting
2540 $ cat <<EOF >> .hg/hgrc
2540 $ cat <<EOF >> .hg/hgrc
2541 > [ui]
2541 > [ui]
2542 > logtemplate = '{rev} {branch|p5}{desc|p5}{author|p5}{date|hgdate}\n'
2542 > logtemplate = '{rev} {branch|p5}{desc|p5}{author|p5}{date|hgdate}\n'
2543 > [templatealias]
2543 > [templatealias]
2544 > p5(s) = pad(s, 5)
2544 > p5(s) = pad(s, 5)
2545 > EOF
2545 > EOF
2546 $ hg branch -qf b12
2546 $ hg branch -qf b12
2547 $ hg ci -m m111 -u u112 -d '111 10800'
2547 $ hg ci -m m111 -u u112 -d '111 10800'
2548 $ hg branch -qf b11
2548 $ hg branch -qf b11
2549 $ hg ci -m m12 -u u111 -d '112 7200'
2549 $ hg ci -m m12 -u u111 -d '112 7200'
2550 $ hg branch -qf b111
2550 $ hg branch -qf b111
2551 $ hg ci -m m11 -u u12 -d '111 3600'
2551 $ hg ci -m m11 -u u12 -d '111 3600'
2552 $ hg branch -qf b112
2552 $ hg branch -qf b112
2553 $ hg ci -m m111 -u u11 -d '120 0'
2553 $ hg ci -m m111 -u u11 -d '120 0'
2554 $ hg branch -qf b111
2554 $ hg branch -qf b111
2555 $ hg ci -m m112 -u u111 -d '110 14400'
2555 $ hg ci -m m112 -u u111 -d '110 14400'
2556 created new head
2556 created new head
2557
2557
2558 compare revisions (has fast path):
2558 compare revisions (has fast path):
2559
2559
2560 $ hg log -r 'sort(all(), rev)'
2560 $ hg log -r 'sort(all(), rev)'
2561 0 b12 m111 u112 111 10800
2561 0 b12 m111 u112 111 10800
2562 1 b11 m12 u111 112 7200
2562 1 b11 m12 u111 112 7200
2563 2 b111 m11 u12 111 3600
2563 2 b111 m11 u12 111 3600
2564 3 b112 m111 u11 120 0
2564 3 b112 m111 u11 120 0
2565 4 b111 m112 u111 110 14400
2565 4 b111 m112 u111 110 14400
2566
2566
2567 $ hg log -r 'sort(all(), -rev)'
2567 $ hg log -r 'sort(all(), -rev)'
2568 4 b111 m112 u111 110 14400
2568 4 b111 m112 u111 110 14400
2569 3 b112 m111 u11 120 0
2569 3 b112 m111 u11 120 0
2570 2 b111 m11 u12 111 3600
2570 2 b111 m11 u12 111 3600
2571 1 b11 m12 u111 112 7200
2571 1 b11 m12 u111 112 7200
2572 0 b12 m111 u112 111 10800
2572 0 b12 m111 u112 111 10800
2573
2573
2574 compare variable-length strings (issue5218):
2574 compare variable-length strings (issue5218):
2575
2575
2576 $ hg log -r 'sort(all(), branch)'
2576 $ hg log -r 'sort(all(), branch)'
2577 1 b11 m12 u111 112 7200
2577 1 b11 m12 u111 112 7200
2578 2 b111 m11 u12 111 3600
2578 2 b111 m11 u12 111 3600
2579 4 b111 m112 u111 110 14400
2579 4 b111 m112 u111 110 14400
2580 3 b112 m111 u11 120 0
2580 3 b112 m111 u11 120 0
2581 0 b12 m111 u112 111 10800
2581 0 b12 m111 u112 111 10800
2582
2582
2583 $ hg log -r 'sort(all(), -branch)'
2583 $ hg log -r 'sort(all(), -branch)'
2584 0 b12 m111 u112 111 10800
2584 0 b12 m111 u112 111 10800
2585 3 b112 m111 u11 120 0
2585 3 b112 m111 u11 120 0
2586 2 b111 m11 u12 111 3600
2586 2 b111 m11 u12 111 3600
2587 4 b111 m112 u111 110 14400
2587 4 b111 m112 u111 110 14400
2588 1 b11 m12 u111 112 7200
2588 1 b11 m12 u111 112 7200
2589
2589
2590 $ hg log -r 'sort(all(), desc)'
2590 $ hg log -r 'sort(all(), desc)'
2591 2 b111 m11 u12 111 3600
2591 2 b111 m11 u12 111 3600
2592 0 b12 m111 u112 111 10800
2592 0 b12 m111 u112 111 10800
2593 3 b112 m111 u11 120 0
2593 3 b112 m111 u11 120 0
2594 4 b111 m112 u111 110 14400
2594 4 b111 m112 u111 110 14400
2595 1 b11 m12 u111 112 7200
2595 1 b11 m12 u111 112 7200
2596
2596
2597 $ hg log -r 'sort(all(), -desc)'
2597 $ hg log -r 'sort(all(), -desc)'
2598 1 b11 m12 u111 112 7200
2598 1 b11 m12 u111 112 7200
2599 4 b111 m112 u111 110 14400
2599 4 b111 m112 u111 110 14400
2600 0 b12 m111 u112 111 10800
2600 0 b12 m111 u112 111 10800
2601 3 b112 m111 u11 120 0
2601 3 b112 m111 u11 120 0
2602 2 b111 m11 u12 111 3600
2602 2 b111 m11 u12 111 3600
2603
2603
2604 $ hg log -r 'sort(all(), user)'
2604 $ hg log -r 'sort(all(), user)'
2605 3 b112 m111 u11 120 0
2605 3 b112 m111 u11 120 0
2606 1 b11 m12 u111 112 7200
2606 1 b11 m12 u111 112 7200
2607 4 b111 m112 u111 110 14400
2607 4 b111 m112 u111 110 14400
2608 0 b12 m111 u112 111 10800
2608 0 b12 m111 u112 111 10800
2609 2 b111 m11 u12 111 3600
2609 2 b111 m11 u12 111 3600
2610
2610
2611 $ hg log -r 'sort(all(), -user)'
2611 $ hg log -r 'sort(all(), -user)'
2612 2 b111 m11 u12 111 3600
2612 2 b111 m11 u12 111 3600
2613 0 b12 m111 u112 111 10800
2613 0 b12 m111 u112 111 10800
2614 1 b11 m12 u111 112 7200
2614 1 b11 m12 u111 112 7200
2615 4 b111 m112 u111 110 14400
2615 4 b111 m112 u111 110 14400
2616 3 b112 m111 u11 120 0
2616 3 b112 m111 u11 120 0
2617
2617
2618 compare dates (tz offset should have no effect):
2618 compare dates (tz offset should have no effect):
2619
2619
2620 $ hg log -r 'sort(all(), date)'
2620 $ hg log -r 'sort(all(), date)'
2621 4 b111 m112 u111 110 14400
2621 4 b111 m112 u111 110 14400
2622 0 b12 m111 u112 111 10800
2622 0 b12 m111 u112 111 10800
2623 2 b111 m11 u12 111 3600
2623 2 b111 m11 u12 111 3600
2624 1 b11 m12 u111 112 7200
2624 1 b11 m12 u111 112 7200
2625 3 b112 m111 u11 120 0
2625 3 b112 m111 u11 120 0
2626
2626
2627 $ hg log -r 'sort(all(), -date)'
2627 $ hg log -r 'sort(all(), -date)'
2628 3 b112 m111 u11 120 0
2628 3 b112 m111 u11 120 0
2629 1 b11 m12 u111 112 7200
2629 1 b11 m12 u111 112 7200
2630 0 b12 m111 u112 111 10800
2630 0 b12 m111 u112 111 10800
2631 2 b111 m11 u12 111 3600
2631 2 b111 m11 u12 111 3600
2632 4 b111 m112 u111 110 14400
2632 4 b111 m112 u111 110 14400
2633
2633
2634 be aware that 'sort(x, -k)' is not exactly the same as 'reverse(sort(x, k))'
2634 be aware that 'sort(x, -k)' is not exactly the same as 'reverse(sort(x, k))'
2635 because '-k' reverses the comparison, not the list itself:
2635 because '-k' reverses the comparison, not the list itself:
2636
2636
2637 $ hg log -r 'sort(0 + 2, date)'
2637 $ hg log -r 'sort(0 + 2, date)'
2638 0 b12 m111 u112 111 10800
2638 0 b12 m111 u112 111 10800
2639 2 b111 m11 u12 111 3600
2639 2 b111 m11 u12 111 3600
2640
2640
2641 $ hg log -r 'sort(0 + 2, -date)'
2641 $ hg log -r 'sort(0 + 2, -date)'
2642 0 b12 m111 u112 111 10800
2642 0 b12 m111 u112 111 10800
2643 2 b111 m11 u12 111 3600
2643 2 b111 m11 u12 111 3600
2644
2644
2645 $ hg log -r 'reverse(sort(0 + 2, date))'
2645 $ hg log -r 'reverse(sort(0 + 2, date))'
2646 2 b111 m11 u12 111 3600
2646 2 b111 m11 u12 111 3600
2647 0 b12 m111 u112 111 10800
2647 0 b12 m111 u112 111 10800
2648
2648
2649 sort by multiple keys:
2649 sort by multiple keys:
2650
2650
2651 $ hg log -r 'sort(all(), "branch -rev")'
2651 $ hg log -r 'sort(all(), "branch -rev")'
2652 1 b11 m12 u111 112 7200
2652 1 b11 m12 u111 112 7200
2653 4 b111 m112 u111 110 14400
2653 4 b111 m112 u111 110 14400
2654 2 b111 m11 u12 111 3600
2654 2 b111 m11 u12 111 3600
2655 3 b112 m111 u11 120 0
2655 3 b112 m111 u11 120 0
2656 0 b12 m111 u112 111 10800
2656 0 b12 m111 u112 111 10800
2657
2657
2658 $ hg log -r 'sort(all(), "-desc -date")'
2658 $ hg log -r 'sort(all(), "-desc -date")'
2659 1 b11 m12 u111 112 7200
2659 1 b11 m12 u111 112 7200
2660 4 b111 m112 u111 110 14400
2660 4 b111 m112 u111 110 14400
2661 3 b112 m111 u11 120 0
2661 3 b112 m111 u11 120 0
2662 0 b12 m111 u112 111 10800
2662 0 b12 m111 u112 111 10800
2663 2 b111 m11 u12 111 3600
2663 2 b111 m11 u12 111 3600
2664
2664
2665 $ hg log -r 'sort(all(), "user -branch date rev")'
2665 $ hg log -r 'sort(all(), "user -branch date rev")'
2666 3 b112 m111 u11 120 0
2666 3 b112 m111 u11 120 0
2667 4 b111 m112 u111 110 14400
2667 4 b111 m112 u111 110 14400
2668 1 b11 m12 u111 112 7200
2668 1 b11 m12 u111 112 7200
2669 0 b12 m111 u112 111 10800
2669 0 b12 m111 u112 111 10800
2670 2 b111 m11 u12 111 3600
2670 2 b111 m11 u12 111 3600
2671
2671
2672 toposort prioritises graph branches
2672 toposort prioritises graph branches
2673
2673
2674 $ hg up 2
2674 $ hg up 2
2675 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2675 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2676 $ touch a
2676 $ touch a
2677 $ hg addremove
2677 $ hg addremove
2678 adding a
2678 adding a
2679 $ hg ci -m 't1' -u 'tu' -d '130 0'
2679 $ hg ci -m 't1' -u 'tu' -d '130 0'
2680 created new head
2680 created new head
2681 $ echo 'a' >> a
2681 $ echo 'a' >> a
2682 $ hg ci -m 't2' -u 'tu' -d '130 0'
2682 $ hg ci -m 't2' -u 'tu' -d '130 0'
2683 $ hg book book1
2683 $ hg book book1
2684 $ hg up 4
2684 $ hg up 4
2685 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2685 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2686 (leaving bookmark book1)
2686 (leaving bookmark book1)
2687 $ touch a
2687 $ touch a
2688 $ hg addremove
2688 $ hg addremove
2689 adding a
2689 adding a
2690 $ hg ci -m 't3' -u 'tu' -d '130 0'
2690 $ hg ci -m 't3' -u 'tu' -d '130 0'
2691
2691
2692 $ hg log -r 'sort(all(), topo)'
2692 $ hg log -r 'sort(all(), topo)'
2693 7 b111 t3 tu 130 0
2693 7 b111 t3 tu 130 0
2694 4 b111 m112 u111 110 14400
2694 4 b111 m112 u111 110 14400
2695 3 b112 m111 u11 120 0
2695 3 b112 m111 u11 120 0
2696 6 b111 t2 tu 130 0
2696 6 b111 t2 tu 130 0
2697 5 b111 t1 tu 130 0
2697 5 b111 t1 tu 130 0
2698 2 b111 m11 u12 111 3600
2698 2 b111 m11 u12 111 3600
2699 1 b11 m12 u111 112 7200
2699 1 b11 m12 u111 112 7200
2700 0 b12 m111 u112 111 10800
2700 0 b12 m111 u112 111 10800
2701
2701
2702 $ hg log -r 'sort(all(), -topo)'
2702 $ hg log -r 'sort(all(), -topo)'
2703 0 b12 m111 u112 111 10800
2703 0 b12 m111 u112 111 10800
2704 1 b11 m12 u111 112 7200
2704 1 b11 m12 u111 112 7200
2705 2 b111 m11 u12 111 3600
2705 2 b111 m11 u12 111 3600
2706 5 b111 t1 tu 130 0
2706 5 b111 t1 tu 130 0
2707 6 b111 t2 tu 130 0
2707 6 b111 t2 tu 130 0
2708 3 b112 m111 u11 120 0
2708 3 b112 m111 u11 120 0
2709 4 b111 m112 u111 110 14400
2709 4 b111 m112 u111 110 14400
2710 7 b111 t3 tu 130 0
2710 7 b111 t3 tu 130 0
2711
2711
2712 $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)'
2712 $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)'
2713 6 b111 t2 tu 130 0
2713 6 b111 t2 tu 130 0
2714 5 b111 t1 tu 130 0
2714 5 b111 t1 tu 130 0
2715 7 b111 t3 tu 130 0
2715 7 b111 t3 tu 130 0
2716 4 b111 m112 u111 110 14400
2716 4 b111 m112 u111 110 14400
2717 3 b112 m111 u11 120 0
2717 3 b112 m111 u11 120 0
2718 2 b111 m11 u12 111 3600
2718 2 b111 m11 u12 111 3600
2719 1 b11 m12 u111 112 7200
2719 1 b11 m12 u111 112 7200
2720 0 b12 m111 u112 111 10800
2720 0 b12 m111 u112 111 10800
2721
2721
2722 topographical sorting can't be combined with other sort keys, and you can't
2722 topographical sorting can't be combined with other sort keys, and you can't
2723 use the topo.firstbranch option when topo sort is not active:
2723 use the topo.firstbranch option when topo sort is not active:
2724
2724
2725 $ hg log -r 'sort(all(), "topo user")'
2725 $ hg log -r 'sort(all(), "topo user")'
2726 hg: parse error: topo sort order cannot be combined with other sort keys
2726 hg: parse error: topo sort order cannot be combined with other sort keys
2727 [255]
2727 [255]
2728
2728
2729 $ hg log -r 'sort(all(), user, topo.firstbranch=book1)'
2729 $ hg log -r 'sort(all(), user, topo.firstbranch=book1)'
2730 hg: parse error: topo.firstbranch can only be used when using the topo sort key
2730 hg: parse error: topo.firstbranch can only be used when using the topo sort key
2731 [255]
2731 [255]
2732
2732
2733 topo.firstbranch should accept any kind of expressions:
2733 topo.firstbranch should accept any kind of expressions:
2734
2734
2735 $ hg log -r 'sort(0, topo, topo.firstbranch=(book1))'
2735 $ hg log -r 'sort(0, topo, topo.firstbranch=(book1))'
2736 0 b12 m111 u112 111 10800
2736 0 b12 m111 u112 111 10800
2737
2737
2738 $ cd ..
2738 $ cd ..
2739 $ cd repo
2739 $ cd repo
2740
2740
2741 test subtracting something from an addset
2741 test subtracting something from an addset
2742
2742
2743 $ log '(outgoing() or removes(a)) - removes(a)'
2743 $ log '(outgoing() or removes(a)) - removes(a)'
2744 8
2744 8
2745 9
2745 9
2746
2746
2747 test intersecting something with an addset
2747 test intersecting something with an addset
2748
2748
2749 $ log 'parents(outgoing() or removes(a))'
2749 $ log 'parents(outgoing() or removes(a))'
2750 1
2750 1
2751 4
2751 4
2752 5
2752 5
2753 8
2753 8
2754
2754
2755 test that `or` operation combines elements in the right order:
2755 test that `or` operation combines elements in the right order:
2756
2756
2757 $ log '3:4 or 2:5'
2757 $ log '3:4 or 2:5'
2758 3
2758 3
2759 4
2759 4
2760 2
2760 2
2761 5
2761 5
2762 $ log '3:4 or 5:2'
2762 $ log '3:4 or 5:2'
2763 3
2763 3
2764 4
2764 4
2765 5
2765 5
2766 2
2766 2
2767 $ log 'sort(3:4 or 2:5)'
2767 $ log 'sort(3:4 or 2:5)'
2768 2
2768 2
2769 3
2769 3
2770 4
2770 4
2771 5
2771 5
2772 $ log 'sort(3:4 or 5:2)'
2772 $ log 'sort(3:4 or 5:2)'
2773 2
2773 2
2774 3
2774 3
2775 4
2775 4
2776 5
2776 5
2777
2777
2778 test that more than one `-r`s are combined in the right order and deduplicated:
2778 test that more than one `-r`s are combined in the right order and deduplicated:
2779
2779
2780 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
2780 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
2781 3
2781 3
2782 4
2782 4
2783 5
2783 5
2784 2
2784 2
2785 0
2785 0
2786 1
2786 1
2787
2787
2788 test that `or` operation skips duplicated revisions from right-hand side
2788 test that `or` operation skips duplicated revisions from right-hand side
2789
2789
2790 $ try 'reverse(1::5) or ancestors(4)'
2790 $ try 'reverse(1::5) or ancestors(4)'
2791 (or
2791 (or
2792 (list
2792 (list
2793 (func
2793 (func
2794 ('symbol', 'reverse')
2794 ('symbol', 'reverse')
2795 (dagrange
2795 (dagrange
2796 ('symbol', '1')
2796 ('symbol', '1')
2797 ('symbol', '5')))
2797 ('symbol', '5')))
2798 (func
2798 (func
2799 ('symbol', 'ancestors')
2799 ('symbol', 'ancestors')
2800 ('symbol', '4'))))
2800 ('symbol', '4'))))
2801 * set:
2801 * set:
2802 <addset
2802 <addset
2803 <baseset- [1, 3, 5]>,
2803 <baseset- [1, 3, 5]>,
2804 <generatorset+>>
2804 <generatorset+>>
2805 5
2805 5
2806 3
2806 3
2807 1
2807 1
2808 0
2808 0
2809 2
2809 2
2810 4
2810 4
2811 $ try 'sort(ancestors(4) or reverse(1::5))'
2811 $ try 'sort(ancestors(4) or reverse(1::5))'
2812 (func
2812 (func
2813 ('symbol', 'sort')
2813 ('symbol', 'sort')
2814 (or
2814 (or
2815 (list
2815 (list
2816 (func
2816 (func
2817 ('symbol', 'ancestors')
2817 ('symbol', 'ancestors')
2818 ('symbol', '4'))
2818 ('symbol', '4'))
2819 (func
2819 (func
2820 ('symbol', 'reverse')
2820 ('symbol', 'reverse')
2821 (dagrange
2821 (dagrange
2822 ('symbol', '1')
2822 ('symbol', '1')
2823 ('symbol', '5'))))))
2823 ('symbol', '5'))))))
2824 * set:
2824 * set:
2825 <addset+
2825 <addset+
2826 <generatorset+>,
2826 <generatorset+>,
2827 <baseset- [1, 3, 5]>>
2827 <baseset- [1, 3, 5]>>
2828 0
2828 0
2829 1
2829 1
2830 2
2830 2
2831 3
2831 3
2832 4
2832 4
2833 5
2833 5
2834
2834
2835 test optimization of trivial `or` operation
2835 test optimization of trivial `or` operation
2836
2836
2837 $ try --optimize '0|(1)|"2"|-2|tip|null'
2837 $ try --optimize '0|(1)|"2"|-2|tip|null'
2838 (or
2838 (or
2839 (list
2839 (list
2840 ('symbol', '0')
2840 ('symbol', '0')
2841 (group
2841 (group
2842 ('symbol', '1'))
2842 ('symbol', '1'))
2843 ('string', '2')
2843 ('string', '2')
2844 (negate
2844 (negate
2845 ('symbol', '2'))
2845 ('symbol', '2'))
2846 ('symbol', 'tip')
2846 ('symbol', 'tip')
2847 ('symbol', 'null')))
2847 ('symbol', 'null')))
2848 * optimized:
2848 * optimized:
2849 (func
2849 (func
2850 ('symbol', '_list')
2850 ('symbol', '_list')
2851 ('string', '0\x001\x002\x00-2\x00tip\x00null'))
2851 ('string', '0\x001\x002\x00-2\x00tip\x00null'))
2852 * set:
2852 * set:
2853 <baseset [0, 1, 2, 8, 9, -1]>
2853 <baseset [0, 1, 2, 8, 9, -1]>
2854 0
2854 0
2855 1
2855 1
2856 2
2856 2
2857 8
2857 8
2858 9
2858 9
2859 -1
2859 -1
2860
2860
2861 $ try --optimize '0|1|2:3'
2861 $ try --optimize '0|1|2:3'
2862 (or
2862 (or
2863 (list
2863 (list
2864 ('symbol', '0')
2864 ('symbol', '0')
2865 ('symbol', '1')
2865 ('symbol', '1')
2866 (range
2866 (range
2867 ('symbol', '2')
2867 ('symbol', '2')
2868 ('symbol', '3'))))
2868 ('symbol', '3'))))
2869 * optimized:
2869 * optimized:
2870 (or
2870 (or
2871 (list
2871 (list
2872 (func
2872 (func
2873 ('symbol', '_list')
2873 ('symbol', '_list')
2874 ('string', '0\x001'))
2874 ('string', '0\x001'))
2875 (range
2875 (range
2876 ('symbol', '2')
2876 ('symbol', '2')
2877 ('symbol', '3'))))
2877 ('symbol', '3'))))
2878 * set:
2878 * set:
2879 <addset
2879 <addset
2880 <baseset [0, 1]>,
2880 <baseset [0, 1]>,
2881 <spanset+ 2:4>>
2881 <spanset+ 2:4>>
2882 0
2882 0
2883 1
2883 1
2884 2
2884 2
2885 3
2885 3
2886
2886
2887 $ try --optimize '0:1|2|3:4|5|6'
2887 $ try --optimize '0:1|2|3:4|5|6'
2888 (or
2888 (or
2889 (list
2889 (list
2890 (range
2890 (range
2891 ('symbol', '0')
2891 ('symbol', '0')
2892 ('symbol', '1'))
2892 ('symbol', '1'))
2893 ('symbol', '2')
2893 ('symbol', '2')
2894 (range
2894 (range
2895 ('symbol', '3')
2895 ('symbol', '3')
2896 ('symbol', '4'))
2896 ('symbol', '4'))
2897 ('symbol', '5')
2897 ('symbol', '5')
2898 ('symbol', '6')))
2898 ('symbol', '6')))
2899 * optimized:
2899 * optimized:
2900 (or
2900 (or
2901 (list
2901 (list
2902 (range
2902 (range
2903 ('symbol', '0')
2903 ('symbol', '0')
2904 ('symbol', '1'))
2904 ('symbol', '1'))
2905 ('symbol', '2')
2905 ('symbol', '2')
2906 (range
2906 (range
2907 ('symbol', '3')
2907 ('symbol', '3')
2908 ('symbol', '4'))
2908 ('symbol', '4'))
2909 (func
2909 (func
2910 ('symbol', '_list')
2910 ('symbol', '_list')
2911 ('string', '5\x006'))))
2911 ('string', '5\x006'))))
2912 * set:
2912 * set:
2913 <addset
2913 <addset
2914 <addset
2914 <addset
2915 <spanset+ 0:2>,
2915 <spanset+ 0:2>,
2916 <baseset [2]>>,
2916 <baseset [2]>>,
2917 <addset
2917 <addset
2918 <spanset+ 3:5>,
2918 <spanset+ 3:5>,
2919 <baseset [5, 6]>>>
2919 <baseset [5, 6]>>>
2920 0
2920 0
2921 1
2921 1
2922 2
2922 2
2923 3
2923 3
2924 4
2924 4
2925 5
2925 5
2926 6
2926 6
2927
2927
2928 unoptimized `or` looks like this
2928 unoptimized `or` looks like this
2929
2929
2930 $ try --no-optimized -p analyzed '0|1|2|3|4'
2930 $ try --no-optimized -p analyzed '0|1|2|3|4'
2931 * analyzed:
2931 * analyzed:
2932 (or
2932 (or
2933 (list
2933 (list
2934 ('symbol', '0')
2934 ('symbol', '0')
2935 ('symbol', '1')
2935 ('symbol', '1')
2936 ('symbol', '2')
2936 ('symbol', '2')
2937 ('symbol', '3')
2937 ('symbol', '3')
2938 ('symbol', '4')))
2938 ('symbol', '4')))
2939 * set:
2939 * set:
2940 <addset
2940 <addset
2941 <addset
2941 <addset
2942 <baseset [0]>,
2942 <baseset [0]>,
2943 <baseset [1]>>,
2943 <baseset [1]>>,
2944 <addset
2944 <addset
2945 <baseset [2]>,
2945 <baseset [2]>,
2946 <addset
2946 <addset
2947 <baseset [3]>,
2947 <baseset [3]>,
2948 <baseset [4]>>>>
2948 <baseset [4]>>>>
2949 0
2949 0
2950 1
2950 1
2951 2
2951 2
2952 3
2952 3
2953 4
2953 4
2954
2954
2955 test that `_list` should be narrowed by provided `subset`
2955 test that `_list` should be narrowed by provided `subset`
2956
2956
2957 $ log '0:2 and (null|1|2|3)'
2957 $ log '0:2 and (null|1|2|3)'
2958 1
2958 1
2959 2
2959 2
2960
2960
2961 test that `_list` should remove duplicates
2961 test that `_list` should remove duplicates
2962
2962
2963 $ log '0|1|2|1|2|-1|tip'
2963 $ log '0|1|2|1|2|-1|tip'
2964 0
2964 0
2965 1
2965 1
2966 2
2966 2
2967 9
2967 9
2968
2968
2969 test unknown revision in `_list`
2969 test unknown revision in `_list`
2970
2970
2971 $ log '0|unknown'
2971 $ log '0|unknown'
2972 abort: unknown revision 'unknown'!
2972 abort: unknown revision 'unknown'!
2973 [255]
2973 [255]
2974
2974
2975 test integer range in `_list`
2975 test integer range in `_list`
2976
2976
2977 $ log '-1|-10'
2977 $ log '-1|-10'
2978 9
2978 9
2979 0
2979 0
2980
2980
2981 $ log '-10|-11'
2981 $ log '-10|-11'
2982 abort: unknown revision '-11'!
2982 abort: unknown revision '-11'!
2983 [255]
2983 [255]
2984
2984
2985 $ log '9|10'
2985 $ log '9|10'
2986 abort: unknown revision '10'!
2986 abort: unknown revision '10'!
2987 [255]
2987 [255]
2988
2988
2989 test '0000' != '0' in `_list`
2989 test '0000' != '0' in `_list`
2990
2990
2991 $ log '0|0000'
2991 $ log '0|0000'
2992 0
2992 0
2993 -1
2993 -1
2994
2994
2995 test ',' in `_list`
2995 test ',' in `_list`
2996 $ log '0,1'
2996 $ log '0,1'
2997 hg: parse error: can't use a list in this context
2997 hg: parse error: can't use a list in this context
2998 (see hg help "revsets.x or y")
2998 (see hg help "revsets.x or y")
2999 [255]
2999 [255]
3000 $ try '0,1,2'
3000 $ try '0,1,2'
3001 (list
3001 (list
3002 ('symbol', '0')
3002 ('symbol', '0')
3003 ('symbol', '1')
3003 ('symbol', '1')
3004 ('symbol', '2'))
3004 ('symbol', '2'))
3005 hg: parse error: can't use a list in this context
3005 hg: parse error: can't use a list in this context
3006 (see hg help "revsets.x or y")
3006 (see hg help "revsets.x or y")
3007 [255]
3007 [255]
3008
3008
3009 test that chained `or` operations make balanced addsets
3009 test that chained `or` operations make balanced addsets
3010
3010
3011 $ try '0:1|1:2|2:3|3:4|4:5'
3011 $ try '0:1|1:2|2:3|3:4|4:5'
3012 (or
3012 (or
3013 (list
3013 (list
3014 (range
3014 (range
3015 ('symbol', '0')
3015 ('symbol', '0')
3016 ('symbol', '1'))
3016 ('symbol', '1'))
3017 (range
3017 (range
3018 ('symbol', '1')
3018 ('symbol', '1')
3019 ('symbol', '2'))
3019 ('symbol', '2'))
3020 (range
3020 (range
3021 ('symbol', '2')
3021 ('symbol', '2')
3022 ('symbol', '3'))
3022 ('symbol', '3'))
3023 (range
3023 (range
3024 ('symbol', '3')
3024 ('symbol', '3')
3025 ('symbol', '4'))
3025 ('symbol', '4'))
3026 (range
3026 (range
3027 ('symbol', '4')
3027 ('symbol', '4')
3028 ('symbol', '5'))))
3028 ('symbol', '5'))))
3029 * set:
3029 * set:
3030 <addset
3030 <addset
3031 <addset
3031 <addset
3032 <spanset+ 0:2>,
3032 <spanset+ 0:2>,
3033 <spanset+ 1:3>>,
3033 <spanset+ 1:3>>,
3034 <addset
3034 <addset
3035 <spanset+ 2:4>,
3035 <spanset+ 2:4>,
3036 <addset
3036 <addset
3037 <spanset+ 3:5>,
3037 <spanset+ 3:5>,
3038 <spanset+ 4:6>>>>
3038 <spanset+ 4:6>>>>
3039 0
3039 0
3040 1
3040 1
3041 2
3041 2
3042 3
3042 3
3043 4
3043 4
3044 5
3044 5
3045
3045
3046 no crash by empty group "()" while optimizing `or` operations
3046 no crash by empty group "()" while optimizing `or` operations
3047
3047
3048 $ try --optimize '0|()'
3048 $ try --optimize '0|()'
3049 (or
3049 (or
3050 (list
3050 (list
3051 ('symbol', '0')
3051 ('symbol', '0')
3052 (group
3052 (group
3053 None)))
3053 None)))
3054 * optimized:
3054 * optimized:
3055 (or
3055 (or
3056 (list
3056 (list
3057 ('symbol', '0')
3057 ('symbol', '0')
3058 None))
3058 None))
3059 hg: parse error: missing argument
3059 hg: parse error: missing argument
3060 [255]
3060 [255]
3061
3061
3062 test that chained `or` operations never eat up stack (issue4624)
3062 test that chained `or` operations never eat up stack (issue4624)
3063 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
3063 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
3064
3064
3065 $ hg log -T '{rev}\n' -r `$PYTHON -c "print '+'.join(['0:1'] * 500)"`
3065 $ hg log -T '{rev}\n' -r `$PYTHON -c "print '+'.join(['0:1'] * 500)"`
3066 0
3066 0
3067 1
3067 1
3068
3068
3069 test that repeated `-r` options never eat up stack (issue4565)
3069 test that repeated `-r` options never eat up stack (issue4565)
3070 (uses `-r 0::1` to avoid possible optimization at old-style parser)
3070 (uses `-r 0::1` to avoid possible optimization at old-style parser)
3071
3071
3072 $ hg log -T '{rev}\n' `$PYTHON -c "for i in xrange(500): print '-r 0::1 ',"`
3072 $ hg log -T '{rev}\n' `$PYTHON -c "for i in xrange(500): print '-r 0::1 ',"`
3073 0
3073 0
3074 1
3074 1
3075
3075
3076 check that conversion to only works
3076 check that conversion to only works
3077 $ try --optimize '::3 - ::1'
3077 $ try --optimize '::3 - ::1'
3078 (minus
3078 (minus
3079 (dagrangepre
3079 (dagrangepre
3080 ('symbol', '3'))
3080 ('symbol', '3'))
3081 (dagrangepre
3081 (dagrangepre
3082 ('symbol', '1')))
3082 ('symbol', '1')))
3083 * optimized:
3083 * optimized:
3084 (func
3084 (func
3085 ('symbol', 'only')
3085 ('symbol', 'only')
3086 (list
3086 (list
3087 ('symbol', '3')
3087 ('symbol', '3')
3088 ('symbol', '1')))
3088 ('symbol', '1')))
3089 * set:
3089 * set:
3090 <baseset+ [3]>
3090 <baseset+ [3]>
3091 3
3091 3
3092 $ try --optimize 'ancestors(1) - ancestors(3)'
3092 $ try --optimize 'ancestors(1) - ancestors(3)'
3093 (minus
3093 (minus
3094 (func
3094 (func
3095 ('symbol', 'ancestors')
3095 ('symbol', 'ancestors')
3096 ('symbol', '1'))
3096 ('symbol', '1'))
3097 (func
3097 (func
3098 ('symbol', 'ancestors')
3098 ('symbol', 'ancestors')
3099 ('symbol', '3')))
3099 ('symbol', '3')))
3100 * optimized:
3100 * optimized:
3101 (func
3101 (func
3102 ('symbol', 'only')
3102 ('symbol', 'only')
3103 (list
3103 (list
3104 ('symbol', '1')
3104 ('symbol', '1')
3105 ('symbol', '3')))
3105 ('symbol', '3')))
3106 * set:
3106 * set:
3107 <baseset+ []>
3107 <baseset+ []>
3108 $ try --optimize 'not ::2 and ::6'
3108 $ try --optimize 'not ::2 and ::6'
3109 (and
3109 (and
3110 (not
3110 (not
3111 (dagrangepre
3111 (dagrangepre
3112 ('symbol', '2')))
3112 ('symbol', '2')))
3113 (dagrangepre
3113 (dagrangepre
3114 ('symbol', '6')))
3114 ('symbol', '6')))
3115 * optimized:
3115 * optimized:
3116 (func
3116 (func
3117 ('symbol', 'only')
3117 ('symbol', 'only')
3118 (list
3118 (list
3119 ('symbol', '6')
3119 ('symbol', '6')
3120 ('symbol', '2')))
3120 ('symbol', '2')))
3121 * set:
3121 * set:
3122 <baseset+ [3, 4, 5, 6]>
3122 <baseset+ [3, 4, 5, 6]>
3123 3
3123 3
3124 4
3124 4
3125 5
3125 5
3126 6
3126 6
3127 $ try --optimize 'ancestors(6) and not ancestors(4)'
3127 $ try --optimize 'ancestors(6) and not ancestors(4)'
3128 (and
3128 (and
3129 (func
3129 (func
3130 ('symbol', 'ancestors')
3130 ('symbol', 'ancestors')
3131 ('symbol', '6'))
3131 ('symbol', '6'))
3132 (not
3132 (not
3133 (func
3133 (func
3134 ('symbol', 'ancestors')
3134 ('symbol', 'ancestors')
3135 ('symbol', '4'))))
3135 ('symbol', '4'))))
3136 * optimized:
3136 * optimized:
3137 (func
3137 (func
3138 ('symbol', 'only')
3138 ('symbol', 'only')
3139 (list
3139 (list
3140 ('symbol', '6')
3140 ('symbol', '6')
3141 ('symbol', '4')))
3141 ('symbol', '4')))
3142 * set:
3142 * set:
3143 <baseset+ [3, 5, 6]>
3143 <baseset+ [3, 5, 6]>
3144 3
3144 3
3145 5
3145 5
3146 6
3146 6
3147
3147
3148 no crash by empty group "()" while optimizing to "only()"
3148 no crash by empty group "()" while optimizing to "only()"
3149
3149
3150 $ try --optimize '::1 and ()'
3150 $ try --optimize '::1 and ()'
3151 (and
3151 (and
3152 (dagrangepre
3152 (dagrangepre
3153 ('symbol', '1'))
3153 ('symbol', '1'))
3154 (group
3154 (group
3155 None))
3155 None))
3156 * optimized:
3156 * optimized:
3157 (andsmally
3157 (andsmally
3158 (func
3158 (func
3159 ('symbol', 'ancestors')
3159 ('symbol', 'ancestors')
3160 ('symbol', '1'))
3160 ('symbol', '1'))
3161 None)
3161 None)
3162 hg: parse error: missing argument
3162 hg: parse error: missing argument
3163 [255]
3163 [255]
3164
3164
3165 optimization to only() works only if ancestors() takes only one argument
3165 optimization to only() works only if ancestors() takes only one argument
3166
3166
3167 $ hg debugrevspec -p optimized 'ancestors(6) - ancestors(4, 1)'
3167 $ hg debugrevspec -p optimized 'ancestors(6) - ancestors(4, 1)'
3168 * optimized:
3168 * optimized:
3169 (difference
3169 (difference
3170 (func
3170 (func
3171 ('symbol', 'ancestors')
3171 ('symbol', 'ancestors')
3172 ('symbol', '6'))
3172 ('symbol', '6'))
3173 (func
3173 (func
3174 ('symbol', 'ancestors')
3174 ('symbol', 'ancestors')
3175 (list
3175 (list
3176 ('symbol', '4')
3176 ('symbol', '4')
3177 ('symbol', '1'))))
3177 ('symbol', '1'))))
3178 0
3178 0
3179 1
3179 1
3180 3
3180 3
3181 5
3181 5
3182 6
3182 6
3183 $ hg debugrevspec -p optimized 'ancestors(6, 1) - ancestors(4)'
3183 $ hg debugrevspec -p optimized 'ancestors(6, 1) - ancestors(4)'
3184 * optimized:
3184 * optimized:
3185 (difference
3185 (difference
3186 (func
3186 (func
3187 ('symbol', 'ancestors')
3187 ('symbol', 'ancestors')
3188 (list
3188 (list
3189 ('symbol', '6')
3189 ('symbol', '6')
3190 ('symbol', '1')))
3190 ('symbol', '1')))
3191 (func
3191 (func
3192 ('symbol', 'ancestors')
3192 ('symbol', 'ancestors')
3193 ('symbol', '4')))
3193 ('symbol', '4')))
3194 5
3194 5
3195 6
3195 6
3196
3196
3197 optimization disabled if keyword arguments passed (because we're too lazy
3197 optimization disabled if keyword arguments passed (because we're too lazy
3198 to support it)
3198 to support it)
3199
3199
3200 $ hg debugrevspec -p optimized 'ancestors(set=6) - ancestors(set=4)'
3200 $ hg debugrevspec -p optimized 'ancestors(set=6) - ancestors(set=4)'
3201 * optimized:
3201 * optimized:
3202 (difference
3202 (difference
3203 (func
3203 (func
3204 ('symbol', 'ancestors')
3204 ('symbol', 'ancestors')
3205 (keyvalue
3205 (keyvalue
3206 ('symbol', 'set')
3206 ('symbol', 'set')
3207 ('symbol', '6')))
3207 ('symbol', '6')))
3208 (func
3208 (func
3209 ('symbol', 'ancestors')
3209 ('symbol', 'ancestors')
3210 (keyvalue
3210 (keyvalue
3211 ('symbol', 'set')
3211 ('symbol', 'set')
3212 ('symbol', '4'))))
3212 ('symbol', '4'))))
3213 3
3213 3
3214 5
3214 5
3215 6
3215 6
3216
3216
3217 invalid function call should not be optimized to only()
3217 invalid function call should not be optimized to only()
3218
3218
3219 $ log '"ancestors"(6) and not ancestors(4)'
3219 $ log '"ancestors"(6) and not ancestors(4)'
3220 hg: parse error: not a symbol
3220 hg: parse error: not a symbol
3221 [255]
3221 [255]
3222
3222
3223 $ log 'ancestors(6) and not "ancestors"(4)'
3223 $ log 'ancestors(6) and not "ancestors"(4)'
3224 hg: parse error: not a symbol
3224 hg: parse error: not a symbol
3225 [255]
3225 [255]
3226
3226
3227 we can use patterns when searching for tags
3227 we can use patterns when searching for tags
3228
3228
3229 $ log 'tag("1..*")'
3229 $ log 'tag("1..*")'
3230 abort: tag '1..*' does not exist!
3230 abort: tag '1..*' does not exist!
3231 [255]
3231 [255]
3232 $ log 'tag("re:1..*")'
3232 $ log 'tag("re:1..*")'
3233 6
3233 6
3234 $ log 'tag("re:[0-9].[0-9]")'
3234 $ log 'tag("re:[0-9].[0-9]")'
3235 6
3235 6
3236 $ log 'tag("literal:1.0")'
3236 $ log 'tag("literal:1.0")'
3237 6
3237 6
3238 $ log 'tag("re:0..*")'
3238 $ log 'tag("re:0..*")'
3239
3239
3240 $ log 'tag(unknown)'
3240 $ log 'tag(unknown)'
3241 abort: tag 'unknown' does not exist!
3241 abort: tag 'unknown' does not exist!
3242 [255]
3242 [255]
3243 $ log 'tag("re:unknown")'
3243 $ log 'tag("re:unknown")'
3244 $ log 'present(tag("unknown"))'
3244 $ log 'present(tag("unknown"))'
3245 $ log 'present(tag("re:unknown"))'
3245 $ log 'present(tag("re:unknown"))'
3246 $ log 'branch(unknown)'
3246 $ log 'branch(unknown)'
3247 abort: unknown revision 'unknown'!
3247 abort: unknown revision 'unknown'!
3248 [255]
3248 [255]
3249 $ log 'branch("literal:unknown")'
3249 $ log 'branch("literal:unknown")'
3250 abort: branch 'unknown' does not exist!
3250 abort: branch 'unknown' does not exist!
3251 [255]
3251 [255]
3252 $ log 'branch("re:unknown")'
3252 $ log 'branch("re:unknown")'
3253 $ log 'present(branch("unknown"))'
3253 $ log 'present(branch("unknown"))'
3254 $ log 'present(branch("re:unknown"))'
3254 $ log 'present(branch("re:unknown"))'
3255 $ log 'user(bob)'
3255 $ log 'user(bob)'
3256 2
3256 2
3257
3257
3258 $ log '4::8'
3258 $ log '4::8'
3259 4
3259 4
3260 8
3260 8
3261 $ log '4:8'
3261 $ log '4:8'
3262 4
3262 4
3263 5
3263 5
3264 6
3264 6
3265 7
3265 7
3266 8
3266 8
3267
3267
3268 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
3268 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
3269 4
3269 4
3270 2
3270 2
3271 5
3271 5
3272
3272
3273 $ log 'not 0 and 0:2'
3273 $ log 'not 0 and 0:2'
3274 1
3274 1
3275 2
3275 2
3276 $ log 'not 1 and 0:2'
3276 $ log 'not 1 and 0:2'
3277 0
3277 0
3278 2
3278 2
3279 $ log 'not 2 and 0:2'
3279 $ log 'not 2 and 0:2'
3280 0
3280 0
3281 1
3281 1
3282 $ log '(1 and 2)::'
3282 $ log '(1 and 2)::'
3283 $ log '(1 and 2):'
3283 $ log '(1 and 2):'
3284 $ log '(1 and 2):3'
3284 $ log '(1 and 2):3'
3285 $ log 'sort(head(), -rev)'
3285 $ log 'sort(head(), -rev)'
3286 9
3286 9
3287 7
3287 7
3288 6
3288 6
3289 5
3289 5
3290 4
3290 4
3291 3
3291 3
3292 2
3292 2
3293 1
3293 1
3294 0
3294 0
3295 $ log '4::8 - 8'
3295 $ log '4::8 - 8'
3296 4
3296 4
3297
3297
3298 matching() should preserve the order of the input set:
3298 matching() should preserve the order of the input set:
3299
3299
3300 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
3300 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
3301 2
3301 2
3302 3
3302 3
3303 1
3303 1
3304
3304
3305 $ log 'named("unknown")'
3305 $ log 'named("unknown")'
3306 abort: namespace 'unknown' does not exist!
3306 abort: namespace 'unknown' does not exist!
3307 [255]
3307 [255]
3308 $ log 'named("re:unknown")'
3308 $ log 'named("re:unknown")'
3309 abort: no namespace exists that match 'unknown'!
3309 abort: no namespace exists that match 'unknown'!
3310 [255]
3310 [255]
3311 $ log 'present(named("unknown"))'
3311 $ log 'present(named("unknown"))'
3312 $ log 'present(named("re:unknown"))'
3312 $ log 'present(named("re:unknown"))'
3313
3313
3314 $ log 'tag()'
3314 $ log 'tag()'
3315 6
3315 6
3316 $ log 'named("tags")'
3316 $ log 'named("tags")'
3317 6
3317 6
3318
3318
3319 issue2437
3319 issue2437
3320
3320
3321 $ log '3 and p1(5)'
3321 $ log '3 and p1(5)'
3322 3
3322 3
3323 $ log '4 and p2(6)'
3323 $ log '4 and p2(6)'
3324 4
3324 4
3325 $ log '1 and parents(:2)'
3325 $ log '1 and parents(:2)'
3326 1
3326 1
3327 $ log '2 and children(1:)'
3327 $ log '2 and children(1:)'
3328 2
3328 2
3329 $ log 'roots(all()) or roots(all())'
3329 $ log 'roots(all()) or roots(all())'
3330 0
3330 0
3331 $ hg debugrevspec 'roots(all()) or roots(all())'
3331 $ hg debugrevspec 'roots(all()) or roots(all())'
3332 0
3332 0
3333 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
3333 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
3334 9
3334 9
3335 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
3335 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
3336 4
3336 4
3337
3337
3338 issue2654: report a parse error if the revset was not completely parsed
3338 issue2654: report a parse error if the revset was not completely parsed
3339
3339
3340 $ log '1 OR 2'
3340 $ log '1 OR 2'
3341 hg: parse error at 2: invalid token
3341 hg: parse error at 2: invalid token
3342 [255]
3342 [255]
3343
3343
3344 or operator should preserve ordering:
3344 or operator should preserve ordering:
3345 $ log 'reverse(2::4) or tip'
3345 $ log 'reverse(2::4) or tip'
3346 4
3346 4
3347 2
3347 2
3348 9
3348 9
3349
3349
3350 parentrevspec
3350 parentrevspec
3351
3351
3352 $ log 'merge()^0'
3352 $ log 'merge()^0'
3353 6
3353 6
3354 $ log 'merge()^'
3354 $ log 'merge()^'
3355 5
3355 5
3356 $ log 'merge()^1'
3356 $ log 'merge()^1'
3357 5
3357 5
3358 $ log 'merge()^2'
3358 $ log 'merge()^2'
3359 4
3359 4
3360 $ log '(not merge())^2'
3360 $ log '(not merge())^2'
3361 $ log 'merge()^^'
3361 $ log 'merge()^^'
3362 3
3362 3
3363 $ log 'merge()^1^'
3363 $ log 'merge()^1^'
3364 3
3364 3
3365 $ log 'merge()^^^'
3365 $ log 'merge()^^^'
3366 1
3366 1
3367
3367
3368 $ hg debugrevspec -s '(merge() | 0)~-1'
3368 $ hg debugrevspec -s '(merge() | 0)~-1'
3369 * set:
3369 * set:
3370 <baseset+ [1, 7]>
3370 <baseset+ [1, 7]>
3371 1
3371 1
3372 7
3372 7
3373 $ log 'merge()~-1'
3373 $ log 'merge()~-1'
3374 7
3374 7
3375 $ log 'tip~-1'
3375 $ log 'tip~-1'
3376 $ log '(tip | merge())~-1'
3376 $ log '(tip | merge())~-1'
3377 7
3377 7
3378 $ log 'merge()~0'
3378 $ log 'merge()~0'
3379 6
3379 6
3380 $ log 'merge()~1'
3380 $ log 'merge()~1'
3381 5
3381 5
3382 $ log 'merge()~2'
3382 $ log 'merge()~2'
3383 3
3383 3
3384 $ log 'merge()~2^1'
3384 $ log 'merge()~2^1'
3385 1
3385 1
3386 $ log 'merge()~3'
3386 $ log 'merge()~3'
3387 1
3387 1
3388
3388
3389 $ log '(-3:tip)^'
3389 $ log '(-3:tip)^'
3390 4
3390 4
3391 6
3391 6
3392 8
3392 8
3393
3393
3394 $ log 'tip^foo'
3394 $ log 'tip^foo'
3395 hg: parse error: ^ expects a number 0, 1, or 2
3395 hg: parse error: ^ expects a number 0, 1, or 2
3396 [255]
3396 [255]
3397
3397
3398 $ log 'branchpoint()~-1'
3398 $ log 'branchpoint()~-1'
3399 abort: revision in set has more than one child!
3399 abort: revision in set has more than one child!
3400 [255]
3400 [255]
3401
3401
3402 Bogus function gets suggestions
3402 Bogus function gets suggestions
3403 $ log 'add()'
3403 $ log 'add()'
3404 hg: parse error: unknown identifier: add
3404 hg: parse error: unknown identifier: add
3405 (did you mean adds?)
3405 (did you mean adds?)
3406 [255]
3406 [255]
3407 $ log 'added()'
3407 $ log 'added()'
3408 hg: parse error: unknown identifier: added
3408 hg: parse error: unknown identifier: added
3409 (did you mean adds?)
3409 (did you mean adds?)
3410 [255]
3410 [255]
3411 $ log 'remo()'
3411 $ log 'remo()'
3412 hg: parse error: unknown identifier: remo
3412 hg: parse error: unknown identifier: remo
3413 (did you mean one of remote, removes?)
3413 (did you mean one of remote, removes?)
3414 [255]
3414 [255]
3415 $ log 'babar()'
3415 $ log 'babar()'
3416 hg: parse error: unknown identifier: babar
3416 hg: parse error: unknown identifier: babar
3417 [255]
3417 [255]
3418
3418
3419 Bogus function with a similar internal name doesn't suggest the internal name
3419 Bogus function with a similar internal name doesn't suggest the internal name
3420 $ log 'matches()'
3420 $ log 'matches()'
3421 hg: parse error: unknown identifier: matches
3421 hg: parse error: unknown identifier: matches
3422 (did you mean matching?)
3422 (did you mean matching?)
3423 [255]
3423 [255]
3424
3424
3425 Undocumented functions aren't suggested as similar either
3425 Undocumented functions aren't suggested as similar either
3426 $ log 'tagged2()'
3426 $ log 'tagged2()'
3427 hg: parse error: unknown identifier: tagged2
3427 hg: parse error: unknown identifier: tagged2
3428 [255]
3428 [255]
3429
3429
3430 multiple revspecs
3430 multiple revspecs
3431
3431
3432 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
3432 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
3433 8
3433 8
3434 9
3434 9
3435 4
3435 4
3436 5
3436 5
3437 6
3437 6
3438 7
3438 7
3439
3439
3440 test usage in revpair (with "+")
3440 test usage in revpair (with "+")
3441
3441
3442 (real pair)
3442 (real pair)
3443
3443
3444 $ hg diff -r 'tip^^' -r 'tip'
3444 $ hg diff -r 'tip^^' -r 'tip'
3445 diff -r 2326846efdab -r 24286f4ae135 .hgtags
3445 diff -r 2326846efdab -r 24286f4ae135 .hgtags
3446 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3446 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3447 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
3447 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
3448 @@ -0,0 +1,1 @@
3448 @@ -0,0 +1,1 @@
3449 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3449 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3450 $ hg diff -r 'tip^^::tip'
3450 $ hg diff -r 'tip^^::tip'
3451 diff -r 2326846efdab -r 24286f4ae135 .hgtags
3451 diff -r 2326846efdab -r 24286f4ae135 .hgtags
3452 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3452 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3453 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
3453 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
3454 @@ -0,0 +1,1 @@
3454 @@ -0,0 +1,1 @@
3455 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3455 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3456
3456
3457 (single rev)
3457 (single rev)
3458
3458
3459 $ hg diff -r 'tip^' -r 'tip^'
3459 $ hg diff -r 'tip^' -r 'tip^'
3460 $ hg diff -r 'tip^:tip^'
3460 $ hg diff -r 'tip^:tip^'
3461
3461
3462 (single rev that does not looks like a range)
3462 (single rev that does not looks like a range)
3463
3463
3464 $ hg diff -r 'tip^::tip^ or tip^'
3464 $ hg diff -r 'tip^::tip^ or tip^'
3465 diff -r d5d0dcbdc4d9 .hgtags
3465 diff -r d5d0dcbdc4d9 .hgtags
3466 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3466 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3467 +++ b/.hgtags * (glob)
3467 +++ b/.hgtags * (glob)
3468 @@ -0,0 +1,1 @@
3468 @@ -0,0 +1,1 @@
3469 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3469 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3470 $ hg diff -r 'tip^ or tip^'
3470 $ hg diff -r 'tip^ or tip^'
3471 diff -r d5d0dcbdc4d9 .hgtags
3471 diff -r d5d0dcbdc4d9 .hgtags
3472 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3472 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3473 +++ b/.hgtags * (glob)
3473 +++ b/.hgtags * (glob)
3474 @@ -0,0 +1,1 @@
3474 @@ -0,0 +1,1 @@
3475 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3475 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3476
3476
3477 (no rev)
3477 (no rev)
3478
3478
3479 $ hg diff -r 'author("babar") or author("celeste")'
3479 $ hg diff -r 'author("babar") or author("celeste")'
3480 abort: empty revision range
3480 abort: empty revision range
3481 [255]
3481 [255]
3482
3482
3483 aliases:
3483 aliases:
3484
3484
3485 $ echo '[revsetalias]' >> .hg/hgrc
3485 $ echo '[revsetalias]' >> .hg/hgrc
3486 $ echo 'm = merge()' >> .hg/hgrc
3486 $ echo 'm = merge()' >> .hg/hgrc
3487 (revset aliases can override builtin revsets)
3487 (revset aliases can override builtin revsets)
3488 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
3488 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
3489 $ echo 'sincem = descendants(m)' >> .hg/hgrc
3489 $ echo 'sincem = descendants(m)' >> .hg/hgrc
3490 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
3490 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
3491 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
3491 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
3492 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
3492 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
3493
3493
3494 $ try m
3494 $ try m
3495 ('symbol', 'm')
3495 ('symbol', 'm')
3496 * expanded:
3496 * expanded:
3497 (func
3497 (func
3498 ('symbol', 'merge')
3498 ('symbol', 'merge')
3499 None)
3499 None)
3500 * set:
3500 * set:
3501 <filteredset
3501 <filteredset
3502 <fullreposet+ 0:10>,
3502 <fullreposet+ 0:10>,
3503 <merge>>
3503 <merge>>
3504 6
3504 6
3505
3505
3506 $ HGPLAIN=1
3506 $ HGPLAIN=1
3507 $ export HGPLAIN
3507 $ export HGPLAIN
3508 $ try m
3508 $ try m
3509 ('symbol', 'm')
3509 ('symbol', 'm')
3510 abort: unknown revision 'm'!
3510 abort: unknown revision 'm'!
3511 [255]
3511 [255]
3512
3512
3513 $ HGPLAINEXCEPT=revsetalias
3513 $ HGPLAINEXCEPT=revsetalias
3514 $ export HGPLAINEXCEPT
3514 $ export HGPLAINEXCEPT
3515 $ try m
3515 $ try m
3516 ('symbol', 'm')
3516 ('symbol', 'm')
3517 * expanded:
3517 * expanded:
3518 (func
3518 (func
3519 ('symbol', 'merge')
3519 ('symbol', 'merge')
3520 None)
3520 None)
3521 * set:
3521 * set:
3522 <filteredset
3522 <filteredset
3523 <fullreposet+ 0:10>,
3523 <fullreposet+ 0:10>,
3524 <merge>>
3524 <merge>>
3525 6
3525 6
3526
3526
3527 $ unset HGPLAIN
3527 $ unset HGPLAIN
3528 $ unset HGPLAINEXCEPT
3528 $ unset HGPLAINEXCEPT
3529
3529
3530 $ try 'p2(.)'
3530 $ try 'p2(.)'
3531 (func
3531 (func
3532 ('symbol', 'p2')
3532 ('symbol', 'p2')
3533 ('symbol', '.'))
3533 ('symbol', '.'))
3534 * expanded:
3534 * expanded:
3535 (func
3535 (func
3536 ('symbol', 'p1')
3536 ('symbol', 'p1')
3537 ('symbol', '.'))
3537 ('symbol', '.'))
3538 * set:
3538 * set:
3539 <baseset+ [8]>
3539 <baseset+ [8]>
3540 8
3540 8
3541
3541
3542 $ HGPLAIN=1
3542 $ HGPLAIN=1
3543 $ export HGPLAIN
3543 $ export HGPLAIN
3544 $ try 'p2(.)'
3544 $ try 'p2(.)'
3545 (func
3545 (func
3546 ('symbol', 'p2')
3546 ('symbol', 'p2')
3547 ('symbol', '.'))
3547 ('symbol', '.'))
3548 * set:
3548 * set:
3549 <baseset+ []>
3549 <baseset+ []>
3550
3550
3551 $ HGPLAINEXCEPT=revsetalias
3551 $ HGPLAINEXCEPT=revsetalias
3552 $ export HGPLAINEXCEPT
3552 $ export HGPLAINEXCEPT
3553 $ try 'p2(.)'
3553 $ try 'p2(.)'
3554 (func
3554 (func
3555 ('symbol', 'p2')
3555 ('symbol', 'p2')
3556 ('symbol', '.'))
3556 ('symbol', '.'))
3557 * expanded:
3557 * expanded:
3558 (func
3558 (func
3559 ('symbol', 'p1')
3559 ('symbol', 'p1')
3560 ('symbol', '.'))
3560 ('symbol', '.'))
3561 * set:
3561 * set:
3562 <baseset+ [8]>
3562 <baseset+ [8]>
3563 8
3563 8
3564
3564
3565 $ unset HGPLAIN
3565 $ unset HGPLAIN
3566 $ unset HGPLAINEXCEPT
3566 $ unset HGPLAINEXCEPT
3567
3567
3568 test alias recursion
3568 test alias recursion
3569
3569
3570 $ try sincem
3570 $ try sincem
3571 ('symbol', 'sincem')
3571 ('symbol', 'sincem')
3572 * expanded:
3572 * expanded:
3573 (func
3573 (func
3574 ('symbol', 'descendants')
3574 ('symbol', 'descendants')
3575 (func
3575 (func
3576 ('symbol', 'merge')
3576 ('symbol', 'merge')
3577 None))
3577 None))
3578 * set:
3578 * set:
3579 <generatorset+>
3579 <generatorset+>
3580 6
3580 6
3581 7
3581 7
3582
3582
3583 test infinite recursion
3583 test infinite recursion
3584
3584
3585 $ echo 'recurse1 = recurse2' >> .hg/hgrc
3585 $ echo 'recurse1 = recurse2' >> .hg/hgrc
3586 $ echo 'recurse2 = recurse1' >> .hg/hgrc
3586 $ echo 'recurse2 = recurse1' >> .hg/hgrc
3587 $ try recurse1
3587 $ try recurse1
3588 ('symbol', 'recurse1')
3588 ('symbol', 'recurse1')
3589 hg: parse error: infinite expansion of revset alias "recurse1" detected
3589 hg: parse error: infinite expansion of revset alias "recurse1" detected
3590 [255]
3590 [255]
3591
3591
3592 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
3592 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
3593 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
3593 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
3594 $ try "level2(level1(1, 2), 3)"
3594 $ try "level2(level1(1, 2), 3)"
3595 (func
3595 (func
3596 ('symbol', 'level2')
3596 ('symbol', 'level2')
3597 (list
3597 (list
3598 (func
3598 (func
3599 ('symbol', 'level1')
3599 ('symbol', 'level1')
3600 (list
3600 (list
3601 ('symbol', '1')
3601 ('symbol', '1')
3602 ('symbol', '2')))
3602 ('symbol', '2')))
3603 ('symbol', '3')))
3603 ('symbol', '3')))
3604 * expanded:
3604 * expanded:
3605 (or
3605 (or
3606 (list
3606 (list
3607 ('symbol', '3')
3607 ('symbol', '3')
3608 (or
3608 (or
3609 (list
3609 (list
3610 ('symbol', '1')
3610 ('symbol', '1')
3611 ('symbol', '2')))))
3611 ('symbol', '2')))))
3612 * set:
3612 * set:
3613 <addset
3613 <addset
3614 <baseset [3]>,
3614 <baseset [3]>,
3615 <baseset [1, 2]>>
3615 <baseset [1, 2]>>
3616 3
3616 3
3617 1
3617 1
3618 2
3618 2
3619
3619
3620 test nesting and variable passing
3620 test nesting and variable passing
3621
3621
3622 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
3622 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
3623 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
3623 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
3624 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
3624 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
3625 $ try 'nested(2:5)'
3625 $ try 'nested(2:5)'
3626 (func
3626 (func
3627 ('symbol', 'nested')
3627 ('symbol', 'nested')
3628 (range
3628 (range
3629 ('symbol', '2')
3629 ('symbol', '2')
3630 ('symbol', '5')))
3630 ('symbol', '5')))
3631 * expanded:
3631 * expanded:
3632 (func
3632 (func
3633 ('symbol', 'max')
3633 ('symbol', 'max')
3634 (range
3634 (range
3635 ('symbol', '2')
3635 ('symbol', '2')
3636 ('symbol', '5')))
3636 ('symbol', '5')))
3637 * set:
3637 * set:
3638 <baseset
3638 <baseset
3639 <max
3639 <max
3640 <fullreposet+ 0:10>,
3640 <fullreposet+ 0:10>,
3641 <spanset+ 2:6>>>
3641 <spanset+ 2:6>>>
3642 5
3642 5
3643
3643
3644 test chained `or` operations are flattened at parsing phase
3644 test chained `or` operations are flattened at parsing phase
3645
3645
3646 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
3646 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
3647 $ try 'chainedorops(0:1, 1:2, 2:3)'
3647 $ try 'chainedorops(0:1, 1:2, 2:3)'
3648 (func
3648 (func
3649 ('symbol', 'chainedorops')
3649 ('symbol', 'chainedorops')
3650 (list
3650 (list
3651 (range
3651 (range
3652 ('symbol', '0')
3652 ('symbol', '0')
3653 ('symbol', '1'))
3653 ('symbol', '1'))
3654 (range
3654 (range
3655 ('symbol', '1')
3655 ('symbol', '1')
3656 ('symbol', '2'))
3656 ('symbol', '2'))
3657 (range
3657 (range
3658 ('symbol', '2')
3658 ('symbol', '2')
3659 ('symbol', '3'))))
3659 ('symbol', '3'))))
3660 * expanded:
3660 * expanded:
3661 (or
3661 (or
3662 (list
3662 (list
3663 (range
3663 (range
3664 ('symbol', '0')
3664 ('symbol', '0')
3665 ('symbol', '1'))
3665 ('symbol', '1'))
3666 (range
3666 (range
3667 ('symbol', '1')
3667 ('symbol', '1')
3668 ('symbol', '2'))
3668 ('symbol', '2'))
3669 (range
3669 (range
3670 ('symbol', '2')
3670 ('symbol', '2')
3671 ('symbol', '3'))))
3671 ('symbol', '3'))))
3672 * set:
3672 * set:
3673 <addset
3673 <addset
3674 <spanset+ 0:2>,
3674 <spanset+ 0:2>,
3675 <addset
3675 <addset
3676 <spanset+ 1:3>,
3676 <spanset+ 1:3>,
3677 <spanset+ 2:4>>>
3677 <spanset+ 2:4>>>
3678 0
3678 0
3679 1
3679 1
3680 2
3680 2
3681 3
3681 3
3682
3682
3683 test variable isolation, variable placeholders are rewritten as string
3683 test variable isolation, variable placeholders are rewritten as string
3684 then parsed and matched again as string. Check they do not leak too
3684 then parsed and matched again as string. Check they do not leak too
3685 far away.
3685 far away.
3686
3686
3687 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
3687 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
3688 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
3688 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
3689 $ try 'callinjection(2:5)'
3689 $ try 'callinjection(2:5)'
3690 (func
3690 (func
3691 ('symbol', 'callinjection')
3691 ('symbol', 'callinjection')
3692 (range
3692 (range
3693 ('symbol', '2')
3693 ('symbol', '2')
3694 ('symbol', '5')))
3694 ('symbol', '5')))
3695 * expanded:
3695 * expanded:
3696 (func
3696 (func
3697 ('symbol', 'descendants')
3697 ('symbol', 'descendants')
3698 (func
3698 (func
3699 ('symbol', 'max')
3699 ('symbol', 'max')
3700 ('string', '$1')))
3700 ('string', '$1')))
3701 abort: unknown revision '$1'!
3701 abort: unknown revision '$1'!
3702 [255]
3702 [255]
3703
3703
3704 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
3704 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
3705 but 'all()' should never be substituted to '0()'.
3705 but 'all()' should never be substituted to '0()'.
3706
3706
3707 $ echo 'universe = all()' >> .hg/hgrc
3707 $ echo 'universe = all()' >> .hg/hgrc
3708 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
3708 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
3709 $ try 'shadowall(0)'
3709 $ try 'shadowall(0)'
3710 (func
3710 (func
3711 ('symbol', 'shadowall')
3711 ('symbol', 'shadowall')
3712 ('symbol', '0'))
3712 ('symbol', '0'))
3713 * expanded:
3713 * expanded:
3714 (and
3714 (and
3715 ('symbol', '0')
3715 ('symbol', '0')
3716 (func
3716 (func
3717 ('symbol', 'all')
3717 ('symbol', 'all')
3718 None))
3718 None))
3719 * set:
3719 * set:
3720 <filteredset
3720 <filteredset
3721 <baseset [0]>,
3721 <baseset [0]>,
3722 <spanset+ 0:10>>
3722 <spanset+ 0:10>>
3723 0
3723 0
3724
3724
3725 test unknown reference:
3725 test unknown reference:
3726
3726
3727 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
3727 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
3728 (func
3728 (func
3729 ('symbol', 'unknownref')
3729 ('symbol', 'unknownref')
3730 ('symbol', '0'))
3730 ('symbol', '0'))
3731 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
3731 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
3732 [255]
3732 [255]
3733
3733
3734 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
3734 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
3735 ('symbol', 'tip')
3735 ('symbol', 'tip')
3736 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
3736 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
3737 * set:
3737 * set:
3738 <baseset [9]>
3738 <baseset [9]>
3739 9
3739 9
3740
3740
3741 $ try 'tip'
3741 $ try 'tip'
3742 ('symbol', 'tip')
3742 ('symbol', 'tip')
3743 * set:
3743 * set:
3744 <baseset [9]>
3744 <baseset [9]>
3745 9
3745 9
3746
3746
3747 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
3747 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
3748 ('symbol', 'tip')
3748 ('symbol', 'tip')
3749 warning: bad declaration of revset alias "bad name": at 4: invalid token
3749 warning: bad declaration of revset alias "bad name": at 4: invalid token
3750 * set:
3750 * set:
3751 <baseset [9]>
3751 <baseset [9]>
3752 9
3752 9
3753 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
3753 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
3754 $ try 'strictreplacing("foo", tip)'
3754 $ try 'strictreplacing("foo", tip)'
3755 (func
3755 (func
3756 ('symbol', 'strictreplacing')
3756 ('symbol', 'strictreplacing')
3757 (list
3757 (list
3758 ('string', 'foo')
3758 ('string', 'foo')
3759 ('symbol', 'tip')))
3759 ('symbol', 'tip')))
3760 * expanded:
3760 * expanded:
3761 (or
3761 (or
3762 (list
3762 (list
3763 ('symbol', 'tip')
3763 ('symbol', 'tip')
3764 (func
3764 (func
3765 ('symbol', 'desc')
3765 ('symbol', 'desc')
3766 ('string', '$1'))))
3766 ('string', '$1'))))
3767 * set:
3767 * set:
3768 <addset
3768 <addset
3769 <baseset [9]>,
3769 <baseset [9]>,
3770 <filteredset
3770 <filteredset
3771 <fullreposet+ 0:10>,
3771 <fullreposet+ 0:10>,
3772 <desc '$1'>>>
3772 <desc '$1'>>>
3773 9
3773 9
3774
3774
3775 $ try 'd(2:5)'
3775 $ try 'd(2:5)'
3776 (func
3776 (func
3777 ('symbol', 'd')
3777 ('symbol', 'd')
3778 (range
3778 (range
3779 ('symbol', '2')
3779 ('symbol', '2')
3780 ('symbol', '5')))
3780 ('symbol', '5')))
3781 * expanded:
3781 * expanded:
3782 (func
3782 (func
3783 ('symbol', 'reverse')
3783 ('symbol', 'reverse')
3784 (func
3784 (func
3785 ('symbol', 'sort')
3785 ('symbol', 'sort')
3786 (list
3786 (list
3787 (range
3787 (range
3788 ('symbol', '2')
3788 ('symbol', '2')
3789 ('symbol', '5'))
3789 ('symbol', '5'))
3790 ('symbol', 'date'))))
3790 ('symbol', 'date'))))
3791 * set:
3791 * set:
3792 <baseset [4, 5, 3, 2]>
3792 <baseset [4, 5, 3, 2]>
3793 4
3793 4
3794 5
3794 5
3795 3
3795 3
3796 2
3796 2
3797 $ try 'rs(2 or 3, date)'
3797 $ try 'rs(2 or 3, date)'
3798 (func
3798 (func
3799 ('symbol', 'rs')
3799 ('symbol', 'rs')
3800 (list
3800 (list
3801 (or
3801 (or
3802 (list
3802 (list
3803 ('symbol', '2')
3803 ('symbol', '2')
3804 ('symbol', '3')))
3804 ('symbol', '3')))
3805 ('symbol', 'date')))
3805 ('symbol', 'date')))
3806 * expanded:
3806 * expanded:
3807 (func
3807 (func
3808 ('symbol', 'reverse')
3808 ('symbol', 'reverse')
3809 (func
3809 (func
3810 ('symbol', 'sort')
3810 ('symbol', 'sort')
3811 (list
3811 (list
3812 (or
3812 (or
3813 (list
3813 (list
3814 ('symbol', '2')
3814 ('symbol', '2')
3815 ('symbol', '3')))
3815 ('symbol', '3')))
3816 ('symbol', 'date'))))
3816 ('symbol', 'date'))))
3817 * set:
3817 * set:
3818 <baseset [3, 2]>
3818 <baseset [3, 2]>
3819 3
3819 3
3820 2
3820 2
3821 $ try 'rs()'
3821 $ try 'rs()'
3822 (func
3822 (func
3823 ('symbol', 'rs')
3823 ('symbol', 'rs')
3824 None)
3824 None)
3825 hg: parse error: invalid number of arguments: 0
3825 hg: parse error: invalid number of arguments: 0
3826 [255]
3826 [255]
3827 $ try 'rs(2)'
3827 $ try 'rs(2)'
3828 (func
3828 (func
3829 ('symbol', 'rs')
3829 ('symbol', 'rs')
3830 ('symbol', '2'))
3830 ('symbol', '2'))
3831 hg: parse error: invalid number of arguments: 1
3831 hg: parse error: invalid number of arguments: 1
3832 [255]
3832 [255]
3833 $ try 'rs(2, data, 7)'
3833 $ try 'rs(2, data, 7)'
3834 (func
3834 (func
3835 ('symbol', 'rs')
3835 ('symbol', 'rs')
3836 (list
3836 (list
3837 ('symbol', '2')
3837 ('symbol', '2')
3838 ('symbol', 'data')
3838 ('symbol', 'data')
3839 ('symbol', '7')))
3839 ('symbol', '7')))
3840 hg: parse error: invalid number of arguments: 3
3840 hg: parse error: invalid number of arguments: 3
3841 [255]
3841 [255]
3842 $ try 'rs4(2 or 3, x, x, date)'
3842 $ try 'rs4(2 or 3, x, x, date)'
3843 (func
3843 (func
3844 ('symbol', 'rs4')
3844 ('symbol', 'rs4')
3845 (list
3845 (list
3846 (or
3846 (or
3847 (list
3847 (list
3848 ('symbol', '2')
3848 ('symbol', '2')
3849 ('symbol', '3')))
3849 ('symbol', '3')))
3850 ('symbol', 'x')
3850 ('symbol', 'x')
3851 ('symbol', 'x')
3851 ('symbol', 'x')
3852 ('symbol', 'date')))
3852 ('symbol', 'date')))
3853 * expanded:
3853 * expanded:
3854 (func
3854 (func
3855 ('symbol', 'reverse')
3855 ('symbol', 'reverse')
3856 (func
3856 (func
3857 ('symbol', 'sort')
3857 ('symbol', 'sort')
3858 (list
3858 (list
3859 (or
3859 (or
3860 (list
3860 (list
3861 ('symbol', '2')
3861 ('symbol', '2')
3862 ('symbol', '3')))
3862 ('symbol', '3')))
3863 ('symbol', 'date'))))
3863 ('symbol', 'date'))))
3864 * set:
3864 * set:
3865 <baseset [3, 2]>
3865 <baseset [3, 2]>
3866 3
3866 3
3867 2
3867 2
3868
3868
3869 issue4553: check that revset aliases override existing hash prefix
3869 issue4553: check that revset aliases override existing hash prefix
3870
3870
3871 $ hg log -qr e
3871 $ hg log -qr e
3872 6:e0cc66ef77e8
3872 6:e0cc66ef77e8
3873
3873
3874 $ hg log -qr e --config revsetalias.e="all()"
3874 $ hg log -qr e --config revsetalias.e="all()"
3875 0:2785f51eece5
3875 0:2785f51eece5
3876 1:d75937da8da0
3876 1:d75937da8da0
3877 2:5ed5505e9f1c
3877 2:5ed5505e9f1c
3878 3:8528aa5637f2
3878 3:8528aa5637f2
3879 4:2326846efdab
3879 4:2326846efdab
3880 5:904fa392b941
3880 5:904fa392b941
3881 6:e0cc66ef77e8
3881 6:e0cc66ef77e8
3882 7:013af1973af4
3882 7:013af1973af4
3883 8:d5d0dcbdc4d9
3883 8:d5d0dcbdc4d9
3884 9:24286f4ae135
3884 9:24286f4ae135
3885
3885
3886 $ hg log -qr e: --config revsetalias.e="0"
3886 $ hg log -qr e: --config revsetalias.e="0"
3887 0:2785f51eece5
3887 0:2785f51eece5
3888 1:d75937da8da0
3888 1:d75937da8da0
3889 2:5ed5505e9f1c
3889 2:5ed5505e9f1c
3890 3:8528aa5637f2
3890 3:8528aa5637f2
3891 4:2326846efdab
3891 4:2326846efdab
3892 5:904fa392b941
3892 5:904fa392b941
3893 6:e0cc66ef77e8
3893 6:e0cc66ef77e8
3894 7:013af1973af4
3894 7:013af1973af4
3895 8:d5d0dcbdc4d9
3895 8:d5d0dcbdc4d9
3896 9:24286f4ae135
3896 9:24286f4ae135
3897
3897
3898 $ hg log -qr :e --config revsetalias.e="9"
3898 $ hg log -qr :e --config revsetalias.e="9"
3899 0:2785f51eece5
3899 0:2785f51eece5
3900 1:d75937da8da0
3900 1:d75937da8da0
3901 2:5ed5505e9f1c
3901 2:5ed5505e9f1c
3902 3:8528aa5637f2
3902 3:8528aa5637f2
3903 4:2326846efdab
3903 4:2326846efdab
3904 5:904fa392b941
3904 5:904fa392b941
3905 6:e0cc66ef77e8
3905 6:e0cc66ef77e8
3906 7:013af1973af4
3906 7:013af1973af4
3907 8:d5d0dcbdc4d9
3907 8:d5d0dcbdc4d9
3908 9:24286f4ae135
3908 9:24286f4ae135
3909
3909
3910 $ hg log -qr e:
3910 $ hg log -qr e:
3911 6:e0cc66ef77e8
3911 6:e0cc66ef77e8
3912 7:013af1973af4
3912 7:013af1973af4
3913 8:d5d0dcbdc4d9
3913 8:d5d0dcbdc4d9
3914 9:24286f4ae135
3914 9:24286f4ae135
3915
3915
3916 $ hg log -qr :e
3916 $ hg log -qr :e
3917 0:2785f51eece5
3917 0:2785f51eece5
3918 1:d75937da8da0
3918 1:d75937da8da0
3919 2:5ed5505e9f1c
3919 2:5ed5505e9f1c
3920 3:8528aa5637f2
3920 3:8528aa5637f2
3921 4:2326846efdab
3921 4:2326846efdab
3922 5:904fa392b941
3922 5:904fa392b941
3923 6:e0cc66ef77e8
3923 6:e0cc66ef77e8
3924
3924
3925 issue2549 - correct optimizations
3925 issue2549 - correct optimizations
3926
3926
3927 $ try 'limit(1 or 2 or 3, 2) and not 2'
3927 $ try 'limit(1 or 2 or 3, 2) and not 2'
3928 (and
3928 (and
3929 (func
3929 (func
3930 ('symbol', 'limit')
3930 ('symbol', 'limit')
3931 (list
3931 (list
3932 (or
3932 (or
3933 (list
3933 (list
3934 ('symbol', '1')
3934 ('symbol', '1')
3935 ('symbol', '2')
3935 ('symbol', '2')
3936 ('symbol', '3')))
3936 ('symbol', '3')))
3937 ('symbol', '2')))
3937 ('symbol', '2')))
3938 (not
3938 (not
3939 ('symbol', '2')))
3939 ('symbol', '2')))
3940 * set:
3940 * set:
3941 <filteredset
3941 <filteredset
3942 <baseset [1, 2]>,
3942 <baseset [1, 2]>,
3943 <not
3943 <not
3944 <baseset [2]>>>
3944 <baseset [2]>>>
3945 1
3945 1
3946 $ try 'max(1 or 2) and not 2'
3946 $ try 'max(1 or 2) and not 2'
3947 (and
3947 (and
3948 (func
3948 (func
3949 ('symbol', 'max')
3949 ('symbol', 'max')
3950 (or
3950 (or
3951 (list
3951 (list
3952 ('symbol', '1')
3952 ('symbol', '1')
3953 ('symbol', '2'))))
3953 ('symbol', '2'))))
3954 (not
3954 (not
3955 ('symbol', '2')))
3955 ('symbol', '2')))
3956 * set:
3956 * set:
3957 <filteredset
3957 <filteredset
3958 <baseset
3958 <baseset
3959 <max
3959 <max
3960 <fullreposet+ 0:10>,
3960 <fullreposet+ 0:10>,
3961 <baseset [1, 2]>>>,
3961 <baseset [1, 2]>>>,
3962 <not
3962 <not
3963 <baseset [2]>>>
3963 <baseset [2]>>>
3964 $ try 'min(1 or 2) and not 1'
3964 $ try 'min(1 or 2) and not 1'
3965 (and
3965 (and
3966 (func
3966 (func
3967 ('symbol', 'min')
3967 ('symbol', 'min')
3968 (or
3968 (or
3969 (list
3969 (list
3970 ('symbol', '1')
3970 ('symbol', '1')
3971 ('symbol', '2'))))
3971 ('symbol', '2'))))
3972 (not
3972 (not
3973 ('symbol', '1')))
3973 ('symbol', '1')))
3974 * set:
3974 * set:
3975 <filteredset
3975 <filteredset
3976 <baseset
3976 <baseset
3977 <min
3977 <min
3978 <fullreposet+ 0:10>,
3978 <fullreposet+ 0:10>,
3979 <baseset [1, 2]>>>,
3979 <baseset [1, 2]>>>,
3980 <not
3980 <not
3981 <baseset [1]>>>
3981 <baseset [1]>>>
3982 $ try 'last(1 or 2, 1) and not 2'
3982 $ try 'last(1 or 2, 1) and not 2'
3983 (and
3983 (and
3984 (func
3984 (func
3985 ('symbol', 'last')
3985 ('symbol', 'last')
3986 (list
3986 (list
3987 (or
3987 (or
3988 (list
3988 (list
3989 ('symbol', '1')
3989 ('symbol', '1')
3990 ('symbol', '2')))
3990 ('symbol', '2')))
3991 ('symbol', '1')))
3991 ('symbol', '1')))
3992 (not
3992 (not
3993 ('symbol', '2')))
3993 ('symbol', '2')))
3994 * set:
3994 * set:
3995 <filteredset
3995 <filteredset
3996 <baseset [2]>,
3996 <baseset [2]>,
3997 <not
3997 <not
3998 <baseset [2]>>>
3998 <baseset [2]>>>
3999
3999
4000 issue4289 - ordering of built-ins
4000 issue4289 - ordering of built-ins
4001 $ hg log -M -q -r 3:2
4001 $ hg log -M -q -r 3:2
4002 3:8528aa5637f2
4002 3:8528aa5637f2
4003 2:5ed5505e9f1c
4003 2:5ed5505e9f1c
4004
4004
4005 test revsets started with 40-chars hash (issue3669)
4005 test revsets started with 40-chars hash (issue3669)
4006
4006
4007 $ ISSUE3669_TIP=`hg tip --template '{node}'`
4007 $ ISSUE3669_TIP=`hg tip --template '{node}'`
4008 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
4008 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
4009 9
4009 9
4010 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
4010 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
4011 8
4011 8
4012
4012
4013 test or-ed indirect predicates (issue3775)
4013 test or-ed indirect predicates (issue3775)
4014
4014
4015 $ log '6 or 6^1' | sort
4015 $ log '6 or 6^1' | sort
4016 5
4016 5
4017 6
4017 6
4018 $ log '6^1 or 6' | sort
4018 $ log '6^1 or 6' | sort
4019 5
4019 5
4020 6
4020 6
4021 $ log '4 or 4~1' | sort
4021 $ log '4 or 4~1' | sort
4022 2
4022 2
4023 4
4023 4
4024 $ log '4~1 or 4' | sort
4024 $ log '4~1 or 4' | sort
4025 2
4025 2
4026 4
4026 4
4027 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
4027 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
4028 0
4028 0
4029 1
4029 1
4030 2
4030 2
4031 3
4031 3
4032 4
4032 4
4033 5
4033 5
4034 6
4034 6
4035 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
4035 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
4036 0
4036 0
4037 1
4037 1
4038 2
4038 2
4039 3
4039 3
4040 4
4040 4
4041 5
4041 5
4042 6
4042 6
4043
4043
4044 tests for 'remote()' predicate:
4044 tests for 'remote()' predicate:
4045 #. (csets in remote) (id) (remote)
4045 #. (csets in remote) (id) (remote)
4046 1. less than local current branch "default"
4046 1. less than local current branch "default"
4047 2. same with local specified "default"
4047 2. same with local specified "default"
4048 3. more than local specified specified
4048 3. more than local specified specified
4049
4049
4050 $ hg clone --quiet -U . ../remote3
4050 $ hg clone --quiet -U . ../remote3
4051 $ cd ../remote3
4051 $ cd ../remote3
4052 $ hg update -q 7
4052 $ hg update -q 7
4053 $ echo r > r
4053 $ echo r > r
4054 $ hg ci -Aqm 10
4054 $ hg ci -Aqm 10
4055 $ log 'remote()'
4055 $ log 'remote()'
4056 7
4056 7
4057 $ log 'remote("a-b-c-")'
4057 $ log 'remote("a-b-c-")'
4058 2
4058 2
4059 $ cd ../repo
4059 $ cd ../repo
4060 $ log 'remote(".a.b.c.", "../remote3")'
4060 $ log 'remote(".a.b.c.", "../remote3")'
4061
4061
4062 tests for concatenation of strings/symbols by "##"
4062 tests for concatenation of strings/symbols by "##"
4063
4063
4064 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
4064 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
4065 (_concat
4065 (_concat
4066 (_concat
4066 (_concat
4067 (_concat
4067 (_concat
4068 ('symbol', '278')
4068 ('symbol', '278')
4069 ('string', '5f5'))
4069 ('string', '5f5'))
4070 ('symbol', '1ee'))
4070 ('symbol', '1ee'))
4071 ('string', 'ce5'))
4071 ('string', 'ce5'))
4072 * concatenated:
4072 * concatenated:
4073 ('string', '2785f51eece5')
4073 ('string', '2785f51eece5')
4074 * set:
4074 * set:
4075 <baseset [0]>
4075 <baseset [0]>
4076 0
4076 0
4077
4077
4078 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
4078 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
4079 $ try "cat4(278, '5f5', 1ee, 'ce5')"
4079 $ try "cat4(278, '5f5', 1ee, 'ce5')"
4080 (func
4080 (func
4081 ('symbol', 'cat4')
4081 ('symbol', 'cat4')
4082 (list
4082 (list
4083 ('symbol', '278')
4083 ('symbol', '278')
4084 ('string', '5f5')
4084 ('string', '5f5')
4085 ('symbol', '1ee')
4085 ('symbol', '1ee')
4086 ('string', 'ce5')))
4086 ('string', 'ce5')))
4087 * expanded:
4087 * expanded:
4088 (_concat
4088 (_concat
4089 (_concat
4089 (_concat
4090 (_concat
4090 (_concat
4091 ('symbol', '278')
4091 ('symbol', '278')
4092 ('string', '5f5'))
4092 ('string', '5f5'))
4093 ('symbol', '1ee'))
4093 ('symbol', '1ee'))
4094 ('string', 'ce5'))
4094 ('string', 'ce5'))
4095 * concatenated:
4095 * concatenated:
4096 ('string', '2785f51eece5')
4096 ('string', '2785f51eece5')
4097 * set:
4097 * set:
4098 <baseset [0]>
4098 <baseset [0]>
4099 0
4099 0
4100
4100
4101 (check concatenation in alias nesting)
4101 (check concatenation in alias nesting)
4102
4102
4103 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
4103 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
4104 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
4104 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
4105 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
4105 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
4106 0
4106 0
4107
4107
4108 (check operator priority)
4108 (check operator priority)
4109
4109
4110 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
4110 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
4111 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
4111 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
4112 0
4112 0
4113 4
4113 4
4114
4114
4115 $ cd ..
4115 $ cd ..
4116
4116
4117 prepare repository that has "default" branches of multiple roots
4117 prepare repository that has "default" branches of multiple roots
4118
4118
4119 $ hg init namedbranch
4119 $ hg init namedbranch
4120 $ cd namedbranch
4120 $ cd namedbranch
4121
4121
4122 $ echo default0 >> a
4122 $ echo default0 >> a
4123 $ hg ci -Aqm0
4123 $ hg ci -Aqm0
4124 $ echo default1 >> a
4124 $ echo default1 >> a
4125 $ hg ci -m1
4125 $ hg ci -m1
4126
4126
4127 $ hg branch -q stable
4127 $ hg branch -q stable
4128 $ echo stable2 >> a
4128 $ echo stable2 >> a
4129 $ hg ci -m2
4129 $ hg ci -m2
4130 $ echo stable3 >> a
4130 $ echo stable3 >> a
4131 $ hg ci -m3
4131 $ hg ci -m3
4132
4132
4133 $ hg update -q null
4133 $ hg update -q null
4134 $ echo default4 >> a
4134 $ echo default4 >> a
4135 $ hg ci -Aqm4
4135 $ hg ci -Aqm4
4136 $ echo default5 >> a
4136 $ echo default5 >> a
4137 $ hg ci -m5
4137 $ hg ci -m5
4138
4138
4139 "null" revision belongs to "default" branch (issue4683)
4139 "null" revision belongs to "default" branch (issue4683)
4140
4140
4141 $ log 'branch(null)'
4141 $ log 'branch(null)'
4142 0
4142 0
4143 1
4143 1
4144 4
4144 4
4145 5
4145 5
4146
4146
4147 "null" revision belongs to "default" branch, but it shouldn't appear in set
4147 "null" revision belongs to "default" branch, but it shouldn't appear in set
4148 unless explicitly specified (issue4682)
4148 unless explicitly specified (issue4682)
4149
4149
4150 $ log 'children(branch(default))'
4150 $ log 'children(branch(default))'
4151 1
4151 1
4152 2
4152 2
4153 5
4153 5
4154
4154
4155 $ cd ..
4155 $ cd ..
4156
4156
4157 test author/desc/keyword in problematic encoding
4157 test author/desc/keyword in problematic encoding
4158 # unicode: cp932:
4158 # unicode: cp932:
4159 # u30A2 0x83 0x41(= 'A')
4159 # u30A2 0x83 0x41(= 'A')
4160 # u30C2 0x83 0x61(= 'a')
4160 # u30C2 0x83 0x61(= 'a')
4161
4161
4162 $ hg init problematicencoding
4162 $ hg init problematicencoding
4163 $ cd problematicencoding
4163 $ cd problematicencoding
4164
4164
4165 $ $PYTHON > setup.sh <<EOF
4165 $ $PYTHON > setup.sh <<EOF
4166 > print u'''
4166 > print u'''
4167 > echo a > text
4167 > echo a > text
4168 > hg add text
4168 > hg add text
4169 > hg --encoding utf-8 commit -u '\u30A2' -m none
4169 > hg --encoding utf-8 commit -u '\u30A2' -m none
4170 > echo b > text
4170 > echo b > text
4171 > hg --encoding utf-8 commit -u '\u30C2' -m none
4171 > hg --encoding utf-8 commit -u '\u30C2' -m none
4172 > echo c > text
4172 > echo c > text
4173 > hg --encoding utf-8 commit -u none -m '\u30A2'
4173 > hg --encoding utf-8 commit -u none -m '\u30A2'
4174 > echo d > text
4174 > echo d > text
4175 > hg --encoding utf-8 commit -u none -m '\u30C2'
4175 > hg --encoding utf-8 commit -u none -m '\u30C2'
4176 > '''.encode('utf-8')
4176 > '''.encode('utf-8')
4177 > EOF
4177 > EOF
4178 $ sh < setup.sh
4178 $ sh < setup.sh
4179
4179
4180 test in problematic encoding
4180 test in problematic encoding
4181 $ $PYTHON > test.sh <<EOF
4181 $ $PYTHON > test.sh <<EOF
4182 > print u'''
4182 > print u'''
4183 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
4183 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
4184 > echo ====
4184 > echo ====
4185 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
4185 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
4186 > echo ====
4186 > echo ====
4187 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
4187 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
4188 > echo ====
4188 > echo ====
4189 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
4189 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
4190 > echo ====
4190 > echo ====
4191 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
4191 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
4192 > echo ====
4192 > echo ====
4193 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
4193 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
4194 > '''.encode('cp932')
4194 > '''.encode('cp932')
4195 > EOF
4195 > EOF
4196 $ sh < test.sh
4196 $ sh < test.sh
4197 0
4197 0
4198 ====
4198 ====
4199 1
4199 1
4200 ====
4200 ====
4201 2
4201 2
4202 ====
4202 ====
4203 3
4203 3
4204 ====
4204 ====
4205 0
4205 0
4206 2
4206 2
4207 ====
4207 ====
4208 1
4208 1
4209 3
4209 3
4210
4210
4211 test error message of bad revset
4211 test error message of bad revset
4212 $ hg log -r 'foo\\'
4212 $ hg log -r 'foo\\'
4213 hg: parse error at 3: syntax error in revset 'foo\\'
4213 hg: parse error at 3: syntax error in revset 'foo\\'
4214 [255]
4214 [255]
4215
4215
4216 $ cd ..
4216 $ cd ..
4217
4217
4218 Test that revset predicate of extension isn't loaded at failure of
4218 Test that revset predicate of extension isn't loaded at failure of
4219 loading it
4219 loading it
4220
4220
4221 $ cd repo
4221 $ cd repo
4222
4222
4223 $ cat <<EOF > $TESTTMP/custompredicate.py
4223 $ cat <<EOF > $TESTTMP/custompredicate.py
4224 > from mercurial import error, registrar, revset
4224 > from mercurial import error, registrar, revset
4225 >
4225 >
4226 > revsetpredicate = registrar.revsetpredicate()
4226 > revsetpredicate = registrar.revsetpredicate()
4227 >
4227 >
4228 > @revsetpredicate('custom1()')
4228 > @revsetpredicate('custom1()')
4229 > def custom1(repo, subset, x):
4229 > def custom1(repo, subset, x):
4230 > return revset.baseset([1])
4230 > return revset.baseset([1])
4231 >
4231 >
4232 > raise error.Abort('intentional failure of loading extension')
4232 > raise error.Abort('intentional failure of loading extension')
4233 > EOF
4233 > EOF
4234 $ cat <<EOF > .hg/hgrc
4234 $ cat <<EOF > .hg/hgrc
4235 > [extensions]
4235 > [extensions]
4236 > custompredicate = $TESTTMP/custompredicate.py
4236 > custompredicate = $TESTTMP/custompredicate.py
4237 > EOF
4237 > EOF
4238
4238
4239 $ hg debugrevspec "custom1()"
4239 $ hg debugrevspec "custom1()"
4240 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
4240 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
4241 hg: parse error: unknown identifier: custom1
4241 hg: parse error: unknown identifier: custom1
4242 [255]
4242 [255]
4243
4243
4244 Test repo.anyrevs with customized revset overrides
4244 Test repo.anyrevs with customized revset overrides
4245
4245
4246 $ cat > $TESTTMP/printprevset.py <<EOF
4246 $ cat > $TESTTMP/printprevset.py <<EOF
4247 > from mercurial import encoding, registrar
4247 > from mercurial import encoding, registrar
4248 > cmdtable = {}
4248 > cmdtable = {}
4249 > command = registrar.command(cmdtable)
4249 > command = registrar.command(cmdtable)
4250 > @command('printprevset')
4250 > @command('printprevset')
4251 > def printprevset(ui, repo):
4251 > def printprevset(ui, repo):
4252 > alias = {}
4252 > alias = {}
4253 > p = encoding.environ.get('P')
4253 > p = encoding.environ.get('P')
4254 > if p:
4254 > if p:
4255 > alias['P'] = p
4255 > alias['P'] = p
4256 > revs = repo.anyrevs(['P'], user=True, localalias=alias)
4256 > revs = repo.anyrevs(['P'], user=True, localalias=alias)
4257 > ui.write('P=%r\n' % list(revs))
4257 > ui.write('P=%r\n' % list(revs))
4258 > EOF
4258 > EOF
4259
4259
4260 $ cat >> .hg/hgrc <<EOF
4260 $ cat >> .hg/hgrc <<EOF
4261 > custompredicate = !
4261 > custompredicate = !
4262 > printprevset = $TESTTMP/printprevset.py
4262 > printprevset = $TESTTMP/printprevset.py
4263 > EOF
4263 > EOF
4264
4264
4265 $ hg --config revsetalias.P=1 printprevset
4265 $ hg --config revsetalias.P=1 printprevset
4266 P=[1]
4266 P=[1]
4267 $ P=3 hg --config revsetalias.P=2 printprevset
4267 $ P=3 hg --config revsetalias.P=2 printprevset
4268 P=[3]
4268 P=[3]
4269
4269
4270 $ cd ..
4270 $ cd ..
4271
4271
4272 Test obsstore related revsets
4272 Test obsstore related revsets
4273
4273
4274 $ hg init repo1
4274 $ hg init repo1
4275 $ cd repo1
4275 $ cd repo1
4276 $ cat <<EOF >> .hg/hgrc
4276 $ cat <<EOF >> .hg/hgrc
4277 > [experimental]
4277 > [experimental]
4278 > stabilization = createmarkers
4278 > stabilization = createmarkers
4279 > EOF
4279 > EOF
4280
4280
4281 $ hg debugdrawdag <<'EOS'
4281 $ hg debugdrawdag <<'EOS'
4282 > F G
4282 > F G
4283 > |/ # split: B -> E, F
4283 > |/ # split: B -> E, F
4284 > B C D E # amend: B -> C -> D
4284 > B C D E # amend: B -> C -> D
4285 > \|/ | # amend: F -> G
4285 > \|/ | # amend: F -> G
4286 > A A Z # amend: A -> Z
4286 > A A Z # amend: A -> Z
4287 > EOS
4287 > EOS
4288
4288
4289 $ hg log -r 'successors(Z)' -T '{desc}\n'
4289 $ hg log -r 'successors(Z)' -T '{desc}\n'
4290 Z
4290 Z
4291
4291
4292 $ hg log -r 'successors(F)' -T '{desc}\n'
4292 $ hg log -r 'successors(F)' -T '{desc}\n'
4293 F
4293 F
4294 G
4294 G
4295
4295
4296 $ hg tag --remove --local C D E F G
4296 $ hg tag --remove --local C D E F G
4297
4297
4298 $ hg log -r 'successors(B)' -T '{desc}\n'
4298 $ hg log -r 'successors(B)' -T '{desc}\n'
4299 B
4299 B
4300 D
4300 D
4301 E
4301 E
4302 G
4302 G
4303
4303
4304 $ hg log -r 'successors(B)' -T '{desc}\n' --hidden
4304 $ hg log -r 'successors(B)' -T '{desc}\n' --hidden
4305 B
4305 B
4306 C
4306 C
4307 D
4307 D
4308 E
4308 E
4309 F
4309 F
4310 G
4310 G
4311
4311
4312 $ hg log -r 'successors(B)-obsolete()' -T '{desc}\n' --hidden
4312 $ hg log -r 'successors(B)-obsolete()' -T '{desc}\n' --hidden
4313 D
4313 D
4314 E
4314 E
4315 G
4315 G
4316
4316
4317 $ hg log -r 'successors(B+A)-contentdivergent()' -T '{desc}\n'
4317 $ hg log -r 'successors(B+A)-contentdivergent()' -T '{desc}\n'
4318 A
4318 A
4319 Z
4319 Z
4320 B
4320 B
4321
4321
4322 $ hg log -r 'successors(B+A)-contentdivergent()-obsolete()' -T '{desc}\n'
4322 $ hg log -r 'successors(B+A)-contentdivergent()-obsolete()' -T '{desc}\n'
4323 Z
4323 Z
4324
4325 Test `draft() & ::x` optimization
4326
4327 $ hg init $TESTTMP/repo2
4328 $ cd $TESTTMP/repo2
4329 $ hg debugdrawdag <<'EOS'
4330 > P5 S1
4331 > | |
4332 > S2 | D3
4333 > \|/
4334 > P4
4335 > |
4336 > P3 D2
4337 > | |
4338 > P2 D1
4339 > |/
4340 > P1
4341 > |
4342 > P0
4343 > EOS
4344 $ hg phase --public -r P5
4345 $ hg phase --force --secret -r S1+S2
4346 $ hg log -G -T '{rev} {desc} {phase}' -r 'sort(all(), topo, topo.firstbranch=P5)'
4347 o 8 P5 public
4348 |
4349 | o 10 S1 secret
4350 | |
4351 | o 7 D3 draft
4352 |/
4353 | o 9 S2 secret
4354 |/
4355 o 6 P4 public
4356 |
4357 o 5 P3 public
4358 |
4359 o 3 P2 public
4360 |
4361 | o 4 D2 draft
4362 | |
4363 | o 2 D1 draft
4364 |/
4365 o 1 P1 public
4366 |
4367 o 0 P0 public
4368
4369 $ hg debugrevspec --verify -p analyzed -p optimized 'draft() & ::(((S1+D1+P5)-D3)+S2)'
4370 * analyzed:
4371 (and
4372 (func
4373 ('symbol', 'draft')
4374 None)
4375 (func
4376 ('symbol', 'ancestors')
4377 (or
4378 (list
4379 (and
4380 (or
4381 (list
4382 ('symbol', 'S1')
4383 ('symbol', 'D1')
4384 ('symbol', 'P5')))
4385 (not
4386 ('symbol', 'D3')))
4387 ('symbol', 'S2')))))
4388 * optimized:
4389 (func
4390 ('symbol', '_phaseandancestors')
4391 (list
4392 ('symbol', 'draft')
4393 (or
4394 (list
4395 (difference
4396 (func
4397 ('symbol', '_list')
4398 ('string', 'S1\x00D1\x00P5'))
4399 ('symbol', 'D3'))
4400 ('symbol', 'S2')))))
4401 $ hg debugrevspec --verify -p analyzed -p optimized 'secret() & ::9'
4402 * analyzed:
4403 (and
4404 (func
4405 ('symbol', 'secret')
4406 None)
4407 (func
4408 ('symbol', 'ancestors')
4409 ('symbol', '9')))
4410 * optimized:
4411 (func
4412 ('symbol', '_phaseandancestors')
4413 (list
4414 ('symbol', 'secret')
4415 ('symbol', '9')))
4416 $ hg debugrevspec --verify -p analyzed -p optimized '7 & ( (not public()) & ::(tag()) )'
4417 * analyzed:
4418 (and
4419 ('symbol', '7')
4420 (and
4421 (not
4422 (func
4423 ('symbol', 'public')
4424 None))
4425 (func
4426 ('symbol', 'ancestors')
4427 (func
4428 ('symbol', 'tag')
4429 None))))
4430 * optimized:
4431 (and
4432 ('symbol', '7')
4433 (func
4434 ('symbol', '_phaseandancestors')
4435 (list
4436 ('symbol', '_notpublic')
4437 (func
4438 ('symbol', 'tag')
4439 None))))
4440 $ hg debugrevspec --verify -p optimized '(not public()) & ancestors(S1+D2+P5, 1)'
4441 * optimized:
4442 (and
4443 (func
4444 ('symbol', '_notpublic')
4445 None)
4446 (func
4447 ('symbol', 'ancestors')
4448 (list
4449 (func
4450 ('symbol', '_list')
4451 ('string', 'S1\x00D2\x00P5'))
4452 ('symbol', '1'))))
4453 $ hg debugrevspec --verify -p optimized '(not public()) & ancestors(S1+D2+P5, depth=1)'
4454 * optimized:
4455 (and
4456 (func
4457 ('symbol', '_notpublic')
4458 None)
4459 (func
4460 ('symbol', 'ancestors')
4461 (list
4462 (func
4463 ('symbol', '_list')
4464 ('string', 'S1\x00D2\x00P5'))
4465 (keyvalue
4466 ('symbol', 'depth')
4467 ('symbol', '1')))))
General Comments 0
You need to be logged in to leave comments. Login now