##// END OF EJS Templates
revset: add a bumped revset...
Pierre-Yves David -
r17829:c73f7a28 default
parent child Browse files
Show More
@@ -1,1885 +1,1897 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 import re
8 import re
9 import parser, util, error, discovery, hbisect, phases
9 import parser, util, error, discovery, hbisect, phases
10 import node
10 import node
11 import bookmarks as bookmarksmod
11 import bookmarks as bookmarksmod
12 import match as matchmod
12 import match as matchmod
13 from i18n import _
13 from i18n import _
14 import encoding
14 import encoding
15 import obsolete as obsmod
15 import obsolete as obsmod
16
16
17 def _revancestors(repo, revs, followfirst):
17 def _revancestors(repo, revs, followfirst):
18 """Like revlog.ancestors(), but supports followfirst."""
18 """Like revlog.ancestors(), but supports followfirst."""
19 cut = followfirst and 1 or None
19 cut = followfirst and 1 or None
20 cl = repo.changelog
20 cl = repo.changelog
21 visit = util.deque(revs)
21 visit = util.deque(revs)
22 seen = set([node.nullrev])
22 seen = set([node.nullrev])
23 while visit:
23 while visit:
24 for parent in cl.parentrevs(visit.popleft())[:cut]:
24 for parent in cl.parentrevs(visit.popleft())[:cut]:
25 if parent not in seen:
25 if parent not in seen:
26 visit.append(parent)
26 visit.append(parent)
27 seen.add(parent)
27 seen.add(parent)
28 yield parent
28 yield parent
29
29
30 def _revdescendants(repo, revs, followfirst):
30 def _revdescendants(repo, revs, followfirst):
31 """Like revlog.descendants() but supports followfirst."""
31 """Like revlog.descendants() but supports followfirst."""
32 cut = followfirst and 1 or None
32 cut = followfirst and 1 or None
33 cl = repo.changelog
33 cl = repo.changelog
34 first = min(revs)
34 first = min(revs)
35 nullrev = node.nullrev
35 nullrev = node.nullrev
36 if first == nullrev:
36 if first == nullrev:
37 # Are there nodes with a null first parent and a non-null
37 # Are there nodes with a null first parent and a non-null
38 # second one? Maybe. Do we care? Probably not.
38 # second one? Maybe. Do we care? Probably not.
39 for i in cl:
39 for i in cl:
40 yield i
40 yield i
41 return
41 return
42
42
43 seen = set(revs)
43 seen = set(revs)
44 for i in cl.revs(first + 1):
44 for i in cl.revs(first + 1):
45 for x in cl.parentrevs(i)[:cut]:
45 for x in cl.parentrevs(i)[:cut]:
46 if x != nullrev and x in seen:
46 if x != nullrev and x in seen:
47 seen.add(i)
47 seen.add(i)
48 yield i
48 yield i
49 break
49 break
50
50
51 def _revsbetween(repo, roots, heads):
51 def _revsbetween(repo, roots, heads):
52 """Return all paths between roots and heads, inclusive of both endpoint
52 """Return all paths between roots and heads, inclusive of both endpoint
53 sets."""
53 sets."""
54 if not roots:
54 if not roots:
55 return []
55 return []
56 parentrevs = repo.changelog.parentrevs
56 parentrevs = repo.changelog.parentrevs
57 visit = heads[:]
57 visit = heads[:]
58 reachable = set()
58 reachable = set()
59 seen = {}
59 seen = {}
60 minroot = min(roots)
60 minroot = min(roots)
61 roots = set(roots)
61 roots = set(roots)
62 # open-code the post-order traversal due to the tiny size of
62 # open-code the post-order traversal due to the tiny size of
63 # sys.getrecursionlimit()
63 # sys.getrecursionlimit()
64 while visit:
64 while visit:
65 rev = visit.pop()
65 rev = visit.pop()
66 if rev in roots:
66 if rev in roots:
67 reachable.add(rev)
67 reachable.add(rev)
68 parents = parentrevs(rev)
68 parents = parentrevs(rev)
69 seen[rev] = parents
69 seen[rev] = parents
70 for parent in parents:
70 for parent in parents:
71 if parent >= minroot and parent not in seen:
71 if parent >= minroot and parent not in seen:
72 visit.append(parent)
72 visit.append(parent)
73 if not reachable:
73 if not reachable:
74 return []
74 return []
75 for rev in sorted(seen):
75 for rev in sorted(seen):
76 for parent in seen[rev]:
76 for parent in seen[rev]:
77 if parent in reachable:
77 if parent in reachable:
78 reachable.add(rev)
78 reachable.add(rev)
79 return sorted(reachable)
79 return sorted(reachable)
80
80
81 elements = {
81 elements = {
82 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
82 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
83 "~": (18, None, ("ancestor", 18)),
83 "~": (18, None, ("ancestor", 18)),
84 "^": (18, None, ("parent", 18), ("parentpost", 18)),
84 "^": (18, None, ("parent", 18), ("parentpost", 18)),
85 "-": (5, ("negate", 19), ("minus", 5)),
85 "-": (5, ("negate", 19), ("minus", 5)),
86 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
86 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
87 ("dagrangepost", 17)),
87 ("dagrangepost", 17)),
88 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
88 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
89 ("dagrangepost", 17)),
89 ("dagrangepost", 17)),
90 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
90 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
91 "not": (10, ("not", 10)),
91 "not": (10, ("not", 10)),
92 "!": (10, ("not", 10)),
92 "!": (10, ("not", 10)),
93 "and": (5, None, ("and", 5)),
93 "and": (5, None, ("and", 5)),
94 "&": (5, None, ("and", 5)),
94 "&": (5, None, ("and", 5)),
95 "or": (4, None, ("or", 4)),
95 "or": (4, None, ("or", 4)),
96 "|": (4, None, ("or", 4)),
96 "|": (4, None, ("or", 4)),
97 "+": (4, None, ("or", 4)),
97 "+": (4, None, ("or", 4)),
98 ",": (2, None, ("list", 2)),
98 ",": (2, None, ("list", 2)),
99 ")": (0, None, None),
99 ")": (0, None, None),
100 "symbol": (0, ("symbol",), None),
100 "symbol": (0, ("symbol",), None),
101 "string": (0, ("string",), None),
101 "string": (0, ("string",), None),
102 "end": (0, None, None),
102 "end": (0, None, None),
103 }
103 }
104
104
105 keywords = set(['and', 'or', 'not'])
105 keywords = set(['and', 'or', 'not'])
106
106
107 def tokenize(program):
107 def tokenize(program):
108 pos, l = 0, len(program)
108 pos, l = 0, len(program)
109 while pos < l:
109 while pos < l:
110 c = program[pos]
110 c = program[pos]
111 if c.isspace(): # skip inter-token whitespace
111 if c.isspace(): # skip inter-token whitespace
112 pass
112 pass
113 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
113 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
114 yield ('::', None, pos)
114 yield ('::', None, pos)
115 pos += 1 # skip ahead
115 pos += 1 # skip ahead
116 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
116 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
117 yield ('..', None, pos)
117 yield ('..', None, pos)
118 pos += 1 # skip ahead
118 pos += 1 # skip ahead
119 elif c in "():,-|&+!~^": # handle simple operators
119 elif c in "():,-|&+!~^": # handle simple operators
120 yield (c, None, pos)
120 yield (c, None, pos)
121 elif (c in '"\'' or c == 'r' and
121 elif (c in '"\'' or c == 'r' and
122 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
122 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
123 if c == 'r':
123 if c == 'r':
124 pos += 1
124 pos += 1
125 c = program[pos]
125 c = program[pos]
126 decode = lambda x: x
126 decode = lambda x: x
127 else:
127 else:
128 decode = lambda x: x.decode('string-escape')
128 decode = lambda x: x.decode('string-escape')
129 pos += 1
129 pos += 1
130 s = pos
130 s = pos
131 while pos < l: # find closing quote
131 while pos < l: # find closing quote
132 d = program[pos]
132 d = program[pos]
133 if d == '\\': # skip over escaped characters
133 if d == '\\': # skip over escaped characters
134 pos += 2
134 pos += 2
135 continue
135 continue
136 if d == c:
136 if d == c:
137 yield ('string', decode(program[s:pos]), s)
137 yield ('string', decode(program[s:pos]), s)
138 break
138 break
139 pos += 1
139 pos += 1
140 else:
140 else:
141 raise error.ParseError(_("unterminated string"), s)
141 raise error.ParseError(_("unterminated string"), s)
142 # gather up a symbol/keyword
142 # gather up a symbol/keyword
143 elif c.isalnum() or c in '._' or ord(c) > 127:
143 elif c.isalnum() or c in '._' or ord(c) > 127:
144 s = pos
144 s = pos
145 pos += 1
145 pos += 1
146 while pos < l: # find end of symbol
146 while pos < l: # find end of symbol
147 d = program[pos]
147 d = program[pos]
148 if not (d.isalnum() or d in "._/" or ord(d) > 127):
148 if not (d.isalnum() or d in "._/" or ord(d) > 127):
149 break
149 break
150 if d == '.' and program[pos - 1] == '.': # special case for ..
150 if d == '.' and program[pos - 1] == '.': # special case for ..
151 pos -= 1
151 pos -= 1
152 break
152 break
153 pos += 1
153 pos += 1
154 sym = program[s:pos]
154 sym = program[s:pos]
155 if sym in keywords: # operator keywords
155 if sym in keywords: # operator keywords
156 yield (sym, None, s)
156 yield (sym, None, s)
157 else:
157 else:
158 yield ('symbol', sym, s)
158 yield ('symbol', sym, s)
159 pos -= 1
159 pos -= 1
160 else:
160 else:
161 raise error.ParseError(_("syntax error"), pos)
161 raise error.ParseError(_("syntax error"), pos)
162 pos += 1
162 pos += 1
163 yield ('end', None, pos)
163 yield ('end', None, pos)
164
164
165 # helpers
165 # helpers
166
166
167 def getstring(x, err):
167 def getstring(x, err):
168 if x and (x[0] == 'string' or x[0] == 'symbol'):
168 if x and (x[0] == 'string' or x[0] == 'symbol'):
169 return x[1]
169 return x[1]
170 raise error.ParseError(err)
170 raise error.ParseError(err)
171
171
172 def getlist(x):
172 def getlist(x):
173 if not x:
173 if not x:
174 return []
174 return []
175 if x[0] == 'list':
175 if x[0] == 'list':
176 return getlist(x[1]) + [x[2]]
176 return getlist(x[1]) + [x[2]]
177 return [x]
177 return [x]
178
178
179 def getargs(x, min, max, err):
179 def getargs(x, min, max, err):
180 l = getlist(x)
180 l = getlist(x)
181 if len(l) < min or (max >= 0 and len(l) > max):
181 if len(l) < min or (max >= 0 and len(l) > max):
182 raise error.ParseError(err)
182 raise error.ParseError(err)
183 return l
183 return l
184
184
185 def getset(repo, subset, x):
185 def getset(repo, subset, x):
186 if not x:
186 if not x:
187 raise error.ParseError(_("missing argument"))
187 raise error.ParseError(_("missing argument"))
188 return methods[x[0]](repo, subset, *x[1:])
188 return methods[x[0]](repo, subset, *x[1:])
189
189
190 def _getrevsource(repo, r):
190 def _getrevsource(repo, r):
191 extra = repo[r].extra()
191 extra = repo[r].extra()
192 for label in ('source', 'transplant_source', 'rebase_source'):
192 for label in ('source', 'transplant_source', 'rebase_source'):
193 if label in extra:
193 if label in extra:
194 try:
194 try:
195 return repo[extra[label]].rev()
195 return repo[extra[label]].rev()
196 except error.RepoLookupError:
196 except error.RepoLookupError:
197 pass
197 pass
198 return None
198 return None
199
199
200 # operator methods
200 # operator methods
201
201
202 def stringset(repo, subset, x):
202 def stringset(repo, subset, x):
203 x = repo[x].rev()
203 x = repo[x].rev()
204 if x == -1 and len(subset) == len(repo):
204 if x == -1 and len(subset) == len(repo):
205 return [-1]
205 return [-1]
206 if len(subset) == len(repo) or x in subset:
206 if len(subset) == len(repo) or x in subset:
207 return [x]
207 return [x]
208 return []
208 return []
209
209
210 def symbolset(repo, subset, x):
210 def symbolset(repo, subset, x):
211 if x in symbols:
211 if x in symbols:
212 raise error.ParseError(_("can't use %s here") % x)
212 raise error.ParseError(_("can't use %s here") % x)
213 return stringset(repo, subset, x)
213 return stringset(repo, subset, x)
214
214
215 def rangeset(repo, subset, x, y):
215 def rangeset(repo, subset, x, y):
216 m = getset(repo, subset, x)
216 m = getset(repo, subset, x)
217 if not m:
217 if not m:
218 m = getset(repo, list(repo), x)
218 m = getset(repo, list(repo), x)
219
219
220 n = getset(repo, subset, y)
220 n = getset(repo, subset, y)
221 if not n:
221 if not n:
222 n = getset(repo, list(repo), y)
222 n = getset(repo, list(repo), y)
223
223
224 if not m or not n:
224 if not m or not n:
225 return []
225 return []
226 m, n = m[0], n[-1]
226 m, n = m[0], n[-1]
227
227
228 if m < n:
228 if m < n:
229 r = range(m, n + 1)
229 r = range(m, n + 1)
230 else:
230 else:
231 r = range(m, n - 1, -1)
231 r = range(m, n - 1, -1)
232 s = set(subset)
232 s = set(subset)
233 return [x for x in r if x in s]
233 return [x for x in r if x in s]
234
234
235 def dagrange(repo, subset, x, y):
235 def dagrange(repo, subset, x, y):
236 if subset:
236 if subset:
237 r = list(repo)
237 r = list(repo)
238 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
238 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
239 s = set(subset)
239 s = set(subset)
240 return [r for r in xs if r in s]
240 return [r for r in xs if r in s]
241 return []
241 return []
242
242
243 def andset(repo, subset, x, y):
243 def andset(repo, subset, x, y):
244 return getset(repo, getset(repo, subset, x), y)
244 return getset(repo, getset(repo, subset, x), y)
245
245
246 def orset(repo, subset, x, y):
246 def orset(repo, subset, x, y):
247 xl = getset(repo, subset, x)
247 xl = getset(repo, subset, x)
248 s = set(xl)
248 s = set(xl)
249 yl = getset(repo, [r for r in subset if r not in s], y)
249 yl = getset(repo, [r for r in subset if r not in s], y)
250 return xl + yl
250 return xl + yl
251
251
252 def notset(repo, subset, x):
252 def notset(repo, subset, x):
253 s = set(getset(repo, subset, x))
253 s = set(getset(repo, subset, x))
254 return [r for r in subset if r not in s]
254 return [r for r in subset if r not in s]
255
255
256 def listset(repo, subset, a, b):
256 def listset(repo, subset, a, b):
257 raise error.ParseError(_("can't use a list in this context"))
257 raise error.ParseError(_("can't use a list in this context"))
258
258
259 def func(repo, subset, a, b):
259 def func(repo, subset, a, b):
260 if a[0] == 'symbol' and a[1] in symbols:
260 if a[0] == 'symbol' and a[1] in symbols:
261 return symbols[a[1]](repo, subset, b)
261 return symbols[a[1]](repo, subset, b)
262 raise error.ParseError(_("not a function: %s") % a[1])
262 raise error.ParseError(_("not a function: %s") % a[1])
263
263
264 # functions
264 # functions
265
265
266 def adds(repo, subset, x):
266 def adds(repo, subset, x):
267 """``adds(pattern)``
267 """``adds(pattern)``
268 Changesets that add a file matching pattern.
268 Changesets that add a file matching pattern.
269 """
269 """
270 # i18n: "adds" is a keyword
270 # i18n: "adds" is a keyword
271 pat = getstring(x, _("adds requires a pattern"))
271 pat = getstring(x, _("adds requires a pattern"))
272 return checkstatus(repo, subset, pat, 1)
272 return checkstatus(repo, subset, pat, 1)
273
273
274 def ancestor(repo, subset, x):
274 def ancestor(repo, subset, x):
275 """``ancestor(single, single)``
275 """``ancestor(single, single)``
276 Greatest common ancestor of the two changesets.
276 Greatest common ancestor of the two changesets.
277 """
277 """
278 # i18n: "ancestor" is a keyword
278 # i18n: "ancestor" is a keyword
279 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
279 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
280 r = list(repo)
280 r = list(repo)
281 a = getset(repo, r, l[0])
281 a = getset(repo, r, l[0])
282 b = getset(repo, r, l[1])
282 b = getset(repo, r, l[1])
283 if len(a) != 1 or len(b) != 1:
283 if len(a) != 1 or len(b) != 1:
284 # i18n: "ancestor" is a keyword
284 # i18n: "ancestor" is a keyword
285 raise error.ParseError(_("ancestor arguments must be single revisions"))
285 raise error.ParseError(_("ancestor arguments must be single revisions"))
286 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
286 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
287
287
288 return [r for r in an if r in subset]
288 return [r for r in an if r in subset]
289
289
290 def _ancestors(repo, subset, x, followfirst=False):
290 def _ancestors(repo, subset, x, followfirst=False):
291 args = getset(repo, list(repo), x)
291 args = getset(repo, list(repo), x)
292 if not args:
292 if not args:
293 return []
293 return []
294 s = set(_revancestors(repo, args, followfirst)) | set(args)
294 s = set(_revancestors(repo, args, followfirst)) | set(args)
295 return [r for r in subset if r in s]
295 return [r for r in subset if r in s]
296
296
297 def ancestors(repo, subset, x):
297 def ancestors(repo, subset, x):
298 """``ancestors(set)``
298 """``ancestors(set)``
299 Changesets that are ancestors of a changeset in set.
299 Changesets that are ancestors of a changeset in set.
300 """
300 """
301 return _ancestors(repo, subset, x)
301 return _ancestors(repo, subset, x)
302
302
303 def _firstancestors(repo, subset, x):
303 def _firstancestors(repo, subset, x):
304 # ``_firstancestors(set)``
304 # ``_firstancestors(set)``
305 # Like ``ancestors(set)`` but follows only the first parents.
305 # Like ``ancestors(set)`` but follows only the first parents.
306 return _ancestors(repo, subset, x, followfirst=True)
306 return _ancestors(repo, subset, x, followfirst=True)
307
307
308 def ancestorspec(repo, subset, x, n):
308 def ancestorspec(repo, subset, x, n):
309 """``set~n``
309 """``set~n``
310 Changesets that are the Nth ancestor (first parents only) of a changeset
310 Changesets that are the Nth ancestor (first parents only) of a changeset
311 in set.
311 in set.
312 """
312 """
313 try:
313 try:
314 n = int(n[1])
314 n = int(n[1])
315 except (TypeError, ValueError):
315 except (TypeError, ValueError):
316 raise error.ParseError(_("~ expects a number"))
316 raise error.ParseError(_("~ expects a number"))
317 ps = set()
317 ps = set()
318 cl = repo.changelog
318 cl = repo.changelog
319 for r in getset(repo, subset, x):
319 for r in getset(repo, subset, x):
320 for i in range(n):
320 for i in range(n):
321 r = cl.parentrevs(r)[0]
321 r = cl.parentrevs(r)[0]
322 ps.add(r)
322 ps.add(r)
323 return [r for r in subset if r in ps]
323 return [r for r in subset if r in ps]
324
324
325 def author(repo, subset, x):
325 def author(repo, subset, x):
326 """``author(string)``
326 """``author(string)``
327 Alias for ``user(string)``.
327 Alias for ``user(string)``.
328 """
328 """
329 # i18n: "author" is a keyword
329 # i18n: "author" is a keyword
330 n = encoding.lower(getstring(x, _("author requires a string")))
330 n = encoding.lower(getstring(x, _("author requires a string")))
331 kind, pattern, matcher = _substringmatcher(n)
331 kind, pattern, matcher = _substringmatcher(n)
332 return [r for r in subset if matcher(encoding.lower(repo[r].user()))]
332 return [r for r in subset if matcher(encoding.lower(repo[r].user()))]
333
333
334 def bisect(repo, subset, x):
334 def bisect(repo, subset, x):
335 """``bisect(string)``
335 """``bisect(string)``
336 Changesets marked in the specified bisect status:
336 Changesets marked in the specified bisect status:
337
337
338 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
338 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
339 - ``goods``, ``bads`` : csets topologically good/bad
339 - ``goods``, ``bads`` : csets topologically good/bad
340 - ``range`` : csets taking part in the bisection
340 - ``range`` : csets taking part in the bisection
341 - ``pruned`` : csets that are goods, bads or skipped
341 - ``pruned`` : csets that are goods, bads or skipped
342 - ``untested`` : csets whose fate is yet unknown
342 - ``untested`` : csets whose fate is yet unknown
343 - ``ignored`` : csets ignored due to DAG topology
343 - ``ignored`` : csets ignored due to DAG topology
344 - ``current`` : the cset currently being bisected
344 - ``current`` : the cset currently being bisected
345 """
345 """
346 # i18n: "bisect" is a keyword
346 # i18n: "bisect" is a keyword
347 status = getstring(x, _("bisect requires a string")).lower()
347 status = getstring(x, _("bisect requires a string")).lower()
348 state = set(hbisect.get(repo, status))
348 state = set(hbisect.get(repo, status))
349 return [r for r in subset if r in state]
349 return [r for r in subset if r in state]
350
350
351 # Backward-compatibility
351 # Backward-compatibility
352 # - no help entry so that we do not advertise it any more
352 # - no help entry so that we do not advertise it any more
353 def bisected(repo, subset, x):
353 def bisected(repo, subset, x):
354 return bisect(repo, subset, x)
354 return bisect(repo, subset, x)
355
355
356 def bookmark(repo, subset, x):
356 def bookmark(repo, subset, x):
357 """``bookmark([name])``
357 """``bookmark([name])``
358 The named bookmark or all bookmarks.
358 The named bookmark or all bookmarks.
359
359
360 If `name` starts with `re:`, the remainder of the name is treated as
360 If `name` starts with `re:`, the remainder of the name is treated as
361 a regular expression. To match a bookmark that actually starts with `re:`,
361 a regular expression. To match a bookmark that actually starts with `re:`,
362 use the prefix `literal:`.
362 use the prefix `literal:`.
363 """
363 """
364 # i18n: "bookmark" is a keyword
364 # i18n: "bookmark" is a keyword
365 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
365 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
366 if args:
366 if args:
367 bm = getstring(args[0],
367 bm = getstring(args[0],
368 # i18n: "bookmark" is a keyword
368 # i18n: "bookmark" is a keyword
369 _('the argument to bookmark must be a string'))
369 _('the argument to bookmark must be a string'))
370 kind, pattern, matcher = _stringmatcher(bm)
370 kind, pattern, matcher = _stringmatcher(bm)
371 if kind == 'literal':
371 if kind == 'literal':
372 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
372 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
373 if not bmrev:
373 if not bmrev:
374 raise util.Abort(_("bookmark '%s' does not exist") % bm)
374 raise util.Abort(_("bookmark '%s' does not exist") % bm)
375 bmrev = repo[bmrev].rev()
375 bmrev = repo[bmrev].rev()
376 return [r for r in subset if r == bmrev]
376 return [r for r in subset if r == bmrev]
377 else:
377 else:
378 matchrevs = set()
378 matchrevs = set()
379 for name, bmrev in bookmarksmod.listbookmarks(repo).iteritems():
379 for name, bmrev in bookmarksmod.listbookmarks(repo).iteritems():
380 if matcher(name):
380 if matcher(name):
381 matchrevs.add(bmrev)
381 matchrevs.add(bmrev)
382 if not matchrevs:
382 if not matchrevs:
383 raise util.Abort(_("no bookmarks exist that match '%s'")
383 raise util.Abort(_("no bookmarks exist that match '%s'")
384 % pattern)
384 % pattern)
385 bmrevs = set()
385 bmrevs = set()
386 for bmrev in matchrevs:
386 for bmrev in matchrevs:
387 bmrevs.add(repo[bmrev].rev())
387 bmrevs.add(repo[bmrev].rev())
388 return [r for r in subset if r in bmrevs]
388 return [r for r in subset if r in bmrevs]
389
389
390 bms = set([repo[r].rev()
390 bms = set([repo[r].rev()
391 for r in bookmarksmod.listbookmarks(repo).values()])
391 for r in bookmarksmod.listbookmarks(repo).values()])
392 return [r for r in subset if r in bms]
392 return [r for r in subset if r in bms]
393
393
394 def branch(repo, subset, x):
394 def branch(repo, subset, x):
395 """``branch(string or set)``
395 """``branch(string or set)``
396 All changesets belonging to the given branch or the branches of the given
396 All changesets belonging to the given branch or the branches of the given
397 changesets.
397 changesets.
398
398
399 If `string` starts with `re:`, the remainder of the name is treated as
399 If `string` starts with `re:`, the remainder of the name is treated as
400 a regular expression. To match a branch that actually starts with `re:`,
400 a regular expression. To match a branch that actually starts with `re:`,
401 use the prefix `literal:`.
401 use the prefix `literal:`.
402 """
402 """
403 try:
403 try:
404 b = getstring(x, '')
404 b = getstring(x, '')
405 except error.ParseError:
405 except error.ParseError:
406 # not a string, but another revspec, e.g. tip()
406 # not a string, but another revspec, e.g. tip()
407 pass
407 pass
408 else:
408 else:
409 kind, pattern, matcher = _stringmatcher(b)
409 kind, pattern, matcher = _stringmatcher(b)
410 if kind == 'literal':
410 if kind == 'literal':
411 # note: falls through to the revspec case if no branch with
411 # note: falls through to the revspec case if no branch with
412 # this name exists
412 # this name exists
413 if pattern in repo.branchmap():
413 if pattern in repo.branchmap():
414 return [r for r in subset if matcher(repo[r].branch())]
414 return [r for r in subset if matcher(repo[r].branch())]
415 else:
415 else:
416 return [r for r in subset if matcher(repo[r].branch())]
416 return [r for r in subset if matcher(repo[r].branch())]
417
417
418 s = getset(repo, list(repo), x)
418 s = getset(repo, list(repo), x)
419 b = set()
419 b = set()
420 for r in s:
420 for r in s:
421 b.add(repo[r].branch())
421 b.add(repo[r].branch())
422 s = set(s)
422 s = set(s)
423 return [r for r in subset if r in s or repo[r].branch() in b]
423 return [r for r in subset if r in s or repo[r].branch() in b]
424
424
425 def bumped(repo, subset, x):
426 """``bumped()``
427 Mutable changesets marked as successors of public changesets.
428
429 Only non-public and non-obsolete changesets can be `bumped`.
430 """
431 # i18n: "bumped" is a keyword
432 getargs(x, 0, 0, _("bumped takes no arguments"))
433 bumped = obsmod.getrevs(repo, 'bumped')
434 return [r for r in subset if r in bumped]
435
425 def checkstatus(repo, subset, pat, field):
436 def checkstatus(repo, subset, pat, field):
426 m = None
437 m = None
427 s = []
438 s = []
428 hasset = matchmod.patkind(pat) == 'set'
439 hasset = matchmod.patkind(pat) == 'set'
429 fname = None
440 fname = None
430 for r in subset:
441 for r in subset:
431 c = repo[r]
442 c = repo[r]
432 if not m or hasset:
443 if not m or hasset:
433 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
444 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
434 if not m.anypats() and len(m.files()) == 1:
445 if not m.anypats() and len(m.files()) == 1:
435 fname = m.files()[0]
446 fname = m.files()[0]
436 if fname is not None:
447 if fname is not None:
437 if fname not in c.files():
448 if fname not in c.files():
438 continue
449 continue
439 else:
450 else:
440 for f in c.files():
451 for f in c.files():
441 if m(f):
452 if m(f):
442 break
453 break
443 else:
454 else:
444 continue
455 continue
445 files = repo.status(c.p1().node(), c.node())[field]
456 files = repo.status(c.p1().node(), c.node())[field]
446 if fname is not None:
457 if fname is not None:
447 if fname in files:
458 if fname in files:
448 s.append(r)
459 s.append(r)
449 else:
460 else:
450 for f in files:
461 for f in files:
451 if m(f):
462 if m(f):
452 s.append(r)
463 s.append(r)
453 break
464 break
454 return s
465 return s
455
466
456 def _children(repo, narrow, parentset):
467 def _children(repo, narrow, parentset):
457 cs = set()
468 cs = set()
458 pr = repo.changelog.parentrevs
469 pr = repo.changelog.parentrevs
459 for r in narrow:
470 for r in narrow:
460 for p in pr(r):
471 for p in pr(r):
461 if p in parentset:
472 if p in parentset:
462 cs.add(r)
473 cs.add(r)
463 return cs
474 return cs
464
475
465 def children(repo, subset, x):
476 def children(repo, subset, x):
466 """``children(set)``
477 """``children(set)``
467 Child changesets of changesets in set.
478 Child changesets of changesets in set.
468 """
479 """
469 s = set(getset(repo, list(repo), x))
480 s = set(getset(repo, list(repo), x))
470 cs = _children(repo, subset, s)
481 cs = _children(repo, subset, s)
471 return [r for r in subset if r in cs]
482 return [r for r in subset if r in cs]
472
483
473 def closed(repo, subset, x):
484 def closed(repo, subset, x):
474 """``closed()``
485 """``closed()``
475 Changeset is closed.
486 Changeset is closed.
476 """
487 """
477 # i18n: "closed" is a keyword
488 # i18n: "closed" is a keyword
478 getargs(x, 0, 0, _("closed takes no arguments"))
489 getargs(x, 0, 0, _("closed takes no arguments"))
479 return [r for r in subset if repo[r].closesbranch()]
490 return [r for r in subset if repo[r].closesbranch()]
480
491
481 def contains(repo, subset, x):
492 def contains(repo, subset, x):
482 """``contains(pattern)``
493 """``contains(pattern)``
483 Revision contains a file matching pattern. See :hg:`help patterns`
494 Revision contains a file matching pattern. See :hg:`help patterns`
484 for information about file patterns.
495 for information about file patterns.
485 """
496 """
486 # i18n: "contains" is a keyword
497 # i18n: "contains" is a keyword
487 pat = getstring(x, _("contains requires a pattern"))
498 pat = getstring(x, _("contains requires a pattern"))
488 m = None
499 m = None
489 s = []
500 s = []
490 if not matchmod.patkind(pat):
501 if not matchmod.patkind(pat):
491 for r in subset:
502 for r in subset:
492 if pat in repo[r]:
503 if pat in repo[r]:
493 s.append(r)
504 s.append(r)
494 else:
505 else:
495 for r in subset:
506 for r in subset:
496 c = repo[r]
507 c = repo[r]
497 if not m or matchmod.patkind(pat) == 'set':
508 if not m or matchmod.patkind(pat) == 'set':
498 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
509 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
499 for f in c.manifest():
510 for f in c.manifest():
500 if m(f):
511 if m(f):
501 s.append(r)
512 s.append(r)
502 break
513 break
503 return s
514 return s
504
515
505 def converted(repo, subset, x):
516 def converted(repo, subset, x):
506 """``converted([id])``
517 """``converted([id])``
507 Changesets converted from the given identifier in the old repository if
518 Changesets converted from the given identifier in the old repository if
508 present, or all converted changesets if no identifier is specified.
519 present, or all converted changesets if no identifier is specified.
509 """
520 """
510
521
511 # There is exactly no chance of resolving the revision, so do a simple
522 # There is exactly no chance of resolving the revision, so do a simple
512 # string compare and hope for the best
523 # string compare and hope for the best
513
524
514 rev = None
525 rev = None
515 # i18n: "converted" is a keyword
526 # i18n: "converted" is a keyword
516 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
527 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
517 if l:
528 if l:
518 # i18n: "converted" is a keyword
529 # i18n: "converted" is a keyword
519 rev = getstring(l[0], _('converted requires a revision'))
530 rev = getstring(l[0], _('converted requires a revision'))
520
531
521 def _matchvalue(r):
532 def _matchvalue(r):
522 source = repo[r].extra().get('convert_revision', None)
533 source = repo[r].extra().get('convert_revision', None)
523 return source is not None and (rev is None or source.startswith(rev))
534 return source is not None and (rev is None or source.startswith(rev))
524
535
525 return [r for r in subset if _matchvalue(r)]
536 return [r for r in subset if _matchvalue(r)]
526
537
527 def date(repo, subset, x):
538 def date(repo, subset, x):
528 """``date(interval)``
539 """``date(interval)``
529 Changesets within the interval, see :hg:`help dates`.
540 Changesets within the interval, see :hg:`help dates`.
530 """
541 """
531 # i18n: "date" is a keyword
542 # i18n: "date" is a keyword
532 ds = getstring(x, _("date requires a string"))
543 ds = getstring(x, _("date requires a string"))
533 dm = util.matchdate(ds)
544 dm = util.matchdate(ds)
534 return [r for r in subset if dm(repo[r].date()[0])]
545 return [r for r in subset if dm(repo[r].date()[0])]
535
546
536 def desc(repo, subset, x):
547 def desc(repo, subset, x):
537 """``desc(string)``
548 """``desc(string)``
538 Search commit message for string. The match is case-insensitive.
549 Search commit message for string. The match is case-insensitive.
539 """
550 """
540 # i18n: "desc" is a keyword
551 # i18n: "desc" is a keyword
541 ds = encoding.lower(getstring(x, _("desc requires a string")))
552 ds = encoding.lower(getstring(x, _("desc requires a string")))
542 l = []
553 l = []
543 for r in subset:
554 for r in subset:
544 c = repo[r]
555 c = repo[r]
545 if ds in encoding.lower(c.description()):
556 if ds in encoding.lower(c.description()):
546 l.append(r)
557 l.append(r)
547 return l
558 return l
548
559
549 def _descendants(repo, subset, x, followfirst=False):
560 def _descendants(repo, subset, x, followfirst=False):
550 args = getset(repo, list(repo), x)
561 args = getset(repo, list(repo), x)
551 if not args:
562 if not args:
552 return []
563 return []
553 s = set(_revdescendants(repo, args, followfirst)) | set(args)
564 s = set(_revdescendants(repo, args, followfirst)) | set(args)
554 return [r for r in subset if r in s]
565 return [r for r in subset if r in s]
555
566
556 def descendants(repo, subset, x):
567 def descendants(repo, subset, x):
557 """``descendants(set)``
568 """``descendants(set)``
558 Changesets which are descendants of changesets in set.
569 Changesets which are descendants of changesets in set.
559 """
570 """
560 return _descendants(repo, subset, x)
571 return _descendants(repo, subset, x)
561
572
562 def _firstdescendants(repo, subset, x):
573 def _firstdescendants(repo, subset, x):
563 # ``_firstdescendants(set)``
574 # ``_firstdescendants(set)``
564 # Like ``descendants(set)`` but follows only the first parents.
575 # Like ``descendants(set)`` but follows only the first parents.
565 return _descendants(repo, subset, x, followfirst=True)
576 return _descendants(repo, subset, x, followfirst=True)
566
577
567 def destination(repo, subset, x):
578 def destination(repo, subset, x):
568 """``destination([set])``
579 """``destination([set])``
569 Changesets that were created by a graft, transplant or rebase operation,
580 Changesets that were created by a graft, transplant or rebase operation,
570 with the given revisions specified as the source. Omitting the optional set
581 with the given revisions specified as the source. Omitting the optional set
571 is the same as passing all().
582 is the same as passing all().
572 """
583 """
573 if x is not None:
584 if x is not None:
574 args = set(getset(repo, list(repo), x))
585 args = set(getset(repo, list(repo), x))
575 else:
586 else:
576 args = set(getall(repo, list(repo), x))
587 args = set(getall(repo, list(repo), x))
577
588
578 dests = set()
589 dests = set()
579
590
580 # subset contains all of the possible destinations that can be returned, so
591 # subset contains all of the possible destinations that can be returned, so
581 # iterate over them and see if their source(s) were provided in the args.
592 # iterate over them and see if their source(s) were provided in the args.
582 # Even if the immediate src of r is not in the args, src's source (or
593 # Even if the immediate src of r is not in the args, src's source (or
583 # further back) may be. Scanning back further than the immediate src allows
594 # further back) may be. Scanning back further than the immediate src allows
584 # transitive transplants and rebases to yield the same results as transitive
595 # transitive transplants and rebases to yield the same results as transitive
585 # grafts.
596 # grafts.
586 for r in subset:
597 for r in subset:
587 src = _getrevsource(repo, r)
598 src = _getrevsource(repo, r)
588 lineage = None
599 lineage = None
589
600
590 while src is not None:
601 while src is not None:
591 if lineage is None:
602 if lineage is None:
592 lineage = list()
603 lineage = list()
593
604
594 lineage.append(r)
605 lineage.append(r)
595
606
596 # The visited lineage is a match if the current source is in the arg
607 # The visited lineage is a match if the current source is in the arg
597 # set. Since every candidate dest is visited by way of iterating
608 # set. Since every candidate dest is visited by way of iterating
598 # subset, any dests further back in the lineage will be tested by a
609 # subset, any dests further back in the lineage will be tested by a
599 # different iteration over subset. Likewise, if the src was already
610 # different iteration over subset. Likewise, if the src was already
600 # selected, the current lineage can be selected without going back
611 # selected, the current lineage can be selected without going back
601 # further.
612 # further.
602 if src in args or src in dests:
613 if src in args or src in dests:
603 dests.update(lineage)
614 dests.update(lineage)
604 break
615 break
605
616
606 r = src
617 r = src
607 src = _getrevsource(repo, r)
618 src = _getrevsource(repo, r)
608
619
609 return [r for r in subset if r in dests]
620 return [r for r in subset if r in dests]
610
621
611 def draft(repo, subset, x):
622 def draft(repo, subset, x):
612 """``draft()``
623 """``draft()``
613 Changeset in draft phase."""
624 Changeset in draft phase."""
614 # i18n: "draft" is a keyword
625 # i18n: "draft" is a keyword
615 getargs(x, 0, 0, _("draft takes no arguments"))
626 getargs(x, 0, 0, _("draft takes no arguments"))
616 pc = repo._phasecache
627 pc = repo._phasecache
617 return [r for r in subset if pc.phase(repo, r) == phases.draft]
628 return [r for r in subset if pc.phase(repo, r) == phases.draft]
618
629
619 def extinct(repo, subset, x):
630 def extinct(repo, subset, x):
620 """``extinct()``
631 """``extinct()``
621 Obsolete changesets with obsolete descendants only.
632 Obsolete changesets with obsolete descendants only.
622 """
633 """
623 # i18n: "extinct" is a keyword
634 # i18n: "extinct" is a keyword
624 getargs(x, 0, 0, _("extinct takes no arguments"))
635 getargs(x, 0, 0, _("extinct takes no arguments"))
625 extincts = obsmod.getrevs(repo, 'extinct')
636 extincts = obsmod.getrevs(repo, 'extinct')
626 return [r for r in subset if r in extincts]
637 return [r for r in subset if r in extincts]
627
638
628 def extra(repo, subset, x):
639 def extra(repo, subset, x):
629 """``extra(label, [value])``
640 """``extra(label, [value])``
630 Changesets with the given label in the extra metadata, with the given
641 Changesets with the given label in the extra metadata, with the given
631 optional value.
642 optional value.
632
643
633 If `value` starts with `re:`, the remainder of the value is treated as
644 If `value` starts with `re:`, the remainder of the value is treated as
634 a regular expression. To match a value that actually starts with `re:`,
645 a regular expression. To match a value that actually starts with `re:`,
635 use the prefix `literal:`.
646 use the prefix `literal:`.
636 """
647 """
637
648
638 # i18n: "extra" is a keyword
649 # i18n: "extra" is a keyword
639 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
650 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
640 # i18n: "extra" is a keyword
651 # i18n: "extra" is a keyword
641 label = getstring(l[0], _('first argument to extra must be a string'))
652 label = getstring(l[0], _('first argument to extra must be a string'))
642 value = None
653 value = None
643
654
644 if len(l) > 1:
655 if len(l) > 1:
645 # i18n: "extra" is a keyword
656 # i18n: "extra" is a keyword
646 value = getstring(l[1], _('second argument to extra must be a string'))
657 value = getstring(l[1], _('second argument to extra must be a string'))
647 kind, value, matcher = _stringmatcher(value)
658 kind, value, matcher = _stringmatcher(value)
648
659
649 def _matchvalue(r):
660 def _matchvalue(r):
650 extra = repo[r].extra()
661 extra = repo[r].extra()
651 return label in extra and (value is None or matcher(extra[label]))
662 return label in extra and (value is None or matcher(extra[label]))
652
663
653 return [r for r in subset if _matchvalue(r)]
664 return [r for r in subset if _matchvalue(r)]
654
665
655 def filelog(repo, subset, x):
666 def filelog(repo, subset, x):
656 """``filelog(pattern)``
667 """``filelog(pattern)``
657 Changesets connected to the specified filelog.
668 Changesets connected to the specified filelog.
658
669
659 For performance reasons, ``filelog()`` does not show every changeset
670 For performance reasons, ``filelog()`` does not show every changeset
660 that affects the requested file(s). See :hg:`help log` for details. For
671 that affects the requested file(s). See :hg:`help log` for details. For
661 a slower, more accurate result, use ``file()``.
672 a slower, more accurate result, use ``file()``.
662 """
673 """
663
674
664 # i18n: "filelog" is a keyword
675 # i18n: "filelog" is a keyword
665 pat = getstring(x, _("filelog requires a pattern"))
676 pat = getstring(x, _("filelog requires a pattern"))
666 m = matchmod.match(repo.root, repo.getcwd(), [pat], default='relpath',
677 m = matchmod.match(repo.root, repo.getcwd(), [pat], default='relpath',
667 ctx=repo[None])
678 ctx=repo[None])
668 s = set()
679 s = set()
669
680
670 if not matchmod.patkind(pat):
681 if not matchmod.patkind(pat):
671 for f in m.files():
682 for f in m.files():
672 fl = repo.file(f)
683 fl = repo.file(f)
673 for fr in fl:
684 for fr in fl:
674 s.add(fl.linkrev(fr))
685 s.add(fl.linkrev(fr))
675 else:
686 else:
676 for f in repo[None]:
687 for f in repo[None]:
677 if m(f):
688 if m(f):
678 fl = repo.file(f)
689 fl = repo.file(f)
679 for fr in fl:
690 for fr in fl:
680 s.add(fl.linkrev(fr))
691 s.add(fl.linkrev(fr))
681
692
682 return [r for r in subset if r in s]
693 return [r for r in subset if r in s]
683
694
684 def first(repo, subset, x):
695 def first(repo, subset, x):
685 """``first(set, [n])``
696 """``first(set, [n])``
686 An alias for limit().
697 An alias for limit().
687 """
698 """
688 return limit(repo, subset, x)
699 return limit(repo, subset, x)
689
700
690 def _follow(repo, subset, x, name, followfirst=False):
701 def _follow(repo, subset, x, name, followfirst=False):
691 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
702 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
692 c = repo['.']
703 c = repo['.']
693 if l:
704 if l:
694 x = getstring(l[0], _("%s expected a filename") % name)
705 x = getstring(l[0], _("%s expected a filename") % name)
695 if x in c:
706 if x in c:
696 cx = c[x]
707 cx = c[x]
697 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
708 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
698 # include the revision responsible for the most recent version
709 # include the revision responsible for the most recent version
699 s.add(cx.linkrev())
710 s.add(cx.linkrev())
700 else:
711 else:
701 return []
712 return []
702 else:
713 else:
703 s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()])
714 s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()])
704
715
705 return [r for r in subset if r in s]
716 return [r for r in subset if r in s]
706
717
707 def follow(repo, subset, x):
718 def follow(repo, subset, x):
708 """``follow([file])``
719 """``follow([file])``
709 An alias for ``::.`` (ancestors of the working copy's first parent).
720 An alias for ``::.`` (ancestors of the working copy's first parent).
710 If a filename is specified, the history of the given file is followed,
721 If a filename is specified, the history of the given file is followed,
711 including copies.
722 including copies.
712 """
723 """
713 return _follow(repo, subset, x, 'follow')
724 return _follow(repo, subset, x, 'follow')
714
725
715 def _followfirst(repo, subset, x):
726 def _followfirst(repo, subset, x):
716 # ``followfirst([file])``
727 # ``followfirst([file])``
717 # Like ``follow([file])`` but follows only the first parent of
728 # Like ``follow([file])`` but follows only the first parent of
718 # every revision or file revision.
729 # every revision or file revision.
719 return _follow(repo, subset, x, '_followfirst', followfirst=True)
730 return _follow(repo, subset, x, '_followfirst', followfirst=True)
720
731
721 def getall(repo, subset, x):
732 def getall(repo, subset, x):
722 """``all()``
733 """``all()``
723 All changesets, the same as ``0:tip``.
734 All changesets, the same as ``0:tip``.
724 """
735 """
725 # i18n: "all" is a keyword
736 # i18n: "all" is a keyword
726 getargs(x, 0, 0, _("all takes no arguments"))
737 getargs(x, 0, 0, _("all takes no arguments"))
727 return subset
738 return subset
728
739
729 def grep(repo, subset, x):
740 def grep(repo, subset, x):
730 """``grep(regex)``
741 """``grep(regex)``
731 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
742 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
732 to ensure special escape characters are handled correctly. Unlike
743 to ensure special escape characters are handled correctly. Unlike
733 ``keyword(string)``, the match is case-sensitive.
744 ``keyword(string)``, the match is case-sensitive.
734 """
745 """
735 try:
746 try:
736 # i18n: "grep" is a keyword
747 # i18n: "grep" is a keyword
737 gr = re.compile(getstring(x, _("grep requires a string")))
748 gr = re.compile(getstring(x, _("grep requires a string")))
738 except re.error, e:
749 except re.error, e:
739 raise error.ParseError(_('invalid match pattern: %s') % e)
750 raise error.ParseError(_('invalid match pattern: %s') % e)
740 l = []
751 l = []
741 for r in subset:
752 for r in subset:
742 c = repo[r]
753 c = repo[r]
743 for e in c.files() + [c.user(), c.description()]:
754 for e in c.files() + [c.user(), c.description()]:
744 if gr.search(e):
755 if gr.search(e):
745 l.append(r)
756 l.append(r)
746 break
757 break
747 return l
758 return l
748
759
749 def _matchfiles(repo, subset, x):
760 def _matchfiles(repo, subset, x):
750 # _matchfiles takes a revset list of prefixed arguments:
761 # _matchfiles takes a revset list of prefixed arguments:
751 #
762 #
752 # [p:foo, i:bar, x:baz]
763 # [p:foo, i:bar, x:baz]
753 #
764 #
754 # builds a match object from them and filters subset. Allowed
765 # builds a match object from them and filters subset. Allowed
755 # prefixes are 'p:' for regular patterns, 'i:' for include
766 # prefixes are 'p:' for regular patterns, 'i:' for include
756 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
767 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
757 # a revision identifier, or the empty string to reference the
768 # a revision identifier, or the empty string to reference the
758 # working directory, from which the match object is
769 # working directory, from which the match object is
759 # initialized. Use 'd:' to set the default matching mode, default
770 # initialized. Use 'd:' to set the default matching mode, default
760 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
771 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
761
772
762 # i18n: "_matchfiles" is a keyword
773 # i18n: "_matchfiles" is a keyword
763 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
774 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
764 pats, inc, exc = [], [], []
775 pats, inc, exc = [], [], []
765 hasset = False
776 hasset = False
766 rev, default = None, None
777 rev, default = None, None
767 for arg in l:
778 for arg in l:
768 # i18n: "_matchfiles" is a keyword
779 # i18n: "_matchfiles" is a keyword
769 s = getstring(arg, _("_matchfiles requires string arguments"))
780 s = getstring(arg, _("_matchfiles requires string arguments"))
770 prefix, value = s[:2], s[2:]
781 prefix, value = s[:2], s[2:]
771 if prefix == 'p:':
782 if prefix == 'p:':
772 pats.append(value)
783 pats.append(value)
773 elif prefix == 'i:':
784 elif prefix == 'i:':
774 inc.append(value)
785 inc.append(value)
775 elif prefix == 'x:':
786 elif prefix == 'x:':
776 exc.append(value)
787 exc.append(value)
777 elif prefix == 'r:':
788 elif prefix == 'r:':
778 if rev is not None:
789 if rev is not None:
779 # i18n: "_matchfiles" is a keyword
790 # i18n: "_matchfiles" is a keyword
780 raise error.ParseError(_('_matchfiles expected at most one '
791 raise error.ParseError(_('_matchfiles expected at most one '
781 'revision'))
792 'revision'))
782 rev = value
793 rev = value
783 elif prefix == 'd:':
794 elif prefix == 'd:':
784 if default is not None:
795 if default is not None:
785 # i18n: "_matchfiles" is a keyword
796 # i18n: "_matchfiles" is a keyword
786 raise error.ParseError(_('_matchfiles expected at most one '
797 raise error.ParseError(_('_matchfiles expected at most one '
787 'default mode'))
798 'default mode'))
788 default = value
799 default = value
789 else:
800 else:
790 # i18n: "_matchfiles" is a keyword
801 # i18n: "_matchfiles" is a keyword
791 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
802 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
792 if not hasset and matchmod.patkind(value) == 'set':
803 if not hasset and matchmod.patkind(value) == 'set':
793 hasset = True
804 hasset = True
794 if not default:
805 if not default:
795 default = 'glob'
806 default = 'glob'
796 m = None
807 m = None
797 s = []
808 s = []
798 for r in subset:
809 for r in subset:
799 c = repo[r]
810 c = repo[r]
800 if not m or (hasset and rev is None):
811 if not m or (hasset and rev is None):
801 ctx = c
812 ctx = c
802 if rev is not None:
813 if rev is not None:
803 ctx = repo[rev or None]
814 ctx = repo[rev or None]
804 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
815 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
805 exclude=exc, ctx=ctx, default=default)
816 exclude=exc, ctx=ctx, default=default)
806 for f in c.files():
817 for f in c.files():
807 if m(f):
818 if m(f):
808 s.append(r)
819 s.append(r)
809 break
820 break
810 return s
821 return s
811
822
812 def hasfile(repo, subset, x):
823 def hasfile(repo, subset, x):
813 """``file(pattern)``
824 """``file(pattern)``
814 Changesets affecting files matched by pattern.
825 Changesets affecting files matched by pattern.
815
826
816 For a faster but less accurate result, consider using ``filelog()``
827 For a faster but less accurate result, consider using ``filelog()``
817 instead.
828 instead.
818 """
829 """
819 # i18n: "file" is a keyword
830 # i18n: "file" is a keyword
820 pat = getstring(x, _("file requires a pattern"))
831 pat = getstring(x, _("file requires a pattern"))
821 return _matchfiles(repo, subset, ('string', 'p:' + pat))
832 return _matchfiles(repo, subset, ('string', 'p:' + pat))
822
833
823 def head(repo, subset, x):
834 def head(repo, subset, x):
824 """``head()``
835 """``head()``
825 Changeset is a named branch head.
836 Changeset is a named branch head.
826 """
837 """
827 # i18n: "head" is a keyword
838 # i18n: "head" is a keyword
828 getargs(x, 0, 0, _("head takes no arguments"))
839 getargs(x, 0, 0, _("head takes no arguments"))
829 hs = set()
840 hs = set()
830 for b, ls in repo.branchmap().iteritems():
841 for b, ls in repo.branchmap().iteritems():
831 hs.update(repo[h].rev() for h in ls)
842 hs.update(repo[h].rev() for h in ls)
832 return [r for r in subset if r in hs]
843 return [r for r in subset if r in hs]
833
844
834 def heads(repo, subset, x):
845 def heads(repo, subset, x):
835 """``heads(set)``
846 """``heads(set)``
836 Members of set with no children in set.
847 Members of set with no children in set.
837 """
848 """
838 s = getset(repo, subset, x)
849 s = getset(repo, subset, x)
839 ps = set(parents(repo, subset, x))
850 ps = set(parents(repo, subset, x))
840 return [r for r in s if r not in ps]
851 return [r for r in s if r not in ps]
841
852
842 def hidden(repo, subset, x):
853 def hidden(repo, subset, x):
843 """``hidden()``
854 """``hidden()``
844 Hidden changesets.
855 Hidden changesets.
845 """
856 """
846 # i18n: "hidden" is a keyword
857 # i18n: "hidden" is a keyword
847 getargs(x, 0, 0, _("hidden takes no arguments"))
858 getargs(x, 0, 0, _("hidden takes no arguments"))
848 return [r for r in subset if r in repo.hiddenrevs]
859 return [r for r in subset if r in repo.hiddenrevs]
849
860
850 def keyword(repo, subset, x):
861 def keyword(repo, subset, x):
851 """``keyword(string)``
862 """``keyword(string)``
852 Search commit message, user name, and names of changed files for
863 Search commit message, user name, and names of changed files for
853 string. The match is case-insensitive.
864 string. The match is case-insensitive.
854 """
865 """
855 # i18n: "keyword" is a keyword
866 # i18n: "keyword" is a keyword
856 kw = encoding.lower(getstring(x, _("keyword requires a string")))
867 kw = encoding.lower(getstring(x, _("keyword requires a string")))
857 l = []
868 l = []
858 for r in subset:
869 for r in subset:
859 c = repo[r]
870 c = repo[r]
860 t = " ".join(c.files() + [c.user(), c.description()])
871 t = " ".join(c.files() + [c.user(), c.description()])
861 if kw in encoding.lower(t):
872 if kw in encoding.lower(t):
862 l.append(r)
873 l.append(r)
863 return l
874 return l
864
875
865 def limit(repo, subset, x):
876 def limit(repo, subset, x):
866 """``limit(set, [n])``
877 """``limit(set, [n])``
867 First n members of set, defaulting to 1.
878 First n members of set, defaulting to 1.
868 """
879 """
869 # i18n: "limit" is a keyword
880 # i18n: "limit" is a keyword
870 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
881 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
871 try:
882 try:
872 lim = 1
883 lim = 1
873 if len(l) == 2:
884 if len(l) == 2:
874 # i18n: "limit" is a keyword
885 # i18n: "limit" is a keyword
875 lim = int(getstring(l[1], _("limit requires a number")))
886 lim = int(getstring(l[1], _("limit requires a number")))
876 except (TypeError, ValueError):
887 except (TypeError, ValueError):
877 # i18n: "limit" is a keyword
888 # i18n: "limit" is a keyword
878 raise error.ParseError(_("limit expects a number"))
889 raise error.ParseError(_("limit expects a number"))
879 ss = set(subset)
890 ss = set(subset)
880 os = getset(repo, list(repo), l[0])[:lim]
891 os = getset(repo, list(repo), l[0])[:lim]
881 return [r for r in os if r in ss]
892 return [r for r in os if r in ss]
882
893
883 def last(repo, subset, x):
894 def last(repo, subset, x):
884 """``last(set, [n])``
895 """``last(set, [n])``
885 Last n members of set, defaulting to 1.
896 Last n members of set, defaulting to 1.
886 """
897 """
887 # i18n: "last" is a keyword
898 # i18n: "last" is a keyword
888 l = getargs(x, 1, 2, _("last requires one or two arguments"))
899 l = getargs(x, 1, 2, _("last requires one or two arguments"))
889 try:
900 try:
890 lim = 1
901 lim = 1
891 if len(l) == 2:
902 if len(l) == 2:
892 # i18n: "last" is a keyword
903 # i18n: "last" is a keyword
893 lim = int(getstring(l[1], _("last requires a number")))
904 lim = int(getstring(l[1], _("last requires a number")))
894 except (TypeError, ValueError):
905 except (TypeError, ValueError):
895 # i18n: "last" is a keyword
906 # i18n: "last" is a keyword
896 raise error.ParseError(_("last expects a number"))
907 raise error.ParseError(_("last expects a number"))
897 ss = set(subset)
908 ss = set(subset)
898 os = getset(repo, list(repo), l[0])[-lim:]
909 os = getset(repo, list(repo), l[0])[-lim:]
899 return [r for r in os if r in ss]
910 return [r for r in os if r in ss]
900
911
901 def maxrev(repo, subset, x):
912 def maxrev(repo, subset, x):
902 """``max(set)``
913 """``max(set)``
903 Changeset with highest revision number in set.
914 Changeset with highest revision number in set.
904 """
915 """
905 os = getset(repo, list(repo), x)
916 os = getset(repo, list(repo), x)
906 if os:
917 if os:
907 m = max(os)
918 m = max(os)
908 if m in subset:
919 if m in subset:
909 return [m]
920 return [m]
910 return []
921 return []
911
922
912 def merge(repo, subset, x):
923 def merge(repo, subset, x):
913 """``merge()``
924 """``merge()``
914 Changeset is a merge changeset.
925 Changeset is a merge changeset.
915 """
926 """
916 # i18n: "merge" is a keyword
927 # i18n: "merge" is a keyword
917 getargs(x, 0, 0, _("merge takes no arguments"))
928 getargs(x, 0, 0, _("merge takes no arguments"))
918 cl = repo.changelog
929 cl = repo.changelog
919 return [r for r in subset if cl.parentrevs(r)[1] != -1]
930 return [r for r in subset if cl.parentrevs(r)[1] != -1]
920
931
921 def branchpoint(repo, subset, x):
932 def branchpoint(repo, subset, x):
922 """``branchpoint()``
933 """``branchpoint()``
923 Changesets with more than one child.
934 Changesets with more than one child.
924 """
935 """
925 # i18n: "branchpoint" is a keyword
936 # i18n: "branchpoint" is a keyword
926 getargs(x, 0, 0, _("branchpoint takes no arguments"))
937 getargs(x, 0, 0, _("branchpoint takes no arguments"))
927 cl = repo.changelog
938 cl = repo.changelog
928 if not subset:
939 if not subset:
929 return []
940 return []
930 baserev = min(subset)
941 baserev = min(subset)
931 parentscount = [0]*(len(repo) - baserev)
942 parentscount = [0]*(len(repo) - baserev)
932 for r in cl.revs(start=baserev + 1):
943 for r in cl.revs(start=baserev + 1):
933 for p in cl.parentrevs(r):
944 for p in cl.parentrevs(r):
934 if p >= baserev:
945 if p >= baserev:
935 parentscount[p - baserev] += 1
946 parentscount[p - baserev] += 1
936 return [r for r in subset if (parentscount[r - baserev] > 1)]
947 return [r for r in subset if (parentscount[r - baserev] > 1)]
937
948
938 def minrev(repo, subset, x):
949 def minrev(repo, subset, x):
939 """``min(set)``
950 """``min(set)``
940 Changeset with lowest revision number in set.
951 Changeset with lowest revision number in set.
941 """
952 """
942 os = getset(repo, list(repo), x)
953 os = getset(repo, list(repo), x)
943 if os:
954 if os:
944 m = min(os)
955 m = min(os)
945 if m in subset:
956 if m in subset:
946 return [m]
957 return [m]
947 return []
958 return []
948
959
949 def modifies(repo, subset, x):
960 def modifies(repo, subset, x):
950 """``modifies(pattern)``
961 """``modifies(pattern)``
951 Changesets modifying files matched by pattern.
962 Changesets modifying files matched by pattern.
952 """
963 """
953 # i18n: "modifies" is a keyword
964 # i18n: "modifies" is a keyword
954 pat = getstring(x, _("modifies requires a pattern"))
965 pat = getstring(x, _("modifies requires a pattern"))
955 return checkstatus(repo, subset, pat, 0)
966 return checkstatus(repo, subset, pat, 0)
956
967
957 def node_(repo, subset, x):
968 def node_(repo, subset, x):
958 """``id(string)``
969 """``id(string)``
959 Revision non-ambiguously specified by the given hex string prefix.
970 Revision non-ambiguously specified by the given hex string prefix.
960 """
971 """
961 # i18n: "id" is a keyword
972 # i18n: "id" is a keyword
962 l = getargs(x, 1, 1, _("id requires one argument"))
973 l = getargs(x, 1, 1, _("id requires one argument"))
963 # i18n: "id" is a keyword
974 # i18n: "id" is a keyword
964 n = getstring(l[0], _("id requires a string"))
975 n = getstring(l[0], _("id requires a string"))
965 if len(n) == 40:
976 if len(n) == 40:
966 rn = repo[n].rev()
977 rn = repo[n].rev()
967 else:
978 else:
968 rn = None
979 rn = None
969 pm = repo.changelog._partialmatch(n)
980 pm = repo.changelog._partialmatch(n)
970 if pm is not None:
981 if pm is not None:
971 rn = repo.changelog.rev(pm)
982 rn = repo.changelog.rev(pm)
972
983
973 return [r for r in subset if r == rn]
984 return [r for r in subset if r == rn]
974
985
975 def obsolete(repo, subset, x):
986 def obsolete(repo, subset, x):
976 """``obsolete()``
987 """``obsolete()``
977 Mutable changeset with a newer version."""
988 Mutable changeset with a newer version."""
978 # i18n: "obsolete" is a keyword
989 # i18n: "obsolete" is a keyword
979 getargs(x, 0, 0, _("obsolete takes no arguments"))
990 getargs(x, 0, 0, _("obsolete takes no arguments"))
980 obsoletes = obsmod.getrevs(repo, 'obsolete')
991 obsoletes = obsmod.getrevs(repo, 'obsolete')
981 return [r for r in subset if r in obsoletes]
992 return [r for r in subset if r in obsoletes]
982
993
983 def origin(repo, subset, x):
994 def origin(repo, subset, x):
984 """``origin([set])``
995 """``origin([set])``
985 Changesets that were specified as a source for the grafts, transplants or
996 Changesets that were specified as a source for the grafts, transplants or
986 rebases that created the given revisions. Omitting the optional set is the
997 rebases that created the given revisions. Omitting the optional set is the
987 same as passing all(). If a changeset created by these operations is itself
998 same as passing all(). If a changeset created by these operations is itself
988 specified as a source for one of these operations, only the source changeset
999 specified as a source for one of these operations, only the source changeset
989 for the first operation is selected.
1000 for the first operation is selected.
990 """
1001 """
991 if x is not None:
1002 if x is not None:
992 args = set(getset(repo, list(repo), x))
1003 args = set(getset(repo, list(repo), x))
993 else:
1004 else:
994 args = set(getall(repo, list(repo), x))
1005 args = set(getall(repo, list(repo), x))
995
1006
996 def _firstsrc(rev):
1007 def _firstsrc(rev):
997 src = _getrevsource(repo, rev)
1008 src = _getrevsource(repo, rev)
998 if src is None:
1009 if src is None:
999 return None
1010 return None
1000
1011
1001 while True:
1012 while True:
1002 prev = _getrevsource(repo, src)
1013 prev = _getrevsource(repo, src)
1003
1014
1004 if prev is None:
1015 if prev is None:
1005 return src
1016 return src
1006 src = prev
1017 src = prev
1007
1018
1008 o = set([_firstsrc(r) for r in args])
1019 o = set([_firstsrc(r) for r in args])
1009 return [r for r in subset if r in o]
1020 return [r for r in subset if r in o]
1010
1021
1011 def outgoing(repo, subset, x):
1022 def outgoing(repo, subset, x):
1012 """``outgoing([path])``
1023 """``outgoing([path])``
1013 Changesets not found in the specified destination repository, or the
1024 Changesets not found in the specified destination repository, or the
1014 default push location.
1025 default push location.
1015 """
1026 """
1016 import hg # avoid start-up nasties
1027 import hg # avoid start-up nasties
1017 # i18n: "outgoing" is a keyword
1028 # i18n: "outgoing" is a keyword
1018 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1029 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1019 # i18n: "outgoing" is a keyword
1030 # i18n: "outgoing" is a keyword
1020 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1031 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1021 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1032 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1022 dest, branches = hg.parseurl(dest)
1033 dest, branches = hg.parseurl(dest)
1023 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1034 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1024 if revs:
1035 if revs:
1025 revs = [repo.lookup(rev) for rev in revs]
1036 revs = [repo.lookup(rev) for rev in revs]
1026 other = hg.peer(repo, {}, dest)
1037 other = hg.peer(repo, {}, dest)
1027 repo.ui.pushbuffer()
1038 repo.ui.pushbuffer()
1028 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1039 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1029 repo.ui.popbuffer()
1040 repo.ui.popbuffer()
1030 cl = repo.changelog
1041 cl = repo.changelog
1031 o = set([cl.rev(r) for r in outgoing.missing])
1042 o = set([cl.rev(r) for r in outgoing.missing])
1032 return [r for r in subset if r in o]
1043 return [r for r in subset if r in o]
1033
1044
1034 def p1(repo, subset, x):
1045 def p1(repo, subset, x):
1035 """``p1([set])``
1046 """``p1([set])``
1036 First parent of changesets in set, or the working directory.
1047 First parent of changesets in set, or the working directory.
1037 """
1048 """
1038 if x is None:
1049 if x is None:
1039 p = repo[x].p1().rev()
1050 p = repo[x].p1().rev()
1040 return [r for r in subset if r == p]
1051 return [r for r in subset if r == p]
1041
1052
1042 ps = set()
1053 ps = set()
1043 cl = repo.changelog
1054 cl = repo.changelog
1044 for r in getset(repo, list(repo), x):
1055 for r in getset(repo, list(repo), x):
1045 ps.add(cl.parentrevs(r)[0])
1056 ps.add(cl.parentrevs(r)[0])
1046 return [r for r in subset if r in ps]
1057 return [r for r in subset if r in ps]
1047
1058
1048 def p2(repo, subset, x):
1059 def p2(repo, subset, x):
1049 """``p2([set])``
1060 """``p2([set])``
1050 Second parent of changesets in set, or the working directory.
1061 Second parent of changesets in set, or the working directory.
1051 """
1062 """
1052 if x is None:
1063 if x is None:
1053 ps = repo[x].parents()
1064 ps = repo[x].parents()
1054 try:
1065 try:
1055 p = ps[1].rev()
1066 p = ps[1].rev()
1056 return [r for r in subset if r == p]
1067 return [r for r in subset if r == p]
1057 except IndexError:
1068 except IndexError:
1058 return []
1069 return []
1059
1070
1060 ps = set()
1071 ps = set()
1061 cl = repo.changelog
1072 cl = repo.changelog
1062 for r in getset(repo, list(repo), x):
1073 for r in getset(repo, list(repo), x):
1063 ps.add(cl.parentrevs(r)[1])
1074 ps.add(cl.parentrevs(r)[1])
1064 return [r for r in subset if r in ps]
1075 return [r for r in subset if r in ps]
1065
1076
1066 def parents(repo, subset, x):
1077 def parents(repo, subset, x):
1067 """``parents([set])``
1078 """``parents([set])``
1068 The set of all parents for all changesets in set, or the working directory.
1079 The set of all parents for all changesets in set, or the working directory.
1069 """
1080 """
1070 if x is None:
1081 if x is None:
1071 ps = tuple(p.rev() for p in repo[x].parents())
1082 ps = tuple(p.rev() for p in repo[x].parents())
1072 return [r for r in subset if r in ps]
1083 return [r for r in subset if r in ps]
1073
1084
1074 ps = set()
1085 ps = set()
1075 cl = repo.changelog
1086 cl = repo.changelog
1076 for r in getset(repo, list(repo), x):
1087 for r in getset(repo, list(repo), x):
1077 ps.update(cl.parentrevs(r))
1088 ps.update(cl.parentrevs(r))
1078 return [r for r in subset if r in ps]
1089 return [r for r in subset if r in ps]
1079
1090
1080 def parentspec(repo, subset, x, n):
1091 def parentspec(repo, subset, x, n):
1081 """``set^0``
1092 """``set^0``
1082 The set.
1093 The set.
1083 ``set^1`` (or ``set^``), ``set^2``
1094 ``set^1`` (or ``set^``), ``set^2``
1084 First or second parent, respectively, of all changesets in set.
1095 First or second parent, respectively, of all changesets in set.
1085 """
1096 """
1086 try:
1097 try:
1087 n = int(n[1])
1098 n = int(n[1])
1088 if n not in (0, 1, 2):
1099 if n not in (0, 1, 2):
1089 raise ValueError
1100 raise ValueError
1090 except (TypeError, ValueError):
1101 except (TypeError, ValueError):
1091 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1102 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1092 ps = set()
1103 ps = set()
1093 cl = repo.changelog
1104 cl = repo.changelog
1094 for r in getset(repo, subset, x):
1105 for r in getset(repo, subset, x):
1095 if n == 0:
1106 if n == 0:
1096 ps.add(r)
1107 ps.add(r)
1097 elif n == 1:
1108 elif n == 1:
1098 ps.add(cl.parentrevs(r)[0])
1109 ps.add(cl.parentrevs(r)[0])
1099 elif n == 2:
1110 elif n == 2:
1100 parents = cl.parentrevs(r)
1111 parents = cl.parentrevs(r)
1101 if len(parents) > 1:
1112 if len(parents) > 1:
1102 ps.add(parents[1])
1113 ps.add(parents[1])
1103 return [r for r in subset if r in ps]
1114 return [r for r in subset if r in ps]
1104
1115
1105 def present(repo, subset, x):
1116 def present(repo, subset, x):
1106 """``present(set)``
1117 """``present(set)``
1107 An empty set, if any revision in set isn't found; otherwise,
1118 An empty set, if any revision in set isn't found; otherwise,
1108 all revisions in set.
1119 all revisions in set.
1109
1120
1110 If any of specified revisions is not present in the local repository,
1121 If any of specified revisions is not present in the local repository,
1111 the query is normally aborted. But this predicate allows the query
1122 the query is normally aborted. But this predicate allows the query
1112 to continue even in such cases.
1123 to continue even in such cases.
1113 """
1124 """
1114 try:
1125 try:
1115 return getset(repo, subset, x)
1126 return getset(repo, subset, x)
1116 except error.RepoLookupError:
1127 except error.RepoLookupError:
1117 return []
1128 return []
1118
1129
1119 def public(repo, subset, x):
1130 def public(repo, subset, x):
1120 """``public()``
1131 """``public()``
1121 Changeset in public phase."""
1132 Changeset in public phase."""
1122 # i18n: "public" is a keyword
1133 # i18n: "public" is a keyword
1123 getargs(x, 0, 0, _("public takes no arguments"))
1134 getargs(x, 0, 0, _("public takes no arguments"))
1124 pc = repo._phasecache
1135 pc = repo._phasecache
1125 return [r for r in subset if pc.phase(repo, r) == phases.public]
1136 return [r for r in subset if pc.phase(repo, r) == phases.public]
1126
1137
1127 def remote(repo, subset, x):
1138 def remote(repo, subset, x):
1128 """``remote([id [,path]])``
1139 """``remote([id [,path]])``
1129 Local revision that corresponds to the given identifier in a
1140 Local revision that corresponds to the given identifier in a
1130 remote repository, if present. Here, the '.' identifier is a
1141 remote repository, if present. Here, the '.' identifier is a
1131 synonym for the current local branch.
1142 synonym for the current local branch.
1132 """
1143 """
1133
1144
1134 import hg # avoid start-up nasties
1145 import hg # avoid start-up nasties
1135 # i18n: "remote" is a keyword
1146 # i18n: "remote" is a keyword
1136 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
1147 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
1137
1148
1138 q = '.'
1149 q = '.'
1139 if len(l) > 0:
1150 if len(l) > 0:
1140 # i18n: "remote" is a keyword
1151 # i18n: "remote" is a keyword
1141 q = getstring(l[0], _("remote requires a string id"))
1152 q = getstring(l[0], _("remote requires a string id"))
1142 if q == '.':
1153 if q == '.':
1143 q = repo['.'].branch()
1154 q = repo['.'].branch()
1144
1155
1145 dest = ''
1156 dest = ''
1146 if len(l) > 1:
1157 if len(l) > 1:
1147 # i18n: "remote" is a keyword
1158 # i18n: "remote" is a keyword
1148 dest = getstring(l[1], _("remote requires a repository path"))
1159 dest = getstring(l[1], _("remote requires a repository path"))
1149 dest = repo.ui.expandpath(dest or 'default')
1160 dest = repo.ui.expandpath(dest or 'default')
1150 dest, branches = hg.parseurl(dest)
1161 dest, branches = hg.parseurl(dest)
1151 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1162 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1152 if revs:
1163 if revs:
1153 revs = [repo.lookup(rev) for rev in revs]
1164 revs = [repo.lookup(rev) for rev in revs]
1154 other = hg.peer(repo, {}, dest)
1165 other = hg.peer(repo, {}, dest)
1155 n = other.lookup(q)
1166 n = other.lookup(q)
1156 if n in repo:
1167 if n in repo:
1157 r = repo[n].rev()
1168 r = repo[n].rev()
1158 if r in subset:
1169 if r in subset:
1159 return [r]
1170 return [r]
1160 return []
1171 return []
1161
1172
1162 def removes(repo, subset, x):
1173 def removes(repo, subset, x):
1163 """``removes(pattern)``
1174 """``removes(pattern)``
1164 Changesets which remove files matching pattern.
1175 Changesets which remove files matching pattern.
1165 """
1176 """
1166 # i18n: "removes" is a keyword
1177 # i18n: "removes" is a keyword
1167 pat = getstring(x, _("removes requires a pattern"))
1178 pat = getstring(x, _("removes requires a pattern"))
1168 return checkstatus(repo, subset, pat, 2)
1179 return checkstatus(repo, subset, pat, 2)
1169
1180
1170 def rev(repo, subset, x):
1181 def rev(repo, subset, x):
1171 """``rev(number)``
1182 """``rev(number)``
1172 Revision with the given numeric identifier.
1183 Revision with the given numeric identifier.
1173 """
1184 """
1174 # i18n: "rev" is a keyword
1185 # i18n: "rev" is a keyword
1175 l = getargs(x, 1, 1, _("rev requires one argument"))
1186 l = getargs(x, 1, 1, _("rev requires one argument"))
1176 try:
1187 try:
1177 # i18n: "rev" is a keyword
1188 # i18n: "rev" is a keyword
1178 l = int(getstring(l[0], _("rev requires a number")))
1189 l = int(getstring(l[0], _("rev requires a number")))
1179 except (TypeError, ValueError):
1190 except (TypeError, ValueError):
1180 # i18n: "rev" is a keyword
1191 # i18n: "rev" is a keyword
1181 raise error.ParseError(_("rev expects a number"))
1192 raise error.ParseError(_("rev expects a number"))
1182 return [r for r in subset if r == l]
1193 return [r for r in subset if r == l]
1183
1194
1184 def matching(repo, subset, x):
1195 def matching(repo, subset, x):
1185 """``matching(revision [, field])``
1196 """``matching(revision [, field])``
1186 Changesets in which a given set of fields match the set of fields in the
1197 Changesets in which a given set of fields match the set of fields in the
1187 selected revision or set.
1198 selected revision or set.
1188
1199
1189 To match more than one field pass the list of fields to match separated
1200 To match more than one field pass the list of fields to match separated
1190 by spaces (e.g. ``author description``).
1201 by spaces (e.g. ``author description``).
1191
1202
1192 Valid fields are most regular revision fields and some special fields.
1203 Valid fields are most regular revision fields and some special fields.
1193
1204
1194 Regular revision fields are ``description``, ``author``, ``branch``,
1205 Regular revision fields are ``description``, ``author``, ``branch``,
1195 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1206 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1196 and ``diff``.
1207 and ``diff``.
1197 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1208 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1198 contents of the revision. Two revisions matching their ``diff`` will
1209 contents of the revision. Two revisions matching their ``diff`` will
1199 also match their ``files``.
1210 also match their ``files``.
1200
1211
1201 Special fields are ``summary`` and ``metadata``:
1212 Special fields are ``summary`` and ``metadata``:
1202 ``summary`` matches the first line of the description.
1213 ``summary`` matches the first line of the description.
1203 ``metadata`` is equivalent to matching ``description user date``
1214 ``metadata`` is equivalent to matching ``description user date``
1204 (i.e. it matches the main metadata fields).
1215 (i.e. it matches the main metadata fields).
1205
1216
1206 ``metadata`` is the default field which is used when no fields are
1217 ``metadata`` is the default field which is used when no fields are
1207 specified. You can match more than one field at a time.
1218 specified. You can match more than one field at a time.
1208 """
1219 """
1209 # i18n: "matching" is a keyword
1220 # i18n: "matching" is a keyword
1210 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1221 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1211
1222
1212 revs = getset(repo, repo.changelog, l[0])
1223 revs = getset(repo, repo.changelog, l[0])
1213
1224
1214 fieldlist = ['metadata']
1225 fieldlist = ['metadata']
1215 if len(l) > 1:
1226 if len(l) > 1:
1216 fieldlist = getstring(l[1],
1227 fieldlist = getstring(l[1],
1217 # i18n: "matching" is a keyword
1228 # i18n: "matching" is a keyword
1218 _("matching requires a string "
1229 _("matching requires a string "
1219 "as its second argument")).split()
1230 "as its second argument")).split()
1220
1231
1221 # Make sure that there are no repeated fields,
1232 # Make sure that there are no repeated fields,
1222 # expand the 'special' 'metadata' field type
1233 # expand the 'special' 'metadata' field type
1223 # and check the 'files' whenever we check the 'diff'
1234 # and check the 'files' whenever we check the 'diff'
1224 fields = []
1235 fields = []
1225 for field in fieldlist:
1236 for field in fieldlist:
1226 if field == 'metadata':
1237 if field == 'metadata':
1227 fields += ['user', 'description', 'date']
1238 fields += ['user', 'description', 'date']
1228 elif field == 'diff':
1239 elif field == 'diff':
1229 # a revision matching the diff must also match the files
1240 # a revision matching the diff must also match the files
1230 # since matching the diff is very costly, make sure to
1241 # since matching the diff is very costly, make sure to
1231 # also match the files first
1242 # also match the files first
1232 fields += ['files', 'diff']
1243 fields += ['files', 'diff']
1233 else:
1244 else:
1234 if field == 'author':
1245 if field == 'author':
1235 field = 'user'
1246 field = 'user'
1236 fields.append(field)
1247 fields.append(field)
1237 fields = set(fields)
1248 fields = set(fields)
1238 if 'summary' in fields and 'description' in fields:
1249 if 'summary' in fields and 'description' in fields:
1239 # If a revision matches its description it also matches its summary
1250 # If a revision matches its description it also matches its summary
1240 fields.discard('summary')
1251 fields.discard('summary')
1241
1252
1242 # We may want to match more than one field
1253 # We may want to match more than one field
1243 # Not all fields take the same amount of time to be matched
1254 # Not all fields take the same amount of time to be matched
1244 # Sort the selected fields in order of increasing matching cost
1255 # Sort the selected fields in order of increasing matching cost
1245 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1256 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1246 'files', 'description', 'substate', 'diff']
1257 'files', 'description', 'substate', 'diff']
1247 def fieldkeyfunc(f):
1258 def fieldkeyfunc(f):
1248 try:
1259 try:
1249 return fieldorder.index(f)
1260 return fieldorder.index(f)
1250 except ValueError:
1261 except ValueError:
1251 # assume an unknown field is very costly
1262 # assume an unknown field is very costly
1252 return len(fieldorder)
1263 return len(fieldorder)
1253 fields = list(fields)
1264 fields = list(fields)
1254 fields.sort(key=fieldkeyfunc)
1265 fields.sort(key=fieldkeyfunc)
1255
1266
1256 # Each field will be matched with its own "getfield" function
1267 # Each field will be matched with its own "getfield" function
1257 # which will be added to the getfieldfuncs array of functions
1268 # which will be added to the getfieldfuncs array of functions
1258 getfieldfuncs = []
1269 getfieldfuncs = []
1259 _funcs = {
1270 _funcs = {
1260 'user': lambda r: repo[r].user(),
1271 'user': lambda r: repo[r].user(),
1261 'branch': lambda r: repo[r].branch(),
1272 'branch': lambda r: repo[r].branch(),
1262 'date': lambda r: repo[r].date(),
1273 'date': lambda r: repo[r].date(),
1263 'description': lambda r: repo[r].description(),
1274 'description': lambda r: repo[r].description(),
1264 'files': lambda r: repo[r].files(),
1275 'files': lambda r: repo[r].files(),
1265 'parents': lambda r: repo[r].parents(),
1276 'parents': lambda r: repo[r].parents(),
1266 'phase': lambda r: repo[r].phase(),
1277 'phase': lambda r: repo[r].phase(),
1267 'substate': lambda r: repo[r].substate,
1278 'substate': lambda r: repo[r].substate,
1268 'summary': lambda r: repo[r].description().splitlines()[0],
1279 'summary': lambda r: repo[r].description().splitlines()[0],
1269 'diff': lambda r: list(repo[r].diff(git=True),)
1280 'diff': lambda r: list(repo[r].diff(git=True),)
1270 }
1281 }
1271 for info in fields:
1282 for info in fields:
1272 getfield = _funcs.get(info, None)
1283 getfield = _funcs.get(info, None)
1273 if getfield is None:
1284 if getfield is None:
1274 raise error.ParseError(
1285 raise error.ParseError(
1275 # i18n: "matching" is a keyword
1286 # i18n: "matching" is a keyword
1276 _("unexpected field name passed to matching: %s") % info)
1287 _("unexpected field name passed to matching: %s") % info)
1277 getfieldfuncs.append(getfield)
1288 getfieldfuncs.append(getfield)
1278 # convert the getfield array of functions into a "getinfo" function
1289 # convert the getfield array of functions into a "getinfo" function
1279 # which returns an array of field values (or a single value if there
1290 # which returns an array of field values (or a single value if there
1280 # is only one field to match)
1291 # is only one field to match)
1281 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1292 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1282
1293
1283 matches = set()
1294 matches = set()
1284 for rev in revs:
1295 for rev in revs:
1285 target = getinfo(rev)
1296 target = getinfo(rev)
1286 for r in subset:
1297 for r in subset:
1287 match = True
1298 match = True
1288 for n, f in enumerate(getfieldfuncs):
1299 for n, f in enumerate(getfieldfuncs):
1289 if target[n] != f(r):
1300 if target[n] != f(r):
1290 match = False
1301 match = False
1291 break
1302 break
1292 if match:
1303 if match:
1293 matches.add(r)
1304 matches.add(r)
1294 return [r for r in subset if r in matches]
1305 return [r for r in subset if r in matches]
1295
1306
1296 def reverse(repo, subset, x):
1307 def reverse(repo, subset, x):
1297 """``reverse(set)``
1308 """``reverse(set)``
1298 Reverse order of set.
1309 Reverse order of set.
1299 """
1310 """
1300 l = getset(repo, subset, x)
1311 l = getset(repo, subset, x)
1301 if not isinstance(l, list):
1312 if not isinstance(l, list):
1302 l = list(l)
1313 l = list(l)
1303 l.reverse()
1314 l.reverse()
1304 return l
1315 return l
1305
1316
1306 def roots(repo, subset, x):
1317 def roots(repo, subset, x):
1307 """``roots(set)``
1318 """``roots(set)``
1308 Changesets in set with no parent changeset in set.
1319 Changesets in set with no parent changeset in set.
1309 """
1320 """
1310 s = set(getset(repo, repo.changelog, x))
1321 s = set(getset(repo, repo.changelog, x))
1311 subset = [r for r in subset if r in s]
1322 subset = [r for r in subset if r in s]
1312 cs = _children(repo, subset, s)
1323 cs = _children(repo, subset, s)
1313 return [r for r in subset if r not in cs]
1324 return [r for r in subset if r not in cs]
1314
1325
1315 def secret(repo, subset, x):
1326 def secret(repo, subset, x):
1316 """``secret()``
1327 """``secret()``
1317 Changeset in secret phase."""
1328 Changeset in secret phase."""
1318 # i18n: "secret" is a keyword
1329 # i18n: "secret" is a keyword
1319 getargs(x, 0, 0, _("secret takes no arguments"))
1330 getargs(x, 0, 0, _("secret takes no arguments"))
1320 pc = repo._phasecache
1331 pc = repo._phasecache
1321 return [r for r in subset if pc.phase(repo, r) == phases.secret]
1332 return [r for r in subset if pc.phase(repo, r) == phases.secret]
1322
1333
1323 def sort(repo, subset, x):
1334 def sort(repo, subset, x):
1324 """``sort(set[, [-]key...])``
1335 """``sort(set[, [-]key...])``
1325 Sort set by keys. The default sort order is ascending, specify a key
1336 Sort set by keys. The default sort order is ascending, specify a key
1326 as ``-key`` to sort in descending order.
1337 as ``-key`` to sort in descending order.
1327
1338
1328 The keys can be:
1339 The keys can be:
1329
1340
1330 - ``rev`` for the revision number,
1341 - ``rev`` for the revision number,
1331 - ``branch`` for the branch name,
1342 - ``branch`` for the branch name,
1332 - ``desc`` for the commit message (description),
1343 - ``desc`` for the commit message (description),
1333 - ``user`` for user name (``author`` can be used as an alias),
1344 - ``user`` for user name (``author`` can be used as an alias),
1334 - ``date`` for the commit date
1345 - ``date`` for the commit date
1335 """
1346 """
1336 # i18n: "sort" is a keyword
1347 # i18n: "sort" is a keyword
1337 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1348 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1338 keys = "rev"
1349 keys = "rev"
1339 if len(l) == 2:
1350 if len(l) == 2:
1340 # i18n: "sort" is a keyword
1351 # i18n: "sort" is a keyword
1341 keys = getstring(l[1], _("sort spec must be a string"))
1352 keys = getstring(l[1], _("sort spec must be a string"))
1342
1353
1343 s = l[0]
1354 s = l[0]
1344 keys = keys.split()
1355 keys = keys.split()
1345 l = []
1356 l = []
1346 def invert(s):
1357 def invert(s):
1347 return "".join(chr(255 - ord(c)) for c in s)
1358 return "".join(chr(255 - ord(c)) for c in s)
1348 for r in getset(repo, subset, s):
1359 for r in getset(repo, subset, s):
1349 c = repo[r]
1360 c = repo[r]
1350 e = []
1361 e = []
1351 for k in keys:
1362 for k in keys:
1352 if k == 'rev':
1363 if k == 'rev':
1353 e.append(r)
1364 e.append(r)
1354 elif k == '-rev':
1365 elif k == '-rev':
1355 e.append(-r)
1366 e.append(-r)
1356 elif k == 'branch':
1367 elif k == 'branch':
1357 e.append(c.branch())
1368 e.append(c.branch())
1358 elif k == '-branch':
1369 elif k == '-branch':
1359 e.append(invert(c.branch()))
1370 e.append(invert(c.branch()))
1360 elif k == 'desc':
1371 elif k == 'desc':
1361 e.append(c.description())
1372 e.append(c.description())
1362 elif k == '-desc':
1373 elif k == '-desc':
1363 e.append(invert(c.description()))
1374 e.append(invert(c.description()))
1364 elif k in 'user author':
1375 elif k in 'user author':
1365 e.append(c.user())
1376 e.append(c.user())
1366 elif k in '-user -author':
1377 elif k in '-user -author':
1367 e.append(invert(c.user()))
1378 e.append(invert(c.user()))
1368 elif k == 'date':
1379 elif k == 'date':
1369 e.append(c.date()[0])
1380 e.append(c.date()[0])
1370 elif k == '-date':
1381 elif k == '-date':
1371 e.append(-c.date()[0])
1382 e.append(-c.date()[0])
1372 else:
1383 else:
1373 raise error.ParseError(_("unknown sort key %r") % k)
1384 raise error.ParseError(_("unknown sort key %r") % k)
1374 e.append(r)
1385 e.append(r)
1375 l.append(e)
1386 l.append(e)
1376 l.sort()
1387 l.sort()
1377 return [e[-1] for e in l]
1388 return [e[-1] for e in l]
1378
1389
1379 def _stringmatcher(pattern):
1390 def _stringmatcher(pattern):
1380 """
1391 """
1381 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1392 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1382 returns the matcher name, pattern, and matcher function.
1393 returns the matcher name, pattern, and matcher function.
1383 missing or unknown prefixes are treated as literal matches.
1394 missing or unknown prefixes are treated as literal matches.
1384
1395
1385 helper for tests:
1396 helper for tests:
1386 >>> def test(pattern, *tests):
1397 >>> def test(pattern, *tests):
1387 ... kind, pattern, matcher = _stringmatcher(pattern)
1398 ... kind, pattern, matcher = _stringmatcher(pattern)
1388 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1399 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1389
1400
1390 exact matching (no prefix):
1401 exact matching (no prefix):
1391 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1402 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1392 ('literal', 'abcdefg', [False, False, True])
1403 ('literal', 'abcdefg', [False, False, True])
1393
1404
1394 regex matching ('re:' prefix)
1405 regex matching ('re:' prefix)
1395 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1406 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1396 ('re', 'a.+b', [False, False, True])
1407 ('re', 'a.+b', [False, False, True])
1397
1408
1398 force exact matches ('literal:' prefix)
1409 force exact matches ('literal:' prefix)
1399 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1410 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1400 ('literal', 're:foobar', [False, True])
1411 ('literal', 're:foobar', [False, True])
1401
1412
1402 unknown prefixes are ignored and treated as literals
1413 unknown prefixes are ignored and treated as literals
1403 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1414 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1404 ('literal', 'foo:bar', [False, False, True])
1415 ('literal', 'foo:bar', [False, False, True])
1405 """
1416 """
1406 if pattern.startswith('re:'):
1417 if pattern.startswith('re:'):
1407 pattern = pattern[3:]
1418 pattern = pattern[3:]
1408 try:
1419 try:
1409 regex = re.compile(pattern)
1420 regex = re.compile(pattern)
1410 except re.error, e:
1421 except re.error, e:
1411 raise error.ParseError(_('invalid regular expression: %s')
1422 raise error.ParseError(_('invalid regular expression: %s')
1412 % e)
1423 % e)
1413 return 're', pattern, regex.search
1424 return 're', pattern, regex.search
1414 elif pattern.startswith('literal:'):
1425 elif pattern.startswith('literal:'):
1415 pattern = pattern[8:]
1426 pattern = pattern[8:]
1416 return 'literal', pattern, pattern.__eq__
1427 return 'literal', pattern, pattern.__eq__
1417
1428
1418 def _substringmatcher(pattern):
1429 def _substringmatcher(pattern):
1419 kind, pattern, matcher = _stringmatcher(pattern)
1430 kind, pattern, matcher = _stringmatcher(pattern)
1420 if kind == 'literal':
1431 if kind == 'literal':
1421 matcher = lambda s: pattern in s
1432 matcher = lambda s: pattern in s
1422 return kind, pattern, matcher
1433 return kind, pattern, matcher
1423
1434
1424 def tag(repo, subset, x):
1435 def tag(repo, subset, x):
1425 """``tag([name])``
1436 """``tag([name])``
1426 The specified tag by name, or all tagged revisions if no name is given.
1437 The specified tag by name, or all tagged revisions if no name is given.
1427 """
1438 """
1428 # i18n: "tag" is a keyword
1439 # i18n: "tag" is a keyword
1429 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1440 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1430 cl = repo.changelog
1441 cl = repo.changelog
1431 if args:
1442 if args:
1432 pattern = getstring(args[0],
1443 pattern = getstring(args[0],
1433 # i18n: "tag" is a keyword
1444 # i18n: "tag" is a keyword
1434 _('the argument to tag must be a string'))
1445 _('the argument to tag must be a string'))
1435 kind, pattern, matcher = _stringmatcher(pattern)
1446 kind, pattern, matcher = _stringmatcher(pattern)
1436 if kind == 'literal':
1447 if kind == 'literal':
1437 # avoid resolving all tags
1448 # avoid resolving all tags
1438 tn = repo._tagscache.tags.get(pattern, None)
1449 tn = repo._tagscache.tags.get(pattern, None)
1439 if tn is None:
1450 if tn is None:
1440 raise util.Abort(_("tag '%s' does not exist") % pattern)
1451 raise util.Abort(_("tag '%s' does not exist") % pattern)
1441 s = set([repo[tn].rev()])
1452 s = set([repo[tn].rev()])
1442 else:
1453 else:
1443 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1454 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1444 if not s:
1455 if not s:
1445 raise util.Abort(_("no tags exist that match '%s'") % pattern)
1456 raise util.Abort(_("no tags exist that match '%s'") % pattern)
1446 else:
1457 else:
1447 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1458 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1448 return [r for r in subset if r in s]
1459 return [r for r in subset if r in s]
1449
1460
1450 def tagged(repo, subset, x):
1461 def tagged(repo, subset, x):
1451 return tag(repo, subset, x)
1462 return tag(repo, subset, x)
1452
1463
1453 def unstable(repo, subset, x):
1464 def unstable(repo, subset, x):
1454 """``unstable()``
1465 """``unstable()``
1455 Non-obsolete changesets with obsolete ancestors.
1466 Non-obsolete changesets with obsolete ancestors.
1456 """
1467 """
1457 # i18n: "unstable" is a keyword
1468 # i18n: "unstable" is a keyword
1458 getargs(x, 0, 0, _("unstable takes no arguments"))
1469 getargs(x, 0, 0, _("unstable takes no arguments"))
1459 unstables = obsmod.getrevs(repo, 'unstable')
1470 unstables = obsmod.getrevs(repo, 'unstable')
1460 return [r for r in subset if r in unstables]
1471 return [r for r in subset if r in unstables]
1461
1472
1462
1473
1463 def user(repo, subset, x):
1474 def user(repo, subset, x):
1464 """``user(string)``
1475 """``user(string)``
1465 User name contains string. The match is case-insensitive.
1476 User name contains string. The match is case-insensitive.
1466
1477
1467 If `string` starts with `re:`, the remainder of the string is treated as
1478 If `string` starts with `re:`, the remainder of the string is treated as
1468 a regular expression. To match a user that actually contains `re:`, use
1479 a regular expression. To match a user that actually contains `re:`, use
1469 the prefix `literal:`.
1480 the prefix `literal:`.
1470 """
1481 """
1471 return author(repo, subset, x)
1482 return author(repo, subset, x)
1472
1483
1473 # for internal use
1484 # for internal use
1474 def _list(repo, subset, x):
1485 def _list(repo, subset, x):
1475 s = getstring(x, "internal error")
1486 s = getstring(x, "internal error")
1476 if not s:
1487 if not s:
1477 return []
1488 return []
1478 if not isinstance(subset, set):
1489 if not isinstance(subset, set):
1479 subset = set(subset)
1490 subset = set(subset)
1480 ls = [repo[r].rev() for r in s.split('\0')]
1491 ls = [repo[r].rev() for r in s.split('\0')]
1481 return [r for r in ls if r in subset]
1492 return [r for r in ls if r in subset]
1482
1493
1483 symbols = {
1494 symbols = {
1484 "adds": adds,
1495 "adds": adds,
1485 "all": getall,
1496 "all": getall,
1486 "ancestor": ancestor,
1497 "ancestor": ancestor,
1487 "ancestors": ancestors,
1498 "ancestors": ancestors,
1488 "_firstancestors": _firstancestors,
1499 "_firstancestors": _firstancestors,
1489 "author": author,
1500 "author": author,
1490 "bisect": bisect,
1501 "bisect": bisect,
1491 "bisected": bisected,
1502 "bisected": bisected,
1492 "bookmark": bookmark,
1503 "bookmark": bookmark,
1493 "branch": branch,
1504 "branch": branch,
1494 "branchpoint": branchpoint,
1505 "branchpoint": branchpoint,
1506 "bumped": bumped,
1495 "children": children,
1507 "children": children,
1496 "closed": closed,
1508 "closed": closed,
1497 "contains": contains,
1509 "contains": contains,
1498 "converted": converted,
1510 "converted": converted,
1499 "date": date,
1511 "date": date,
1500 "desc": desc,
1512 "desc": desc,
1501 "descendants": descendants,
1513 "descendants": descendants,
1502 "_firstdescendants": _firstdescendants,
1514 "_firstdescendants": _firstdescendants,
1503 "destination": destination,
1515 "destination": destination,
1504 "draft": draft,
1516 "draft": draft,
1505 "extinct": extinct,
1517 "extinct": extinct,
1506 "extra": extra,
1518 "extra": extra,
1507 "file": hasfile,
1519 "file": hasfile,
1508 "filelog": filelog,
1520 "filelog": filelog,
1509 "first": first,
1521 "first": first,
1510 "follow": follow,
1522 "follow": follow,
1511 "_followfirst": _followfirst,
1523 "_followfirst": _followfirst,
1512 "grep": grep,
1524 "grep": grep,
1513 "head": head,
1525 "head": head,
1514 "heads": heads,
1526 "heads": heads,
1515 "hidden": hidden,
1527 "hidden": hidden,
1516 "id": node_,
1528 "id": node_,
1517 "keyword": keyword,
1529 "keyword": keyword,
1518 "last": last,
1530 "last": last,
1519 "limit": limit,
1531 "limit": limit,
1520 "_matchfiles": _matchfiles,
1532 "_matchfiles": _matchfiles,
1521 "max": maxrev,
1533 "max": maxrev,
1522 "merge": merge,
1534 "merge": merge,
1523 "min": minrev,
1535 "min": minrev,
1524 "modifies": modifies,
1536 "modifies": modifies,
1525 "obsolete": obsolete,
1537 "obsolete": obsolete,
1526 "origin": origin,
1538 "origin": origin,
1527 "outgoing": outgoing,
1539 "outgoing": outgoing,
1528 "p1": p1,
1540 "p1": p1,
1529 "p2": p2,
1541 "p2": p2,
1530 "parents": parents,
1542 "parents": parents,
1531 "present": present,
1543 "present": present,
1532 "public": public,
1544 "public": public,
1533 "remote": remote,
1545 "remote": remote,
1534 "removes": removes,
1546 "removes": removes,
1535 "rev": rev,
1547 "rev": rev,
1536 "reverse": reverse,
1548 "reverse": reverse,
1537 "roots": roots,
1549 "roots": roots,
1538 "sort": sort,
1550 "sort": sort,
1539 "secret": secret,
1551 "secret": secret,
1540 "matching": matching,
1552 "matching": matching,
1541 "tag": tag,
1553 "tag": tag,
1542 "tagged": tagged,
1554 "tagged": tagged,
1543 "user": user,
1555 "user": user,
1544 "unstable": unstable,
1556 "unstable": unstable,
1545 "_list": _list,
1557 "_list": _list,
1546 }
1558 }
1547
1559
1548 methods = {
1560 methods = {
1549 "range": rangeset,
1561 "range": rangeset,
1550 "dagrange": dagrange,
1562 "dagrange": dagrange,
1551 "string": stringset,
1563 "string": stringset,
1552 "symbol": symbolset,
1564 "symbol": symbolset,
1553 "and": andset,
1565 "and": andset,
1554 "or": orset,
1566 "or": orset,
1555 "not": notset,
1567 "not": notset,
1556 "list": listset,
1568 "list": listset,
1557 "func": func,
1569 "func": func,
1558 "ancestor": ancestorspec,
1570 "ancestor": ancestorspec,
1559 "parent": parentspec,
1571 "parent": parentspec,
1560 "parentpost": p1,
1572 "parentpost": p1,
1561 }
1573 }
1562
1574
1563 def optimize(x, small):
1575 def optimize(x, small):
1564 if x is None:
1576 if x is None:
1565 return 0, x
1577 return 0, x
1566
1578
1567 smallbonus = 1
1579 smallbonus = 1
1568 if small:
1580 if small:
1569 smallbonus = .5
1581 smallbonus = .5
1570
1582
1571 op = x[0]
1583 op = x[0]
1572 if op == 'minus':
1584 if op == 'minus':
1573 return optimize(('and', x[1], ('not', x[2])), small)
1585 return optimize(('and', x[1], ('not', x[2])), small)
1574 elif op == 'dagrangepre':
1586 elif op == 'dagrangepre':
1575 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
1587 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
1576 elif op == 'dagrangepost':
1588 elif op == 'dagrangepost':
1577 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
1589 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
1578 elif op == 'rangepre':
1590 elif op == 'rangepre':
1579 return optimize(('range', ('string', '0'), x[1]), small)
1591 return optimize(('range', ('string', '0'), x[1]), small)
1580 elif op == 'rangepost':
1592 elif op == 'rangepost':
1581 return optimize(('range', x[1], ('string', 'tip')), small)
1593 return optimize(('range', x[1], ('string', 'tip')), small)
1582 elif op == 'negate':
1594 elif op == 'negate':
1583 return optimize(('string',
1595 return optimize(('string',
1584 '-' + getstring(x[1], _("can't negate that"))), small)
1596 '-' + getstring(x[1], _("can't negate that"))), small)
1585 elif op in 'string symbol negate':
1597 elif op in 'string symbol negate':
1586 return smallbonus, x # single revisions are small
1598 return smallbonus, x # single revisions are small
1587 elif op == 'and':
1599 elif op == 'and':
1588 wa, ta = optimize(x[1], True)
1600 wa, ta = optimize(x[1], True)
1589 wb, tb = optimize(x[2], True)
1601 wb, tb = optimize(x[2], True)
1590 w = min(wa, wb)
1602 w = min(wa, wb)
1591 if wa > wb:
1603 if wa > wb:
1592 return w, (op, tb, ta)
1604 return w, (op, tb, ta)
1593 return w, (op, ta, tb)
1605 return w, (op, ta, tb)
1594 elif op == 'or':
1606 elif op == 'or':
1595 wa, ta = optimize(x[1], False)
1607 wa, ta = optimize(x[1], False)
1596 wb, tb = optimize(x[2], False)
1608 wb, tb = optimize(x[2], False)
1597 if wb < wa:
1609 if wb < wa:
1598 wb, wa = wa, wb
1610 wb, wa = wa, wb
1599 return max(wa, wb), (op, ta, tb)
1611 return max(wa, wb), (op, ta, tb)
1600 elif op == 'not':
1612 elif op == 'not':
1601 o = optimize(x[1], not small)
1613 o = optimize(x[1], not small)
1602 return o[0], (op, o[1])
1614 return o[0], (op, o[1])
1603 elif op == 'parentpost':
1615 elif op == 'parentpost':
1604 o = optimize(x[1], small)
1616 o = optimize(x[1], small)
1605 return o[0], (op, o[1])
1617 return o[0], (op, o[1])
1606 elif op == 'group':
1618 elif op == 'group':
1607 return optimize(x[1], small)
1619 return optimize(x[1], small)
1608 elif op in 'dagrange range list parent ancestorspec':
1620 elif op in 'dagrange range list parent ancestorspec':
1609 if op == 'parent':
1621 if op == 'parent':
1610 # x^:y means (x^) : y, not x ^ (:y)
1622 # x^:y means (x^) : y, not x ^ (:y)
1611 post = ('parentpost', x[1])
1623 post = ('parentpost', x[1])
1612 if x[2][0] == 'dagrangepre':
1624 if x[2][0] == 'dagrangepre':
1613 return optimize(('dagrange', post, x[2][1]), small)
1625 return optimize(('dagrange', post, x[2][1]), small)
1614 elif x[2][0] == 'rangepre':
1626 elif x[2][0] == 'rangepre':
1615 return optimize(('range', post, x[2][1]), small)
1627 return optimize(('range', post, x[2][1]), small)
1616
1628
1617 wa, ta = optimize(x[1], small)
1629 wa, ta = optimize(x[1], small)
1618 wb, tb = optimize(x[2], small)
1630 wb, tb = optimize(x[2], small)
1619 return wa + wb, (op, ta, tb)
1631 return wa + wb, (op, ta, tb)
1620 elif op == 'func':
1632 elif op == 'func':
1621 f = getstring(x[1], _("not a symbol"))
1633 f = getstring(x[1], _("not a symbol"))
1622 wa, ta = optimize(x[2], small)
1634 wa, ta = optimize(x[2], small)
1623 if f in ("author branch closed date desc file grep keyword "
1635 if f in ("author branch closed date desc file grep keyword "
1624 "outgoing user"):
1636 "outgoing user"):
1625 w = 10 # slow
1637 w = 10 # slow
1626 elif f in "modifies adds removes":
1638 elif f in "modifies adds removes":
1627 w = 30 # slower
1639 w = 30 # slower
1628 elif f == "contains":
1640 elif f == "contains":
1629 w = 100 # very slow
1641 w = 100 # very slow
1630 elif f == "ancestor":
1642 elif f == "ancestor":
1631 w = 1 * smallbonus
1643 w = 1 * smallbonus
1632 elif f in "reverse limit first":
1644 elif f in "reverse limit first":
1633 w = 0
1645 w = 0
1634 elif f in "sort":
1646 elif f in "sort":
1635 w = 10 # assume most sorts look at changelog
1647 w = 10 # assume most sorts look at changelog
1636 else:
1648 else:
1637 w = 1
1649 w = 1
1638 return w + wa, (op, x[1], ta)
1650 return w + wa, (op, x[1], ta)
1639 return 1, x
1651 return 1, x
1640
1652
1641 _aliasarg = ('func', ('symbol', '_aliasarg'))
1653 _aliasarg = ('func', ('symbol', '_aliasarg'))
1642 def _getaliasarg(tree):
1654 def _getaliasarg(tree):
1643 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
1655 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
1644 return X, None otherwise.
1656 return X, None otherwise.
1645 """
1657 """
1646 if (len(tree) == 3 and tree[:2] == _aliasarg
1658 if (len(tree) == 3 and tree[:2] == _aliasarg
1647 and tree[2][0] == 'string'):
1659 and tree[2][0] == 'string'):
1648 return tree[2][1]
1660 return tree[2][1]
1649 return None
1661 return None
1650
1662
1651 def _checkaliasarg(tree, known=None):
1663 def _checkaliasarg(tree, known=None):
1652 """Check tree contains no _aliasarg construct or only ones which
1664 """Check tree contains no _aliasarg construct or only ones which
1653 value is in known. Used to avoid alias placeholders injection.
1665 value is in known. Used to avoid alias placeholders injection.
1654 """
1666 """
1655 if isinstance(tree, tuple):
1667 if isinstance(tree, tuple):
1656 arg = _getaliasarg(tree)
1668 arg = _getaliasarg(tree)
1657 if arg is not None and (not known or arg not in known):
1669 if arg is not None and (not known or arg not in known):
1658 raise error.ParseError(_("not a function: %s") % '_aliasarg')
1670 raise error.ParseError(_("not a function: %s") % '_aliasarg')
1659 for t in tree:
1671 for t in tree:
1660 _checkaliasarg(t, known)
1672 _checkaliasarg(t, known)
1661
1673
1662 class revsetalias(object):
1674 class revsetalias(object):
1663 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
1675 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
1664 args = None
1676 args = None
1665
1677
1666 def __init__(self, name, value):
1678 def __init__(self, name, value):
1667 '''Aliases like:
1679 '''Aliases like:
1668
1680
1669 h = heads(default)
1681 h = heads(default)
1670 b($1) = ancestors($1) - ancestors(default)
1682 b($1) = ancestors($1) - ancestors(default)
1671 '''
1683 '''
1672 m = self.funcre.search(name)
1684 m = self.funcre.search(name)
1673 if m:
1685 if m:
1674 self.name = m.group(1)
1686 self.name = m.group(1)
1675 self.tree = ('func', ('symbol', m.group(1)))
1687 self.tree = ('func', ('symbol', m.group(1)))
1676 self.args = [x.strip() for x in m.group(2).split(',')]
1688 self.args = [x.strip() for x in m.group(2).split(',')]
1677 for arg in self.args:
1689 for arg in self.args:
1678 # _aliasarg() is an unknown symbol only used separate
1690 # _aliasarg() is an unknown symbol only used separate
1679 # alias argument placeholders from regular strings.
1691 # alias argument placeholders from regular strings.
1680 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
1692 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
1681 else:
1693 else:
1682 self.name = name
1694 self.name = name
1683 self.tree = ('symbol', name)
1695 self.tree = ('symbol', name)
1684
1696
1685 self.replacement, pos = parse(value)
1697 self.replacement, pos = parse(value)
1686 if pos != len(value):
1698 if pos != len(value):
1687 raise error.ParseError(_('invalid token'), pos)
1699 raise error.ParseError(_('invalid token'), pos)
1688 # Check for placeholder injection
1700 # Check for placeholder injection
1689 _checkaliasarg(self.replacement, self.args)
1701 _checkaliasarg(self.replacement, self.args)
1690
1702
1691 def _getalias(aliases, tree):
1703 def _getalias(aliases, tree):
1692 """If tree looks like an unexpanded alias, return it. Return None
1704 """If tree looks like an unexpanded alias, return it. Return None
1693 otherwise.
1705 otherwise.
1694 """
1706 """
1695 if isinstance(tree, tuple) and tree:
1707 if isinstance(tree, tuple) and tree:
1696 if tree[0] == 'symbol' and len(tree) == 2:
1708 if tree[0] == 'symbol' and len(tree) == 2:
1697 name = tree[1]
1709 name = tree[1]
1698 alias = aliases.get(name)
1710 alias = aliases.get(name)
1699 if alias and alias.args is None and alias.tree == tree:
1711 if alias and alias.args is None and alias.tree == tree:
1700 return alias
1712 return alias
1701 if tree[0] == 'func' and len(tree) > 1:
1713 if tree[0] == 'func' and len(tree) > 1:
1702 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
1714 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
1703 name = tree[1][1]
1715 name = tree[1][1]
1704 alias = aliases.get(name)
1716 alias = aliases.get(name)
1705 if alias and alias.args is not None and alias.tree == tree[:2]:
1717 if alias and alias.args is not None and alias.tree == tree[:2]:
1706 return alias
1718 return alias
1707 return None
1719 return None
1708
1720
1709 def _expandargs(tree, args):
1721 def _expandargs(tree, args):
1710 """Replace _aliasarg instances with the substitution value of the
1722 """Replace _aliasarg instances with the substitution value of the
1711 same name in args, recursively.
1723 same name in args, recursively.
1712 """
1724 """
1713 if not tree or not isinstance(tree, tuple):
1725 if not tree or not isinstance(tree, tuple):
1714 return tree
1726 return tree
1715 arg = _getaliasarg(tree)
1727 arg = _getaliasarg(tree)
1716 if arg is not None:
1728 if arg is not None:
1717 return args[arg]
1729 return args[arg]
1718 return tuple(_expandargs(t, args) for t in tree)
1730 return tuple(_expandargs(t, args) for t in tree)
1719
1731
1720 def _expandaliases(aliases, tree, expanding, cache):
1732 def _expandaliases(aliases, tree, expanding, cache):
1721 """Expand aliases in tree, recursively.
1733 """Expand aliases in tree, recursively.
1722
1734
1723 'aliases' is a dictionary mapping user defined aliases to
1735 'aliases' is a dictionary mapping user defined aliases to
1724 revsetalias objects.
1736 revsetalias objects.
1725 """
1737 """
1726 if not isinstance(tree, tuple):
1738 if not isinstance(tree, tuple):
1727 # Do not expand raw strings
1739 # Do not expand raw strings
1728 return tree
1740 return tree
1729 alias = _getalias(aliases, tree)
1741 alias = _getalias(aliases, tree)
1730 if alias is not None:
1742 if alias is not None:
1731 if alias in expanding:
1743 if alias in expanding:
1732 raise error.ParseError(_('infinite expansion of revset alias "%s" '
1744 raise error.ParseError(_('infinite expansion of revset alias "%s" '
1733 'detected') % alias.name)
1745 'detected') % alias.name)
1734 expanding.append(alias)
1746 expanding.append(alias)
1735 if alias.name not in cache:
1747 if alias.name not in cache:
1736 cache[alias.name] = _expandaliases(aliases, alias.replacement,
1748 cache[alias.name] = _expandaliases(aliases, alias.replacement,
1737 expanding, cache)
1749 expanding, cache)
1738 result = cache[alias.name]
1750 result = cache[alias.name]
1739 expanding.pop()
1751 expanding.pop()
1740 if alias.args is not None:
1752 if alias.args is not None:
1741 l = getlist(tree[2])
1753 l = getlist(tree[2])
1742 if len(l) != len(alias.args):
1754 if len(l) != len(alias.args):
1743 raise error.ParseError(
1755 raise error.ParseError(
1744 _('invalid number of arguments: %s') % len(l))
1756 _('invalid number of arguments: %s') % len(l))
1745 l = [_expandaliases(aliases, a, [], cache) for a in l]
1757 l = [_expandaliases(aliases, a, [], cache) for a in l]
1746 result = _expandargs(result, dict(zip(alias.args, l)))
1758 result = _expandargs(result, dict(zip(alias.args, l)))
1747 else:
1759 else:
1748 result = tuple(_expandaliases(aliases, t, expanding, cache)
1760 result = tuple(_expandaliases(aliases, t, expanding, cache)
1749 for t in tree)
1761 for t in tree)
1750 return result
1762 return result
1751
1763
1752 def findaliases(ui, tree):
1764 def findaliases(ui, tree):
1753 _checkaliasarg(tree)
1765 _checkaliasarg(tree)
1754 aliases = {}
1766 aliases = {}
1755 for k, v in ui.configitems('revsetalias'):
1767 for k, v in ui.configitems('revsetalias'):
1756 alias = revsetalias(k, v)
1768 alias = revsetalias(k, v)
1757 aliases[alias.name] = alias
1769 aliases[alias.name] = alias
1758 return _expandaliases(aliases, tree, [], {})
1770 return _expandaliases(aliases, tree, [], {})
1759
1771
1760 parse = parser.parser(tokenize, elements).parse
1772 parse = parser.parser(tokenize, elements).parse
1761
1773
1762 def match(ui, spec):
1774 def match(ui, spec):
1763 if not spec:
1775 if not spec:
1764 raise error.ParseError(_("empty query"))
1776 raise error.ParseError(_("empty query"))
1765 tree, pos = parse(spec)
1777 tree, pos = parse(spec)
1766 if (pos != len(spec)):
1778 if (pos != len(spec)):
1767 raise error.ParseError(_("invalid token"), pos)
1779 raise error.ParseError(_("invalid token"), pos)
1768 if ui:
1780 if ui:
1769 tree = findaliases(ui, tree)
1781 tree = findaliases(ui, tree)
1770 weight, tree = optimize(tree, True)
1782 weight, tree = optimize(tree, True)
1771 def mfunc(repo, subset):
1783 def mfunc(repo, subset):
1772 return getset(repo, subset, tree)
1784 return getset(repo, subset, tree)
1773 return mfunc
1785 return mfunc
1774
1786
1775 def formatspec(expr, *args):
1787 def formatspec(expr, *args):
1776 '''
1788 '''
1777 This is a convenience function for using revsets internally, and
1789 This is a convenience function for using revsets internally, and
1778 escapes arguments appropriately. Aliases are intentionally ignored
1790 escapes arguments appropriately. Aliases are intentionally ignored
1779 so that intended expression behavior isn't accidentally subverted.
1791 so that intended expression behavior isn't accidentally subverted.
1780
1792
1781 Supported arguments:
1793 Supported arguments:
1782
1794
1783 %r = revset expression, parenthesized
1795 %r = revset expression, parenthesized
1784 %d = int(arg), no quoting
1796 %d = int(arg), no quoting
1785 %s = string(arg), escaped and single-quoted
1797 %s = string(arg), escaped and single-quoted
1786 %b = arg.branch(), escaped and single-quoted
1798 %b = arg.branch(), escaped and single-quoted
1787 %n = hex(arg), single-quoted
1799 %n = hex(arg), single-quoted
1788 %% = a literal '%'
1800 %% = a literal '%'
1789
1801
1790 Prefixing the type with 'l' specifies a parenthesized list of that type.
1802 Prefixing the type with 'l' specifies a parenthesized list of that type.
1791
1803
1792 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
1804 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
1793 '(10 or 11):: and ((this()) or (that()))'
1805 '(10 or 11):: and ((this()) or (that()))'
1794 >>> formatspec('%d:: and not %d::', 10, 20)
1806 >>> formatspec('%d:: and not %d::', 10, 20)
1795 '10:: and not 20::'
1807 '10:: and not 20::'
1796 >>> formatspec('%ld or %ld', [], [1])
1808 >>> formatspec('%ld or %ld', [], [1])
1797 "_list('') or 1"
1809 "_list('') or 1"
1798 >>> formatspec('keyword(%s)', 'foo\\xe9')
1810 >>> formatspec('keyword(%s)', 'foo\\xe9')
1799 "keyword('foo\\\\xe9')"
1811 "keyword('foo\\\\xe9')"
1800 >>> b = lambda: 'default'
1812 >>> b = lambda: 'default'
1801 >>> b.branch = b
1813 >>> b.branch = b
1802 >>> formatspec('branch(%b)', b)
1814 >>> formatspec('branch(%b)', b)
1803 "branch('default')"
1815 "branch('default')"
1804 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
1816 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
1805 "root(_list('a\\x00b\\x00c\\x00d'))"
1817 "root(_list('a\\x00b\\x00c\\x00d'))"
1806 '''
1818 '''
1807
1819
1808 def quote(s):
1820 def quote(s):
1809 return repr(str(s))
1821 return repr(str(s))
1810
1822
1811 def argtype(c, arg):
1823 def argtype(c, arg):
1812 if c == 'd':
1824 if c == 'd':
1813 return str(int(arg))
1825 return str(int(arg))
1814 elif c == 's':
1826 elif c == 's':
1815 return quote(arg)
1827 return quote(arg)
1816 elif c == 'r':
1828 elif c == 'r':
1817 parse(arg) # make sure syntax errors are confined
1829 parse(arg) # make sure syntax errors are confined
1818 return '(%s)' % arg
1830 return '(%s)' % arg
1819 elif c == 'n':
1831 elif c == 'n':
1820 return quote(node.hex(arg))
1832 return quote(node.hex(arg))
1821 elif c == 'b':
1833 elif c == 'b':
1822 return quote(arg.branch())
1834 return quote(arg.branch())
1823
1835
1824 def listexp(s, t):
1836 def listexp(s, t):
1825 l = len(s)
1837 l = len(s)
1826 if l == 0:
1838 if l == 0:
1827 return "_list('')"
1839 return "_list('')"
1828 elif l == 1:
1840 elif l == 1:
1829 return argtype(t, s[0])
1841 return argtype(t, s[0])
1830 elif t == 'd':
1842 elif t == 'd':
1831 return "_list('%s')" % "\0".join(str(int(a)) for a in s)
1843 return "_list('%s')" % "\0".join(str(int(a)) for a in s)
1832 elif t == 's':
1844 elif t == 's':
1833 return "_list('%s')" % "\0".join(s)
1845 return "_list('%s')" % "\0".join(s)
1834 elif t == 'n':
1846 elif t == 'n':
1835 return "_list('%s')" % "\0".join(node.hex(a) for a in s)
1847 return "_list('%s')" % "\0".join(node.hex(a) for a in s)
1836 elif t == 'b':
1848 elif t == 'b':
1837 return "_list('%s')" % "\0".join(a.branch() for a in s)
1849 return "_list('%s')" % "\0".join(a.branch() for a in s)
1838
1850
1839 m = l // 2
1851 m = l // 2
1840 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
1852 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
1841
1853
1842 ret = ''
1854 ret = ''
1843 pos = 0
1855 pos = 0
1844 arg = 0
1856 arg = 0
1845 while pos < len(expr):
1857 while pos < len(expr):
1846 c = expr[pos]
1858 c = expr[pos]
1847 if c == '%':
1859 if c == '%':
1848 pos += 1
1860 pos += 1
1849 d = expr[pos]
1861 d = expr[pos]
1850 if d == '%':
1862 if d == '%':
1851 ret += d
1863 ret += d
1852 elif d in 'dsnbr':
1864 elif d in 'dsnbr':
1853 ret += argtype(d, args[arg])
1865 ret += argtype(d, args[arg])
1854 arg += 1
1866 arg += 1
1855 elif d == 'l':
1867 elif d == 'l':
1856 # a list of some type
1868 # a list of some type
1857 pos += 1
1869 pos += 1
1858 d = expr[pos]
1870 d = expr[pos]
1859 ret += listexp(list(args[arg]), d)
1871 ret += listexp(list(args[arg]), d)
1860 arg += 1
1872 arg += 1
1861 else:
1873 else:
1862 raise util.Abort('unexpected revspec format character %s' % d)
1874 raise util.Abort('unexpected revspec format character %s' % d)
1863 else:
1875 else:
1864 ret += c
1876 ret += c
1865 pos += 1
1877 pos += 1
1866
1878
1867 return ret
1879 return ret
1868
1880
1869 def prettyformat(tree):
1881 def prettyformat(tree):
1870 def _prettyformat(tree, level, lines):
1882 def _prettyformat(tree, level, lines):
1871 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
1883 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
1872 lines.append((level, str(tree)))
1884 lines.append((level, str(tree)))
1873 else:
1885 else:
1874 lines.append((level, '(%s' % tree[0]))
1886 lines.append((level, '(%s' % tree[0]))
1875 for s in tree[1:]:
1887 for s in tree[1:]:
1876 _prettyformat(s, level + 1, lines)
1888 _prettyformat(s, level + 1, lines)
1877 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
1889 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
1878
1890
1879 lines = []
1891 lines = []
1880 _prettyformat(tree, 0, lines)
1892 _prettyformat(tree, 0, lines)
1881 output = '\n'.join((' '*l + s) for l, s in lines)
1893 output = '\n'.join((' '*l + s) for l, s in lines)
1882 return output
1894 return output
1883
1895
1884 # tell hggettext to extract docstrings from these functions:
1896 # tell hggettext to extract docstrings from these functions:
1885 i18nfunctions = symbols.values()
1897 i18nfunctions = symbols.values()
@@ -1,518 +1,534 b''
1 $ cat >> $HGRCPATH << EOF
1 $ cat >> $HGRCPATH << EOF
2 > [extensions]
2 > [extensions]
3 > graphlog=
3 > graphlog=
4 > [phases]
4 > [phases]
5 > # public changeset are not obsolete
5 > # public changeset are not obsolete
6 > publish=false
6 > publish=false
7 > EOF
7 > EOF
8 $ mkcommit() {
8 $ mkcommit() {
9 > echo "$1" > "$1"
9 > echo "$1" > "$1"
10 > hg add "$1"
10 > hg add "$1"
11 > hg ci -m "add $1"
11 > hg ci -m "add $1"
12 > }
12 > }
13 $ getid() {
13 $ getid() {
14 > hg id --debug -ir "desc('$1')"
14 > hg id --debug -ir "desc('$1')"
15 > }
15 > }
16
16
17 $ cat > debugkeys.py <<EOF
17 $ cat > debugkeys.py <<EOF
18 > def reposetup(ui, repo):
18 > def reposetup(ui, repo):
19 > class debugkeysrepo(repo.__class__):
19 > class debugkeysrepo(repo.__class__):
20 > def listkeys(self, namespace):
20 > def listkeys(self, namespace):
21 > ui.write('listkeys %s\n' % (namespace,))
21 > ui.write('listkeys %s\n' % (namespace,))
22 > return super(debugkeysrepo, self).listkeys(namespace)
22 > return super(debugkeysrepo, self).listkeys(namespace)
23 >
23 >
24 > if repo.local():
24 > if repo.local():
25 > repo.__class__ = debugkeysrepo
25 > repo.__class__ = debugkeysrepo
26 > EOF
26 > EOF
27
27
28 $ hg init tmpa
28 $ hg init tmpa
29 $ cd tmpa
29 $ cd tmpa
30 $ mkcommit kill_me
30 $ mkcommit kill_me
31
31
32 Checking that the feature is properly disabled
32 Checking that the feature is properly disabled
33
33
34 $ hg debugobsolete -d '0 0' `getid kill_me` -u babar
34 $ hg debugobsolete -d '0 0' `getid kill_me` -u babar
35 abort: obsolete feature is not enabled on this repo
35 abort: obsolete feature is not enabled on this repo
36 [255]
36 [255]
37
37
38 Enabling it
38 Enabling it
39
39
40 $ cat > ../obs.py << EOF
40 $ cat > ../obs.py << EOF
41 > import mercurial.obsolete
41 > import mercurial.obsolete
42 > mercurial.obsolete._enabled = True
42 > mercurial.obsolete._enabled = True
43 > EOF
43 > EOF
44 $ echo '[extensions]' >> $HGRCPATH
44 $ echo '[extensions]' >> $HGRCPATH
45 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
45 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
46
46
47 Killing a single changeset without replacement
47 Killing a single changeset without replacement
48
48
49 $ hg debugobsolete 0
49 $ hg debugobsolete 0
50 abort: changeset references must be full hexadecimal node identifiers
50 abort: changeset references must be full hexadecimal node identifiers
51 [255]
51 [255]
52 $ hg debugobsolete '00'
52 $ hg debugobsolete '00'
53 abort: changeset references must be full hexadecimal node identifiers
53 abort: changeset references must be full hexadecimal node identifiers
54 [255]
54 [255]
55 $ hg debugobsolete -d '0 0' `getid kill_me` -u babar
55 $ hg debugobsolete -d '0 0' `getid kill_me` -u babar
56 $ hg debugobsolete
56 $ hg debugobsolete
57 97b7c2d76b1845ed3eb988cd612611e72406cef0 0 {'date': '0 0', 'user': 'babar'}
57 97b7c2d76b1845ed3eb988cd612611e72406cef0 0 {'date': '0 0', 'user': 'babar'}
58 $ cd ..
58 $ cd ..
59
59
60 Killing a single changeset with replacement
60 Killing a single changeset with replacement
61
61
62 $ hg init tmpb
62 $ hg init tmpb
63 $ cd tmpb
63 $ cd tmpb
64 $ mkcommit a
64 $ mkcommit a
65 $ mkcommit b
65 $ mkcommit b
66 $ mkcommit original_c
66 $ mkcommit original_c
67 $ hg up "desc('b')"
67 $ hg up "desc('b')"
68 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
68 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
69 $ mkcommit new_c
69 $ mkcommit new_c
70 created new head
70 created new head
71 $ hg log -r 'hidden()' --template '{rev}:{node|short} {desc}\n' --hidden
71 $ hg log -r 'hidden()' --template '{rev}:{node|short} {desc}\n' --hidden
72 $ hg debugobsolete `getid original_c` `getid new_c` -d '56 12'
72 $ hg debugobsolete `getid original_c` `getid new_c` -d '56 12'
73 $ hg log -r 'hidden()' --template '{rev}:{node|short} {desc}\n' --hidden
73 $ hg log -r 'hidden()' --template '{rev}:{node|short} {desc}\n' --hidden
74 2:245bde4270cd add original_c
74 2:245bde4270cd add original_c
75 $ hg debugobsolete
75 $ hg debugobsolete
76 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
76 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
77
77
78 do it again (it read the obsstore before adding new changeset)
78 do it again (it read the obsstore before adding new changeset)
79
79
80 $ hg up '.^'
80 $ hg up '.^'
81 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
81 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
82 $ mkcommit new_2_c
82 $ mkcommit new_2_c
83 created new head
83 created new head
84 $ hg debugobsolete -d '1337 0' `getid new_c` `getid new_2_c`
84 $ hg debugobsolete -d '1337 0' `getid new_c` `getid new_2_c`
85 $ hg debugobsolete
85 $ hg debugobsolete
86 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
86 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
87 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
87 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
88
88
89 Register two markers with a missing node
89 Register two markers with a missing node
90
90
91 $ hg up '.^'
91 $ hg up '.^'
92 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
92 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
93 $ mkcommit new_3_c
93 $ mkcommit new_3_c
94 created new head
94 created new head
95 $ hg debugobsolete -d '1338 0' `getid new_2_c` 1337133713371337133713371337133713371337
95 $ hg debugobsolete -d '1338 0' `getid new_2_c` 1337133713371337133713371337133713371337
96 $ hg debugobsolete -d '1339 0' 1337133713371337133713371337133713371337 `getid new_3_c`
96 $ hg debugobsolete -d '1339 0' 1337133713371337133713371337133713371337 `getid new_3_c`
97 $ hg debugobsolete
97 $ hg debugobsolete
98 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
98 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
99 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
99 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
100 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
100 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
101 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
101 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
102
102
103 Refuse pathological nullid successors
103 Refuse pathological nullid successors
104 $ hg debugobsolete -d '9001 0' 1337133713371337133713371337133713371337 0000000000000000000000000000000000000000
104 $ hg debugobsolete -d '9001 0' 1337133713371337133713371337133713371337 0000000000000000000000000000000000000000
105 transaction abort!
105 transaction abort!
106 rollback completed
106 rollback completed
107 abort: bad obsolescence marker detected: invalid successors nullid
107 abort: bad obsolescence marker detected: invalid successors nullid
108 [255]
108 [255]
109
109
110 Check that graphlog detect that a changeset is obsolete:
110 Check that graphlog detect that a changeset is obsolete:
111
111
112 $ hg glog
112 $ hg glog
113 @ changeset: 5:5601fb93a350
113 @ changeset: 5:5601fb93a350
114 | tag: tip
114 | tag: tip
115 | parent: 1:7c3bad9141dc
115 | parent: 1:7c3bad9141dc
116 | user: test
116 | user: test
117 | date: Thu Jan 01 00:00:00 1970 +0000
117 | date: Thu Jan 01 00:00:00 1970 +0000
118 | summary: add new_3_c
118 | summary: add new_3_c
119 |
119 |
120 o changeset: 1:7c3bad9141dc
120 o changeset: 1:7c3bad9141dc
121 | user: test
121 | user: test
122 | date: Thu Jan 01 00:00:00 1970 +0000
122 | date: Thu Jan 01 00:00:00 1970 +0000
123 | summary: add b
123 | summary: add b
124 |
124 |
125 o changeset: 0:1f0dee641bb7
125 o changeset: 0:1f0dee641bb7
126 user: test
126 user: test
127 date: Thu Jan 01 00:00:00 1970 +0000
127 date: Thu Jan 01 00:00:00 1970 +0000
128 summary: add a
128 summary: add a
129
129
130
130
131 Check that public changeset are not accounted as obsolete:
131 Check that public changeset are not accounted as obsolete:
132
132
133 $ hg phase --public 2
133 $ hg phase --public 2
134 $ hg --config 'extensions.graphlog=' glog
134 $ hg --config 'extensions.graphlog=' glog
135 @ changeset: 5:5601fb93a350
135 @ changeset: 5:5601fb93a350
136 | tag: tip
136 | tag: tip
137 | parent: 1:7c3bad9141dc
137 | parent: 1:7c3bad9141dc
138 | user: test
138 | user: test
139 | date: Thu Jan 01 00:00:00 1970 +0000
139 | date: Thu Jan 01 00:00:00 1970 +0000
140 | summary: add new_3_c
140 | summary: add new_3_c
141 |
141 |
142 | o changeset: 2:245bde4270cd
142 | o changeset: 2:245bde4270cd
143 |/ user: test
143 |/ user: test
144 | date: Thu Jan 01 00:00:00 1970 +0000
144 | date: Thu Jan 01 00:00:00 1970 +0000
145 | summary: add original_c
145 | summary: add original_c
146 |
146 |
147 o changeset: 1:7c3bad9141dc
147 o changeset: 1:7c3bad9141dc
148 | user: test
148 | user: test
149 | date: Thu Jan 01 00:00:00 1970 +0000
149 | date: Thu Jan 01 00:00:00 1970 +0000
150 | summary: add b
150 | summary: add b
151 |
151 |
152 o changeset: 0:1f0dee641bb7
152 o changeset: 0:1f0dee641bb7
153 user: test
153 user: test
154 date: Thu Jan 01 00:00:00 1970 +0000
154 date: Thu Jan 01 00:00:00 1970 +0000
155 summary: add a
155 summary: add a
156
156
157
157
158 And that bumped changeset are detected
159 --------------------------------------
160
161 If we didn't filtered obsolete changesets out, 3 and 4 would show up too. Also
162 note that the bumped changeset (5:5601fb93a350) is not a direct successor of
163 the public changeset
164
165 $ hg log --hidden -r 'bumped()'
166 changeset: 5:5601fb93a350
167 tag: tip
168 parent: 1:7c3bad9141dc
169 user: test
170 date: Thu Jan 01 00:00:00 1970 +0000
171 summary: add new_3_c
172
173
158 $ cd ..
174 $ cd ..
159
175
160 Exchange Test
176 Exchange Test
161 ============================
177 ============================
162
178
163 Destination repo does not have any data
179 Destination repo does not have any data
164 ---------------------------------------
180 ---------------------------------------
165
181
166 Try to pull markers
182 Try to pull markers
167 (extinct changeset are excluded but marker are pushed)
183 (extinct changeset are excluded but marker are pushed)
168
184
169 $ hg init tmpc
185 $ hg init tmpc
170 $ cd tmpc
186 $ cd tmpc
171 $ hg pull ../tmpb
187 $ hg pull ../tmpb
172 pulling from ../tmpb
188 pulling from ../tmpb
173 requesting all changes
189 requesting all changes
174 adding changesets
190 adding changesets
175 adding manifests
191 adding manifests
176 adding file changes
192 adding file changes
177 added 4 changesets with 4 changes to 4 files (+1 heads)
193 added 4 changesets with 4 changes to 4 files (+1 heads)
178 (run 'hg heads' to see heads, 'hg merge' to merge)
194 (run 'hg heads' to see heads, 'hg merge' to merge)
179 $ hg debugobsolete
195 $ hg debugobsolete
180 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
196 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
181 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
197 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
182 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
198 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
183 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
199 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
184
200
185 Rollback//Transaction support
201 Rollback//Transaction support
186
202
187 $ hg debugobsolete -d '1340 0' aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
203 $ hg debugobsolete -d '1340 0' aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
188 $ hg debugobsolete
204 $ hg debugobsolete
189 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
205 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
190 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
206 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
191 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
207 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
192 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
208 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
193 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 0 {'date': '1340 0', 'user': 'test'}
209 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 0 {'date': '1340 0', 'user': 'test'}
194 $ hg rollback -n
210 $ hg rollback -n
195 repository tip rolled back to revision 3 (undo debugobsolete)
211 repository tip rolled back to revision 3 (undo debugobsolete)
196 $ hg rollback
212 $ hg rollback
197 repository tip rolled back to revision 3 (undo debugobsolete)
213 repository tip rolled back to revision 3 (undo debugobsolete)
198 $ hg debugobsolete
214 $ hg debugobsolete
199 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
215 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
200 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
216 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
201 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
217 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
202 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
218 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
203
219
204 $ cd ..
220 $ cd ..
205
221
206 Try to pull markers
222 Try to pull markers
207
223
208 $ hg init tmpd
224 $ hg init tmpd
209 $ hg -R tmpb push tmpd
225 $ hg -R tmpb push tmpd
210 pushing to tmpd
226 pushing to tmpd
211 searching for changes
227 searching for changes
212 adding changesets
228 adding changesets
213 adding manifests
229 adding manifests
214 adding file changes
230 adding file changes
215 added 4 changesets with 4 changes to 4 files (+1 heads)
231 added 4 changesets with 4 changes to 4 files (+1 heads)
216 $ hg -R tmpd debugobsolete
232 $ hg -R tmpd debugobsolete
217 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
233 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
218 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
234 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
219 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
235 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
220 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
236 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
221
237
222 Check obsolete keys are exchanged only if source has an obsolete store
238 Check obsolete keys are exchanged only if source has an obsolete store
223
239
224 $ hg init empty
240 $ hg init empty
225 $ hg --config extensions.debugkeys=debugkeys.py -R empty push tmpd
241 $ hg --config extensions.debugkeys=debugkeys.py -R empty push tmpd
226 pushing to tmpd
242 pushing to tmpd
227 no changes found
243 no changes found
228 listkeys phases
244 listkeys phases
229 listkeys bookmarks
245 listkeys bookmarks
230 [1]
246 [1]
231
247
232 clone support
248 clone support
233 (markers are copied and extinct changesets are included to allow hardlinks)
249 (markers are copied and extinct changesets are included to allow hardlinks)
234
250
235 $ hg clone tmpb clone-dest
251 $ hg clone tmpb clone-dest
236 updating to branch default
252 updating to branch default
237 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
253 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
238 $ hg -R clone-dest log -G --hidden
254 $ hg -R clone-dest log -G --hidden
239 @ changeset: 5:5601fb93a350
255 @ changeset: 5:5601fb93a350
240 | tag: tip
256 | tag: tip
241 | parent: 1:7c3bad9141dc
257 | parent: 1:7c3bad9141dc
242 | user: test
258 | user: test
243 | date: Thu Jan 01 00:00:00 1970 +0000
259 | date: Thu Jan 01 00:00:00 1970 +0000
244 | summary: add new_3_c
260 | summary: add new_3_c
245 |
261 |
246 | x changeset: 4:ca819180edb9
262 | x changeset: 4:ca819180edb9
247 |/ parent: 1:7c3bad9141dc
263 |/ parent: 1:7c3bad9141dc
248 | user: test
264 | user: test
249 | date: Thu Jan 01 00:00:00 1970 +0000
265 | date: Thu Jan 01 00:00:00 1970 +0000
250 | summary: add new_2_c
266 | summary: add new_2_c
251 |
267 |
252 | x changeset: 3:cdbce2fbb163
268 | x changeset: 3:cdbce2fbb163
253 |/ parent: 1:7c3bad9141dc
269 |/ parent: 1:7c3bad9141dc
254 | user: test
270 | user: test
255 | date: Thu Jan 01 00:00:00 1970 +0000
271 | date: Thu Jan 01 00:00:00 1970 +0000
256 | summary: add new_c
272 | summary: add new_c
257 |
273 |
258 | o changeset: 2:245bde4270cd
274 | o changeset: 2:245bde4270cd
259 |/ user: test
275 |/ user: test
260 | date: Thu Jan 01 00:00:00 1970 +0000
276 | date: Thu Jan 01 00:00:00 1970 +0000
261 | summary: add original_c
277 | summary: add original_c
262 |
278 |
263 o changeset: 1:7c3bad9141dc
279 o changeset: 1:7c3bad9141dc
264 | user: test
280 | user: test
265 | date: Thu Jan 01 00:00:00 1970 +0000
281 | date: Thu Jan 01 00:00:00 1970 +0000
266 | summary: add b
282 | summary: add b
267 |
283 |
268 o changeset: 0:1f0dee641bb7
284 o changeset: 0:1f0dee641bb7
269 user: test
285 user: test
270 date: Thu Jan 01 00:00:00 1970 +0000
286 date: Thu Jan 01 00:00:00 1970 +0000
271 summary: add a
287 summary: add a
272
288
273 $ hg -R clone-dest debugobsolete
289 $ hg -R clone-dest debugobsolete
274 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
290 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
275 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
291 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
276 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
292 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
277 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
293 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
278
294
279
295
280 Destination repo have existing data
296 Destination repo have existing data
281 ---------------------------------------
297 ---------------------------------------
282
298
283 On pull
299 On pull
284
300
285 $ hg init tmpe
301 $ hg init tmpe
286 $ cd tmpe
302 $ cd tmpe
287 $ hg debugobsolete -d '1339 0' 2448244824482448244824482448244824482448 1339133913391339133913391339133913391339
303 $ hg debugobsolete -d '1339 0' 2448244824482448244824482448244824482448 1339133913391339133913391339133913391339
288 $ hg pull ../tmpb
304 $ hg pull ../tmpb
289 pulling from ../tmpb
305 pulling from ../tmpb
290 requesting all changes
306 requesting all changes
291 adding changesets
307 adding changesets
292 adding manifests
308 adding manifests
293 adding file changes
309 adding file changes
294 added 4 changesets with 4 changes to 4 files (+1 heads)
310 added 4 changesets with 4 changes to 4 files (+1 heads)
295 (run 'hg heads' to see heads, 'hg merge' to merge)
311 (run 'hg heads' to see heads, 'hg merge' to merge)
296 $ hg debugobsolete
312 $ hg debugobsolete
297 2448244824482448244824482448244824482448 1339133913391339133913391339133913391339 0 {'date': '1339 0', 'user': 'test'}
313 2448244824482448244824482448244824482448 1339133913391339133913391339133913391339 0 {'date': '1339 0', 'user': 'test'}
298 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
314 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
299 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
315 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
300 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
316 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
301 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
317 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
302
318
303
319
304 On push
320 On push
305
321
306 $ hg push ../tmpc
322 $ hg push ../tmpc
307 pushing to ../tmpc
323 pushing to ../tmpc
308 searching for changes
324 searching for changes
309 no changes found
325 no changes found
310 [1]
326 [1]
311 $ hg -R ../tmpc debugobsolete
327 $ hg -R ../tmpc debugobsolete
312 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
328 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
313 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
329 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
314 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
330 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
315 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
331 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
316 2448244824482448244824482448244824482448 1339133913391339133913391339133913391339 0 {'date': '1339 0', 'user': 'test'}
332 2448244824482448244824482448244824482448 1339133913391339133913391339133913391339 0 {'date': '1339 0', 'user': 'test'}
317
333
318 detect outgoing obsolete and unstable
334 detect outgoing obsolete and unstable
319 ---------------------------------------
335 ---------------------------------------
320
336
321
337
322 $ hg glog
338 $ hg glog
323 o changeset: 3:5601fb93a350
339 o changeset: 3:5601fb93a350
324 | tag: tip
340 | tag: tip
325 | parent: 1:7c3bad9141dc
341 | parent: 1:7c3bad9141dc
326 | user: test
342 | user: test
327 | date: Thu Jan 01 00:00:00 1970 +0000
343 | date: Thu Jan 01 00:00:00 1970 +0000
328 | summary: add new_3_c
344 | summary: add new_3_c
329 |
345 |
330 | o changeset: 2:245bde4270cd
346 | o changeset: 2:245bde4270cd
331 |/ user: test
347 |/ user: test
332 | date: Thu Jan 01 00:00:00 1970 +0000
348 | date: Thu Jan 01 00:00:00 1970 +0000
333 | summary: add original_c
349 | summary: add original_c
334 |
350 |
335 o changeset: 1:7c3bad9141dc
351 o changeset: 1:7c3bad9141dc
336 | user: test
352 | user: test
337 | date: Thu Jan 01 00:00:00 1970 +0000
353 | date: Thu Jan 01 00:00:00 1970 +0000
338 | summary: add b
354 | summary: add b
339 |
355 |
340 o changeset: 0:1f0dee641bb7
356 o changeset: 0:1f0dee641bb7
341 user: test
357 user: test
342 date: Thu Jan 01 00:00:00 1970 +0000
358 date: Thu Jan 01 00:00:00 1970 +0000
343 summary: add a
359 summary: add a
344
360
345 $ hg up 'desc("new_3_c")'
361 $ hg up 'desc("new_3_c")'
346 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
362 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
347 $ mkcommit original_d
363 $ mkcommit original_d
348 $ mkcommit original_e
364 $ mkcommit original_e
349 $ hg debugobsolete `getid original_d` -d '0 0'
365 $ hg debugobsolete `getid original_d` -d '0 0'
350 $ hg log -r 'obsolete()'
366 $ hg log -r 'obsolete()'
351 changeset: 4:7c694bff0650
367 changeset: 4:7c694bff0650
352 user: test
368 user: test
353 date: Thu Jan 01 00:00:00 1970 +0000
369 date: Thu Jan 01 00:00:00 1970 +0000
354 summary: add original_d
370 summary: add original_d
355
371
356 $ hg glog -r '::unstable()'
372 $ hg glog -r '::unstable()'
357 @ changeset: 5:6e572121998e
373 @ changeset: 5:6e572121998e
358 | tag: tip
374 | tag: tip
359 | user: test
375 | user: test
360 | date: Thu Jan 01 00:00:00 1970 +0000
376 | date: Thu Jan 01 00:00:00 1970 +0000
361 | summary: add original_e
377 | summary: add original_e
362 |
378 |
363 x changeset: 4:7c694bff0650
379 x changeset: 4:7c694bff0650
364 | user: test
380 | user: test
365 | date: Thu Jan 01 00:00:00 1970 +0000
381 | date: Thu Jan 01 00:00:00 1970 +0000
366 | summary: add original_d
382 | summary: add original_d
367 |
383 |
368 o changeset: 3:5601fb93a350
384 o changeset: 3:5601fb93a350
369 | parent: 1:7c3bad9141dc
385 | parent: 1:7c3bad9141dc
370 | user: test
386 | user: test
371 | date: Thu Jan 01 00:00:00 1970 +0000
387 | date: Thu Jan 01 00:00:00 1970 +0000
372 | summary: add new_3_c
388 | summary: add new_3_c
373 |
389 |
374 o changeset: 1:7c3bad9141dc
390 o changeset: 1:7c3bad9141dc
375 | user: test
391 | user: test
376 | date: Thu Jan 01 00:00:00 1970 +0000
392 | date: Thu Jan 01 00:00:00 1970 +0000
377 | summary: add b
393 | summary: add b
378 |
394 |
379 o changeset: 0:1f0dee641bb7
395 o changeset: 0:1f0dee641bb7
380 user: test
396 user: test
381 date: Thu Jan 01 00:00:00 1970 +0000
397 date: Thu Jan 01 00:00:00 1970 +0000
382 summary: add a
398 summary: add a
383
399
384
400
385 refuse to push obsolete changeset
401 refuse to push obsolete changeset
386
402
387 $ hg push ../tmpc/ -r 'desc("original_d")'
403 $ hg push ../tmpc/ -r 'desc("original_d")'
388 pushing to ../tmpc/
404 pushing to ../tmpc/
389 searching for changes
405 searching for changes
390 abort: push includes an obsolete changeset: 7c694bff0650!
406 abort: push includes an obsolete changeset: 7c694bff0650!
391 [255]
407 [255]
392
408
393 refuse to push unstable changeset
409 refuse to push unstable changeset
394
410
395 $ hg push ../tmpc/
411 $ hg push ../tmpc/
396 pushing to ../tmpc/
412 pushing to ../tmpc/
397 searching for changes
413 searching for changes
398 abort: push includes an unstable changeset: 6e572121998e!
414 abort: push includes an unstable changeset: 6e572121998e!
399 [255]
415 [255]
400
416
401 Test that extinct changeset are properly detected
417 Test that extinct changeset are properly detected
402
418
403 $ hg log -r 'extinct()'
419 $ hg log -r 'extinct()'
404
420
405 Don't try to push extinct changeset
421 Don't try to push extinct changeset
406
422
407 $ hg init ../tmpf
423 $ hg init ../tmpf
408 $ hg out ../tmpf
424 $ hg out ../tmpf
409 comparing with ../tmpf
425 comparing with ../tmpf
410 searching for changes
426 searching for changes
411 changeset: 0:1f0dee641bb7
427 changeset: 0:1f0dee641bb7
412 user: test
428 user: test
413 date: Thu Jan 01 00:00:00 1970 +0000
429 date: Thu Jan 01 00:00:00 1970 +0000
414 summary: add a
430 summary: add a
415
431
416 changeset: 1:7c3bad9141dc
432 changeset: 1:7c3bad9141dc
417 user: test
433 user: test
418 date: Thu Jan 01 00:00:00 1970 +0000
434 date: Thu Jan 01 00:00:00 1970 +0000
419 summary: add b
435 summary: add b
420
436
421 changeset: 2:245bde4270cd
437 changeset: 2:245bde4270cd
422 user: test
438 user: test
423 date: Thu Jan 01 00:00:00 1970 +0000
439 date: Thu Jan 01 00:00:00 1970 +0000
424 summary: add original_c
440 summary: add original_c
425
441
426 changeset: 3:5601fb93a350
442 changeset: 3:5601fb93a350
427 parent: 1:7c3bad9141dc
443 parent: 1:7c3bad9141dc
428 user: test
444 user: test
429 date: Thu Jan 01 00:00:00 1970 +0000
445 date: Thu Jan 01 00:00:00 1970 +0000
430 summary: add new_3_c
446 summary: add new_3_c
431
447
432 changeset: 4:7c694bff0650
448 changeset: 4:7c694bff0650
433 user: test
449 user: test
434 date: Thu Jan 01 00:00:00 1970 +0000
450 date: Thu Jan 01 00:00:00 1970 +0000
435 summary: add original_d
451 summary: add original_d
436
452
437 changeset: 5:6e572121998e
453 changeset: 5:6e572121998e
438 tag: tip
454 tag: tip
439 user: test
455 user: test
440 date: Thu Jan 01 00:00:00 1970 +0000
456 date: Thu Jan 01 00:00:00 1970 +0000
441 summary: add original_e
457 summary: add original_e
442
458
443 $ hg push ../tmpf -f # -f because be push unstable too
459 $ hg push ../tmpf -f # -f because be push unstable too
444 pushing to ../tmpf
460 pushing to ../tmpf
445 searching for changes
461 searching for changes
446 adding changesets
462 adding changesets
447 adding manifests
463 adding manifests
448 adding file changes
464 adding file changes
449 added 6 changesets with 6 changes to 6 files (+1 heads)
465 added 6 changesets with 6 changes to 6 files (+1 heads)
450
466
451 no warning displayed
467 no warning displayed
452
468
453 $ hg push ../tmpf
469 $ hg push ../tmpf
454 pushing to ../tmpf
470 pushing to ../tmpf
455 searching for changes
471 searching for changes
456 no changes found
472 no changes found
457 [1]
473 [1]
458
474
459 Do not warn about new head when the new head is a successors of a remote one
475 Do not warn about new head when the new head is a successors of a remote one
460
476
461 $ hg glog
477 $ hg glog
462 @ changeset: 5:6e572121998e
478 @ changeset: 5:6e572121998e
463 | tag: tip
479 | tag: tip
464 | user: test
480 | user: test
465 | date: Thu Jan 01 00:00:00 1970 +0000
481 | date: Thu Jan 01 00:00:00 1970 +0000
466 | summary: add original_e
482 | summary: add original_e
467 |
483 |
468 x changeset: 4:7c694bff0650
484 x changeset: 4:7c694bff0650
469 | user: test
485 | user: test
470 | date: Thu Jan 01 00:00:00 1970 +0000
486 | date: Thu Jan 01 00:00:00 1970 +0000
471 | summary: add original_d
487 | summary: add original_d
472 |
488 |
473 o changeset: 3:5601fb93a350
489 o changeset: 3:5601fb93a350
474 | parent: 1:7c3bad9141dc
490 | parent: 1:7c3bad9141dc
475 | user: test
491 | user: test
476 | date: Thu Jan 01 00:00:00 1970 +0000
492 | date: Thu Jan 01 00:00:00 1970 +0000
477 | summary: add new_3_c
493 | summary: add new_3_c
478 |
494 |
479 | o changeset: 2:245bde4270cd
495 | o changeset: 2:245bde4270cd
480 |/ user: test
496 |/ user: test
481 | date: Thu Jan 01 00:00:00 1970 +0000
497 | date: Thu Jan 01 00:00:00 1970 +0000
482 | summary: add original_c
498 | summary: add original_c
483 |
499 |
484 o changeset: 1:7c3bad9141dc
500 o changeset: 1:7c3bad9141dc
485 | user: test
501 | user: test
486 | date: Thu Jan 01 00:00:00 1970 +0000
502 | date: Thu Jan 01 00:00:00 1970 +0000
487 | summary: add b
503 | summary: add b
488 |
504 |
489 o changeset: 0:1f0dee641bb7
505 o changeset: 0:1f0dee641bb7
490 user: test
506 user: test
491 date: Thu Jan 01 00:00:00 1970 +0000
507 date: Thu Jan 01 00:00:00 1970 +0000
492 summary: add a
508 summary: add a
493
509
494 $ hg up -q 'desc(new_3_c)'
510 $ hg up -q 'desc(new_3_c)'
495 $ mkcommit obsolete_e
511 $ mkcommit obsolete_e
496 created new head
512 created new head
497 $ hg debugobsolete `getid 'original_e'` `getid 'obsolete_e'`
513 $ hg debugobsolete `getid 'original_e'` `getid 'obsolete_e'`
498 $ hg push ../tmpf
514 $ hg push ../tmpf
499 pushing to ../tmpf
515 pushing to ../tmpf
500 searching for changes
516 searching for changes
501 adding changesets
517 adding changesets
502 adding manifests
518 adding manifests
503 adding file changes
519 adding file changes
504 added 1 changesets with 1 changes to 1 files (+1 heads)
520 added 1 changesets with 1 changes to 1 files (+1 heads)
505
521
506 Checking _enable=False warning if obsolete marker exists
522 Checking _enable=False warning if obsolete marker exists
507
523
508 $ echo '[extensions]' >> $HGRCPATH
524 $ echo '[extensions]' >> $HGRCPATH
509 $ echo "obs=!" >> $HGRCPATH
525 $ echo "obs=!" >> $HGRCPATH
510 $ hg log -r tip
526 $ hg log -r tip
511 obsolete feature not enabled but 7 markers found!
527 obsolete feature not enabled but 7 markers found!
512 changeset: 6:d6a026544050
528 changeset: 6:d6a026544050
513 tag: tip
529 tag: tip
514 parent: 3:5601fb93a350
530 parent: 3:5601fb93a350
515 user: test
531 user: test
516 date: Thu Jan 01 00:00:00 1970 +0000
532 date: Thu Jan 01 00:00:00 1970 +0000
517 summary: add obsolete_e
533 summary: add obsolete_e
518
534
General Comments 0
You need to be logged in to leave comments. Login now