##// END OF EJS Templates
graphlog: correctly handle calls in subdirectories
Patrick Mezard -
r16411:4c2edcd8 default
parent child Browse files
Show More
@@ -1,553 +1,553 b''
1 1 # ASCII graph log extension for Mercurial
2 2 #
3 3 # Copyright 2007 Joel Rosdahl <joel@rosdahl.net>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''command to view revision graphs from a shell
9 9
10 10 This extension adds a --graph option to the incoming, outgoing and log
11 11 commands. When this options is given, an ASCII representation of the
12 12 revision graph is also shown.
13 13 '''
14 14
15 15 from mercurial.cmdutil import show_changeset
16 16 from mercurial.commands import templateopts
17 17 from mercurial.i18n import _
18 18 from mercurial.node import nullrev
19 19 from mercurial import cmdutil, commands, extensions, scmutil
20 20 from mercurial import hg, util, graphmod, templatekw
21 21 from mercurial import revset as revsetmod
22 22
23 23 cmdtable = {}
24 24 command = cmdutil.command(cmdtable)
25 25
26 26 ASCIIDATA = 'ASC'
27 27
28 28 def asciiedges(type, char, lines, seen, rev, parents):
29 29 """adds edge info to changelog DAG walk suitable for ascii()"""
30 30 if rev not in seen:
31 31 seen.append(rev)
32 32 nodeidx = seen.index(rev)
33 33
34 34 knownparents = []
35 35 newparents = []
36 36 for parent in parents:
37 37 if parent in seen:
38 38 knownparents.append(parent)
39 39 else:
40 40 newparents.append(parent)
41 41
42 42 ncols = len(seen)
43 43 nextseen = seen[:]
44 44 nextseen[nodeidx:nodeidx + 1] = newparents
45 45 edges = [(nodeidx, nextseen.index(p)) for p in knownparents]
46 46
47 47 while len(newparents) > 2:
48 48 # ascii() only knows how to add or remove a single column between two
49 49 # calls. Nodes with more than two parents break this constraint so we
50 50 # introduce intermediate expansion lines to grow the active node list
51 51 # slowly.
52 52 edges.append((nodeidx, nodeidx))
53 53 edges.append((nodeidx, nodeidx + 1))
54 54 nmorecols = 1
55 55 yield (type, char, lines, (nodeidx, edges, ncols, nmorecols))
56 56 char = '\\'
57 57 lines = []
58 58 nodeidx += 1
59 59 ncols += 1
60 60 edges = []
61 61 del newparents[0]
62 62
63 63 if len(newparents) > 0:
64 64 edges.append((nodeidx, nodeidx))
65 65 if len(newparents) > 1:
66 66 edges.append((nodeidx, nodeidx + 1))
67 67 nmorecols = len(nextseen) - ncols
68 68 seen[:] = nextseen
69 69 yield (type, char, lines, (nodeidx, edges, ncols, nmorecols))
70 70
71 71 def fix_long_right_edges(edges):
72 72 for (i, (start, end)) in enumerate(edges):
73 73 if end > start:
74 74 edges[i] = (start, end + 1)
75 75
76 76 def get_nodeline_edges_tail(
77 77 node_index, p_node_index, n_columns, n_columns_diff, p_diff, fix_tail):
78 78 if fix_tail and n_columns_diff == p_diff and n_columns_diff != 0:
79 79 # Still going in the same non-vertical direction.
80 80 if n_columns_diff == -1:
81 81 start = max(node_index + 1, p_node_index)
82 82 tail = ["|", " "] * (start - node_index - 1)
83 83 tail.extend(["/", " "] * (n_columns - start))
84 84 return tail
85 85 else:
86 86 return ["\\", " "] * (n_columns - node_index - 1)
87 87 else:
88 88 return ["|", " "] * (n_columns - node_index - 1)
89 89
90 90 def draw_edges(edges, nodeline, interline):
91 91 for (start, end) in edges:
92 92 if start == end + 1:
93 93 interline[2 * end + 1] = "/"
94 94 elif start == end - 1:
95 95 interline[2 * start + 1] = "\\"
96 96 elif start == end:
97 97 interline[2 * start] = "|"
98 98 else:
99 99 if 2 * end >= len(nodeline):
100 100 continue
101 101 nodeline[2 * end] = "+"
102 102 if start > end:
103 103 (start, end) = (end, start)
104 104 for i in range(2 * start + 1, 2 * end):
105 105 if nodeline[i] != "+":
106 106 nodeline[i] = "-"
107 107
108 108 def get_padding_line(ni, n_columns, edges):
109 109 line = []
110 110 line.extend(["|", " "] * ni)
111 111 if (ni, ni - 1) in edges or (ni, ni) in edges:
112 112 # (ni, ni - 1) (ni, ni)
113 113 # | | | | | | | |
114 114 # +---o | | o---+
115 115 # | | c | | c | |
116 116 # | |/ / | |/ /
117 117 # | | | | | |
118 118 c = "|"
119 119 else:
120 120 c = " "
121 121 line.extend([c, " "])
122 122 line.extend(["|", " "] * (n_columns - ni - 1))
123 123 return line
124 124
125 125 def asciistate():
126 126 """returns the initial value for the "state" argument to ascii()"""
127 127 return [0, 0]
128 128
129 129 def ascii(ui, state, type, char, text, coldata):
130 130 """prints an ASCII graph of the DAG
131 131
132 132 takes the following arguments (one call per node in the graph):
133 133
134 134 - ui to write to
135 135 - Somewhere to keep the needed state in (init to asciistate())
136 136 - Column of the current node in the set of ongoing edges.
137 137 - Type indicator of node data == ASCIIDATA.
138 138 - Payload: (char, lines):
139 139 - Character to use as node's symbol.
140 140 - List of lines to display as the node's text.
141 141 - Edges; a list of (col, next_col) indicating the edges between
142 142 the current node and its parents.
143 143 - Number of columns (ongoing edges) in the current revision.
144 144 - The difference between the number of columns (ongoing edges)
145 145 in the next revision and the number of columns (ongoing edges)
146 146 in the current revision. That is: -1 means one column removed;
147 147 0 means no columns added or removed; 1 means one column added.
148 148 """
149 149
150 150 idx, edges, ncols, coldiff = coldata
151 151 assert -2 < coldiff < 2
152 152 if coldiff == -1:
153 153 # Transform
154 154 #
155 155 # | | | | | |
156 156 # o | | into o---+
157 157 # |X / |/ /
158 158 # | | | |
159 159 fix_long_right_edges(edges)
160 160
161 161 # add_padding_line says whether to rewrite
162 162 #
163 163 # | | | | | | | |
164 164 # | o---+ into | o---+
165 165 # | / / | | | # <--- padding line
166 166 # o | | | / /
167 167 # o | |
168 168 add_padding_line = (len(text) > 2 and coldiff == -1 and
169 169 [x for (x, y) in edges if x + 1 < y])
170 170
171 171 # fix_nodeline_tail says whether to rewrite
172 172 #
173 173 # | | o | | | | o | |
174 174 # | | |/ / | | |/ /
175 175 # | o | | into | o / / # <--- fixed nodeline tail
176 176 # | |/ / | |/ /
177 177 # o | | o | |
178 178 fix_nodeline_tail = len(text) <= 2 and not add_padding_line
179 179
180 180 # nodeline is the line containing the node character (typically o)
181 181 nodeline = ["|", " "] * idx
182 182 nodeline.extend([char, " "])
183 183
184 184 nodeline.extend(
185 185 get_nodeline_edges_tail(idx, state[1], ncols, coldiff,
186 186 state[0], fix_nodeline_tail))
187 187
188 188 # shift_interline is the line containing the non-vertical
189 189 # edges between this entry and the next
190 190 shift_interline = ["|", " "] * idx
191 191 if coldiff == -1:
192 192 n_spaces = 1
193 193 edge_ch = "/"
194 194 elif coldiff == 0:
195 195 n_spaces = 2
196 196 edge_ch = "|"
197 197 else:
198 198 n_spaces = 3
199 199 edge_ch = "\\"
200 200 shift_interline.extend(n_spaces * [" "])
201 201 shift_interline.extend([edge_ch, " "] * (ncols - idx - 1))
202 202
203 203 # draw edges from the current node to its parents
204 204 draw_edges(edges, nodeline, shift_interline)
205 205
206 206 # lines is the list of all graph lines to print
207 207 lines = [nodeline]
208 208 if add_padding_line:
209 209 lines.append(get_padding_line(idx, ncols, edges))
210 210 lines.append(shift_interline)
211 211
212 212 # make sure that there are as many graph lines as there are
213 213 # log strings
214 214 while len(text) < len(lines):
215 215 text.append("")
216 216 if len(lines) < len(text):
217 217 extra_interline = ["|", " "] * (ncols + coldiff)
218 218 while len(lines) < len(text):
219 219 lines.append(extra_interline)
220 220
221 221 # print lines
222 222 indentation_level = max(ncols, ncols + coldiff)
223 223 for (line, logstr) in zip(lines, text):
224 224 ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr)
225 225 ui.write(ln.rstrip() + '\n')
226 226
227 227 # ... and start over
228 228 state[0] = coldiff
229 229 state[1] = idx
230 230
231 231 def get_revs(repo, rev_opt):
232 232 if rev_opt:
233 233 revs = scmutil.revrange(repo, rev_opt)
234 234 if len(revs) == 0:
235 235 return (nullrev, nullrev)
236 236 return (max(revs), min(revs))
237 237 else:
238 238 return (len(repo) - 1, 0)
239 239
240 240 def check_unsupported_flags(pats, opts):
241 241 for op in ["newest_first"]:
242 242 if op in opts and opts[op]:
243 243 raise util.Abort(_("-G/--graph option is incompatible with --%s")
244 244 % op.replace("_", "-"))
245 245
246 246 def makefilematcher(repo, pats, followfirst):
247 247 # When displaying a revision with --patch --follow FILE, we have
248 248 # to know which file of the revision must be diffed. With
249 249 # --follow, we want the names of the ancestors of FILE in the
250 250 # revision, stored in "fcache". "fcache" is populated by
251 251 # reproducing the graph traversal already done by --follow revset
252 252 # and relating linkrevs to file names (which is not "correct" but
253 253 # good enough).
254 254 fcache = {}
255 255 fcacheready = [False]
256 256 pctx = repo['.']
257 257 wctx = repo[None]
258 258
259 259 def populate():
260 260 for fn in pats:
261 261 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
262 262 for c in i:
263 263 fcache.setdefault(c.linkrev(), set()).add(c.path())
264 264
265 265 def filematcher(rev):
266 266 if not fcacheready[0]:
267 267 # Lazy initialization
268 268 fcacheready[0] = True
269 269 populate()
270 270 return scmutil.match(wctx, fcache.get(rev, []), default='path')
271 271
272 272 return filematcher
273 273
274 274 def _makelogrevset(repo, pats, opts, revs):
275 275 """Return (expr, filematcher) where expr is a revset string built
276 276 from log options and file patterns or None. If --stat or --patch
277 277 are not passed filematcher is None. Otherwise it is a callable
278 278 taking a revision number and returning a match objects filtering
279 279 the files to be detailed when displaying the revision.
280 280 """
281 281 opt2revset = {
282 282 'no_merges': ('not merge()', None),
283 283 'only_merges': ('merge()', None),
284 284 '_ancestors': ('ancestors(%(val)s)', None),
285 285 '_fancestors': ('_firstancestors(%(val)s)', None),
286 286 '_descendants': ('descendants(%(val)s)', None),
287 287 '_fdescendants': ('_firstdescendants(%(val)s)', None),
288 288 '_matchfiles': ('_matchfiles(%(val)s)', None),
289 289 'date': ('date(%(val)r)', None),
290 290 'branch': ('branch(%(val)r)', ' or '),
291 291 '_patslog': ('filelog(%(val)r)', ' or '),
292 292 '_patsfollow': ('follow(%(val)r)', ' or '),
293 293 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
294 294 'keyword': ('keyword(%(val)r)', ' or '),
295 295 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
296 296 'user': ('user(%(val)r)', ' or '),
297 297 }
298 298
299 299 opts = dict(opts)
300 300 # follow or not follow?
301 301 follow = opts.get('follow') or opts.get('follow_first')
302 302 followfirst = opts.get('follow_first')
303 303 # --follow with FILE behaviour depends on revs...
304 304 startrev = revs[0]
305 305 followdescendants = len(revs) > 1 and revs[0] < revs[1]
306 306
307 307 # branch and only_branch are really aliases and must be handled at
308 308 # the same time
309 309 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
310 310 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
311 311 # pats/include/exclude are passed to match.match() directly in
312 312 # _matchfile() revset but walkchangerevs() builds its matcher with
313 313 # scmutil.match(). The difference is input pats are globbed on
314 314 # platforms without shell expansion (windows).
315 315 pctx = repo[None]
316 316 match, pats = scmutil.matchandpats(pctx, pats, opts)
317 317 slowpath = match.anypats() or (match.files() and opts.get('removed'))
318 318 if not slowpath:
319 319 for f in match.files():
320 320 if follow and f not in pctx:
321 321 raise util.Abort(_('cannot follow file not in parent '
322 322 'revision: "%s"') % f)
323 323 filelog = repo.file(f)
324 324 if not len(filelog):
325 325 # A zero count may be a directory or deleted file, so
326 326 # try to find matching entries on the slow path.
327 327 if follow:
328 328 raise util.Abort(
329 329 _('cannot follow nonexistent file: "%s"') % f)
330 330 slowpath = True
331 331 if slowpath:
332 332 # See cmdutil.walkchangerevs() slow path.
333 333 #
334 334 if follow:
335 335 raise util.Abort(_('can only follow copies/renames for explicit '
336 336 'filenames'))
337 337 # pats/include/exclude cannot be represented as separate
338 338 # revset expressions as their filtering logic applies at file
339 339 # level. For instance "-I a -X a" matches a revision touching
340 340 # "a" and "b" while "file(a) and not file(b)" does
341 341 # not. Besides, filesets are evaluated against the working
342 342 # directory.
343 matchargs = ['r:']
343 matchargs = ['r:', 'd:relpath']
344 344 for p in pats:
345 345 matchargs.append('p:' + p)
346 346 for p in opts.get('include', []):
347 347 matchargs.append('i:' + p)
348 348 for p in opts.get('exclude', []):
349 349 matchargs.append('x:' + p)
350 350 matchargs = ','.join(('%r' % p) for p in matchargs)
351 351 opts['_matchfiles'] = matchargs
352 352 else:
353 353 if follow:
354 354 if followfirst:
355 355 if pats:
356 356 opts['_patsfollowfirst'] = list(pats)
357 357 else:
358 358 if followdescendants:
359 359 opts['_fdescendants'] = str(startrev)
360 360 else:
361 361 opts['_fancestors'] = str(startrev)
362 362 else:
363 363 if pats:
364 364 opts['_patsfollow'] = list(pats)
365 365 else:
366 366 if followdescendants:
367 367 opts['_descendants'] = str(startrev)
368 368 else:
369 369 opts['_ancestors'] = str(startrev)
370 370 else:
371 371 opts['_patslog'] = list(pats)
372 372
373 373 filematcher = None
374 374 if opts.get('patch') or opts.get('stat'):
375 375 if follow:
376 376 filematcher = makefilematcher(repo, pats, followfirst)
377 377 else:
378 378 filematcher = lambda rev: match
379 379
380 380 revset = []
381 381 for op, val in opts.iteritems():
382 382 if not val:
383 383 continue
384 384 if op not in opt2revset:
385 385 continue
386 386 revop, andor = opt2revset[op]
387 387 if '%(val)' not in revop:
388 388 revset.append(revop)
389 389 else:
390 390 if not isinstance(val, list):
391 391 expr = revop % {'val': val}
392 392 else:
393 393 expr = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
394 394 revset.append(expr)
395 395
396 396 if revset:
397 397 revset = '(' + ' and '.join(revset) + ')'
398 398 else:
399 399 revset = None
400 400 return revset, filematcher
401 401
402 402 def getlogrevs(repo, pats, opts):
403 403 """Return (revs, expr, filematcher) where revs is a list of
404 404 revision numbers, expr is a revset string built from log options
405 405 and file patterns or None, and used to filter 'revs'. If --stat or
406 406 --patch are not passed filematcher is None. Otherwise it is a
407 407 callable taking a revision number and returning a match objects
408 408 filtering the files to be detailed when displaying the revision.
409 409 """
410 410 if not len(repo):
411 411 return [], None, None
412 412 # Default --rev value depends on --follow but --follow behaviour
413 413 # depends on revisions resolved from --rev...
414 414 follow = opts.get('follow') or opts.get('follow_first')
415 415 if opts.get('rev'):
416 416 revs = scmutil.revrange(repo, opts['rev'])
417 417 else:
418 418 if follow and len(repo) > 0:
419 419 revs = scmutil.revrange(repo, ['.:0'])
420 420 else:
421 421 revs = range(len(repo) - 1, -1, -1)
422 422 if not revs:
423 423 return [], None, None
424 424 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
425 425 if expr:
426 426 # Evaluate revisions in changelog order for performance
427 427 # reasons but preserve the original sequence order in the
428 428 # filtered result.
429 429 matched = set(revsetmod.match(repo.ui, expr)(repo, sorted(revs)))
430 430 revs = [r for r in revs if r in matched]
431 431 return revs, expr, filematcher
432 432
433 433 def generate(ui, dag, displayer, showparents, edgefn, getrenamed=None,
434 434 filematcher=None):
435 435 seen, state = [], asciistate()
436 436 for rev, type, ctx, parents in dag:
437 437 char = ctx.node() in showparents and '@' or 'o'
438 438 copies = None
439 439 if getrenamed and ctx.rev():
440 440 copies = []
441 441 for fn in ctx.files():
442 442 rename = getrenamed(fn, ctx.rev())
443 443 if rename:
444 444 copies.append((fn, rename[0]))
445 445 revmatchfn = None
446 446 if filematcher is not None:
447 447 revmatchfn = filematcher(ctx.rev())
448 448 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
449 449 lines = displayer.hunk.pop(rev).split('\n')[:-1]
450 450 displayer.flush(rev)
451 451 edges = edgefn(type, char, lines, seen, rev, parents)
452 452 for type, char, lines, coldata in edges:
453 453 ascii(ui, state, type, char, lines, coldata)
454 454 displayer.close()
455 455
456 456 @command('glog',
457 457 [('l', 'limit', '',
458 458 _('limit number of changes displayed'), _('NUM')),
459 459 ('p', 'patch', False, _('show patch')),
460 460 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
461 461 ] + templateopts,
462 462 _('hg glog [OPTION]... [FILE]'))
463 463 def graphlog(ui, repo, *pats, **opts):
464 464 """show revision history alongside an ASCII revision graph
465 465
466 466 Print a revision history alongside a revision graph drawn with
467 467 ASCII characters.
468 468
469 469 Nodes printed as an @ character are parents of the working
470 470 directory.
471 471 """
472 472
473 473 check_unsupported_flags(pats, opts)
474 474
475 475 revs, expr, filematcher = getlogrevs(repo, pats, opts)
476 476 revs = sorted(revs, reverse=1)
477 477 limit = cmdutil.loglimit(opts)
478 478 if limit is not None:
479 479 revs = revs[:limit]
480 480 revdag = graphmod.dagwalker(repo, revs)
481 481
482 482 getrenamed = None
483 483 if opts.get('copies'):
484 484 endrev = None
485 485 if opts.get('rev'):
486 486 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
487 487 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
488 488 displayer = show_changeset(ui, repo, opts, buffered=True)
489 489 showparents = [ctx.node() for ctx in repo[None].parents()]
490 490 generate(ui, revdag, displayer, showparents, asciiedges, getrenamed,
491 491 filematcher)
492 492
493 493 def graphrevs(repo, nodes, opts):
494 494 limit = cmdutil.loglimit(opts)
495 495 nodes.reverse()
496 496 if limit is not None:
497 497 nodes = nodes[:limit]
498 498 return graphmod.nodes(repo, nodes)
499 499
500 500 def goutgoing(ui, repo, dest=None, **opts):
501 501 """show the outgoing changesets alongside an ASCII revision graph
502 502
503 503 Print the outgoing changesets alongside a revision graph drawn with
504 504 ASCII characters.
505 505
506 506 Nodes printed as an @ character are parents of the working
507 507 directory.
508 508 """
509 509
510 510 check_unsupported_flags([], opts)
511 511 o = hg._outgoing(ui, repo, dest, opts)
512 512 if o is None:
513 513 return
514 514
515 515 revdag = graphrevs(repo, o, opts)
516 516 displayer = show_changeset(ui, repo, opts, buffered=True)
517 517 showparents = [ctx.node() for ctx in repo[None].parents()]
518 518 generate(ui, revdag, displayer, showparents, asciiedges)
519 519
520 520 def gincoming(ui, repo, source="default", **opts):
521 521 """show the incoming changesets alongside an ASCII revision graph
522 522
523 523 Print the incoming changesets alongside a revision graph drawn with
524 524 ASCII characters.
525 525
526 526 Nodes printed as an @ character are parents of the working
527 527 directory.
528 528 """
529 529 def subreporecurse():
530 530 return 1
531 531
532 532 check_unsupported_flags([], opts)
533 533 def display(other, chlist, displayer):
534 534 revdag = graphrevs(other, chlist, opts)
535 535 showparents = [ctx.node() for ctx in repo[None].parents()]
536 536 generate(ui, revdag, displayer, showparents, asciiedges)
537 537
538 538 hg._incoming(display, subreporecurse, ui, repo, source, opts, buffered=True)
539 539
540 540 def uisetup(ui):
541 541 '''Initialize the extension.'''
542 542 _wrapcmd('log', commands.table, graphlog)
543 543 _wrapcmd('incoming', commands.table, gincoming)
544 544 _wrapcmd('outgoing', commands.table, goutgoing)
545 545
546 546 def _wrapcmd(cmd, table, wrapfn):
547 547 '''wrap the command'''
548 548 def graph(orig, *args, **kwargs):
549 549 if kwargs['graph']:
550 550 return wrapfn(*args, **kwargs)
551 551 return orig(*args, **kwargs)
552 552 entry = extensions.wrapcommand(table, cmd, graph)
553 553 entry[1].append(('G', 'graph', None, _("show the revision DAG")))
@@ -1,1459 +1,1467 b''
1 1 # revset.py - revision set queries for mercurial
2 2 #
3 3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 import re
9 9 import parser, util, error, discovery, hbisect, phases
10 10 import node as nodemod
11 11 import bookmarks as bookmarksmod
12 12 import match as matchmod
13 13 from i18n import _
14 14 import encoding
15 15
16 16 def _revancestors(repo, revs, followfirst):
17 17 """Like revlog.ancestors(), but supports followfirst."""
18 18 cut = followfirst and 1 or None
19 19 cl = repo.changelog
20 20 visit = list(revs)
21 21 seen = set([nodemod.nullrev])
22 22 while visit:
23 23 for parent in cl.parentrevs(visit.pop(0))[:cut]:
24 24 if parent not in seen:
25 25 visit.append(parent)
26 26 seen.add(parent)
27 27 yield parent
28 28
29 29 def _revdescendants(repo, revs, followfirst):
30 30 """Like revlog.descendants() but supports followfirst."""
31 31 cut = followfirst and 1 or None
32 32 cl = repo.changelog
33 33 first = min(revs)
34 34 if first == nodemod.nullrev:
35 35 # Are there nodes with a null first parent and a non-null
36 36 # second one? Maybe. Do we care? Probably not.
37 37 for i in cl:
38 38 yield i
39 39 return
40 40
41 41 seen = set(revs)
42 42 for i in xrange(first + 1, len(cl)):
43 43 for x in cl.parentrevs(i)[:cut]:
44 44 if x != nodemod.nullrev and x in seen:
45 45 seen.add(i)
46 46 yield i
47 47 break
48 48
49 49 elements = {
50 50 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
51 51 "~": (18, None, ("ancestor", 18)),
52 52 "^": (18, None, ("parent", 18), ("parentpost", 18)),
53 53 "-": (5, ("negate", 19), ("minus", 5)),
54 54 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
55 55 ("dagrangepost", 17)),
56 56 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
57 57 ("dagrangepost", 17)),
58 58 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
59 59 "not": (10, ("not", 10)),
60 60 "!": (10, ("not", 10)),
61 61 "and": (5, None, ("and", 5)),
62 62 "&": (5, None, ("and", 5)),
63 63 "or": (4, None, ("or", 4)),
64 64 "|": (4, None, ("or", 4)),
65 65 "+": (4, None, ("or", 4)),
66 66 ",": (2, None, ("list", 2)),
67 67 ")": (0, None, None),
68 68 "symbol": (0, ("symbol",), None),
69 69 "string": (0, ("string",), None),
70 70 "end": (0, None, None),
71 71 }
72 72
73 73 keywords = set(['and', 'or', 'not'])
74 74
75 75 def tokenize(program):
76 76 pos, l = 0, len(program)
77 77 while pos < l:
78 78 c = program[pos]
79 79 if c.isspace(): # skip inter-token whitespace
80 80 pass
81 81 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
82 82 yield ('::', None, pos)
83 83 pos += 1 # skip ahead
84 84 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
85 85 yield ('..', None, pos)
86 86 pos += 1 # skip ahead
87 87 elif c in "():,-|&+!~^": # handle simple operators
88 88 yield (c, None, pos)
89 89 elif (c in '"\'' or c == 'r' and
90 90 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
91 91 if c == 'r':
92 92 pos += 1
93 93 c = program[pos]
94 94 decode = lambda x: x
95 95 else:
96 96 decode = lambda x: x.decode('string-escape')
97 97 pos += 1
98 98 s = pos
99 99 while pos < l: # find closing quote
100 100 d = program[pos]
101 101 if d == '\\': # skip over escaped characters
102 102 pos += 2
103 103 continue
104 104 if d == c:
105 105 yield ('string', decode(program[s:pos]), s)
106 106 break
107 107 pos += 1
108 108 else:
109 109 raise error.ParseError(_("unterminated string"), s)
110 110 elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
111 111 s = pos
112 112 pos += 1
113 113 while pos < l: # find end of symbol
114 114 d = program[pos]
115 115 if not (d.isalnum() or d in "._/" or ord(d) > 127):
116 116 break
117 117 if d == '.' and program[pos - 1] == '.': # special case for ..
118 118 pos -= 1
119 119 break
120 120 pos += 1
121 121 sym = program[s:pos]
122 122 if sym in keywords: # operator keywords
123 123 yield (sym, None, s)
124 124 else:
125 125 yield ('symbol', sym, s)
126 126 pos -= 1
127 127 else:
128 128 raise error.ParseError(_("syntax error"), pos)
129 129 pos += 1
130 130 yield ('end', None, pos)
131 131
132 132 # helpers
133 133
134 134 def getstring(x, err):
135 135 if x and (x[0] == 'string' or x[0] == 'symbol'):
136 136 return x[1]
137 137 raise error.ParseError(err)
138 138
139 139 def getlist(x):
140 140 if not x:
141 141 return []
142 142 if x[0] == 'list':
143 143 return getlist(x[1]) + [x[2]]
144 144 return [x]
145 145
146 146 def getargs(x, min, max, err):
147 147 l = getlist(x)
148 148 if len(l) < min or (max >= 0 and len(l) > max):
149 149 raise error.ParseError(err)
150 150 return l
151 151
152 152 def getset(repo, subset, x):
153 153 if not x:
154 154 raise error.ParseError(_("missing argument"))
155 155 return methods[x[0]](repo, subset, *x[1:])
156 156
157 157 # operator methods
158 158
159 159 def stringset(repo, subset, x):
160 160 x = repo[x].rev()
161 161 if x == -1 and len(subset) == len(repo):
162 162 return [-1]
163 163 if len(subset) == len(repo) or x in subset:
164 164 return [x]
165 165 return []
166 166
167 167 def symbolset(repo, subset, x):
168 168 if x in symbols:
169 169 raise error.ParseError(_("can't use %s here") % x)
170 170 return stringset(repo, subset, x)
171 171
172 172 def rangeset(repo, subset, x, y):
173 173 m = getset(repo, subset, x)
174 174 if not m:
175 175 m = getset(repo, range(len(repo)), x)
176 176
177 177 n = getset(repo, subset, y)
178 178 if not n:
179 179 n = getset(repo, range(len(repo)), y)
180 180
181 181 if not m or not n:
182 182 return []
183 183 m, n = m[0], n[-1]
184 184
185 185 if m < n:
186 186 r = range(m, n + 1)
187 187 else:
188 188 r = range(m, n - 1, -1)
189 189 s = set(subset)
190 190 return [x for x in r if x in s]
191 191
192 192 def andset(repo, subset, x, y):
193 193 return getset(repo, getset(repo, subset, x), y)
194 194
195 195 def orset(repo, subset, x, y):
196 196 xl = getset(repo, subset, x)
197 197 s = set(xl)
198 198 yl = getset(repo, [r for r in subset if r not in s], y)
199 199 return xl + yl
200 200
201 201 def notset(repo, subset, x):
202 202 s = set(getset(repo, subset, x))
203 203 return [r for r in subset if r not in s]
204 204
205 205 def listset(repo, subset, a, b):
206 206 raise error.ParseError(_("can't use a list in this context"))
207 207
208 208 def func(repo, subset, a, b):
209 209 if a[0] == 'symbol' and a[1] in symbols:
210 210 return symbols[a[1]](repo, subset, b)
211 211 raise error.ParseError(_("not a function: %s") % a[1])
212 212
213 213 # functions
214 214
215 215 def adds(repo, subset, x):
216 216 """``adds(pattern)``
217 217 Changesets that add a file matching pattern.
218 218 """
219 219 # i18n: "adds" is a keyword
220 220 pat = getstring(x, _("adds requires a pattern"))
221 221 return checkstatus(repo, subset, pat, 1)
222 222
223 223 def ancestor(repo, subset, x):
224 224 """``ancestor(single, single)``
225 225 Greatest common ancestor of the two changesets.
226 226 """
227 227 # i18n: "ancestor" is a keyword
228 228 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
229 229 r = range(len(repo))
230 230 a = getset(repo, r, l[0])
231 231 b = getset(repo, r, l[1])
232 232 if len(a) != 1 or len(b) != 1:
233 233 # i18n: "ancestor" is a keyword
234 234 raise error.ParseError(_("ancestor arguments must be single revisions"))
235 235 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
236 236
237 237 return [r for r in an if r in subset]
238 238
239 239 def _ancestors(repo, subset, x, followfirst=False):
240 240 args = getset(repo, range(len(repo)), x)
241 241 if not args:
242 242 return []
243 243 s = set(_revancestors(repo, args, followfirst)) | set(args)
244 244 return [r for r in subset if r in s]
245 245
246 246 def ancestors(repo, subset, x):
247 247 """``ancestors(set)``
248 248 Changesets that are ancestors of a changeset in set.
249 249 """
250 250 return _ancestors(repo, subset, x)
251 251
252 252 def _firstancestors(repo, subset, x):
253 253 # ``_firstancestors(set)``
254 254 # Like ``ancestors(set)`` but follows only the first parents.
255 255 return _ancestors(repo, subset, x, followfirst=True)
256 256
257 257 def ancestorspec(repo, subset, x, n):
258 258 """``set~n``
259 259 Changesets that are the Nth ancestor (first parents only) of a changeset in set.
260 260 """
261 261 try:
262 262 n = int(n[1])
263 263 except (TypeError, ValueError):
264 264 raise error.ParseError(_("~ expects a number"))
265 265 ps = set()
266 266 cl = repo.changelog
267 267 for r in getset(repo, subset, x):
268 268 for i in range(n):
269 269 r = cl.parentrevs(r)[0]
270 270 ps.add(r)
271 271 return [r for r in subset if r in ps]
272 272
273 273 def author(repo, subset, x):
274 274 """``author(string)``
275 275 Alias for ``user(string)``.
276 276 """
277 277 # i18n: "author" is a keyword
278 278 n = encoding.lower(getstring(x, _("author requires a string")))
279 279 return [r for r in subset if n in encoding.lower(repo[r].user())]
280 280
281 281 def bisect(repo, subset, x):
282 282 """``bisect(string)``
283 283 Changesets marked in the specified bisect status:
284 284
285 285 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
286 286 - ``goods``, ``bads`` : csets topologicaly good/bad
287 287 - ``range`` : csets taking part in the bisection
288 288 - ``pruned`` : csets that are goods, bads or skipped
289 289 - ``untested`` : csets whose fate is yet unknown
290 290 - ``ignored`` : csets ignored due to DAG topology
291 291 """
292 292 status = getstring(x, _("bisect requires a string")).lower()
293 293 return [r for r in subset if r in hbisect.get(repo, status)]
294 294
295 295 # Backward-compatibility
296 296 # - no help entry so that we do not advertise it any more
297 297 def bisected(repo, subset, x):
298 298 return bisect(repo, subset, x)
299 299
300 300 def bookmark(repo, subset, x):
301 301 """``bookmark([name])``
302 302 The named bookmark or all bookmarks.
303 303 """
304 304 # i18n: "bookmark" is a keyword
305 305 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
306 306 if args:
307 307 bm = getstring(args[0],
308 308 # i18n: "bookmark" is a keyword
309 309 _('the argument to bookmark must be a string'))
310 310 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
311 311 if not bmrev:
312 312 raise util.Abort(_("bookmark '%s' does not exist") % bm)
313 313 bmrev = repo[bmrev].rev()
314 314 return [r for r in subset if r == bmrev]
315 315 bms = set([repo[r].rev()
316 316 for r in bookmarksmod.listbookmarks(repo).values()])
317 317 return [r for r in subset if r in bms]
318 318
319 319 def branch(repo, subset, x):
320 320 """``branch(string or set)``
321 321 All changesets belonging to the given branch or the branches of the given
322 322 changesets.
323 323 """
324 324 try:
325 325 b = getstring(x, '')
326 326 if b in repo.branchmap():
327 327 return [r for r in subset if repo[r].branch() == b]
328 328 except error.ParseError:
329 329 # not a string, but another revspec, e.g. tip()
330 330 pass
331 331
332 332 s = getset(repo, range(len(repo)), x)
333 333 b = set()
334 334 for r in s:
335 335 b.add(repo[r].branch())
336 336 s = set(s)
337 337 return [r for r in subset if r in s or repo[r].branch() in b]
338 338
339 339 def checkstatus(repo, subset, pat, field):
340 340 m = None
341 341 s = []
342 342 fast = not matchmod.patkind(pat)
343 343 for r in subset:
344 344 c = repo[r]
345 345 if fast:
346 346 if pat not in c.files():
347 347 continue
348 348 else:
349 349 if not m or matchmod.patkind(pat) == 'set':
350 350 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
351 351 for f in c.files():
352 352 if m(f):
353 353 break
354 354 else:
355 355 continue
356 356 files = repo.status(c.p1().node(), c.node())[field]
357 357 if fast:
358 358 if pat in files:
359 359 s.append(r)
360 360 else:
361 361 for f in files:
362 362 if m(f):
363 363 s.append(r)
364 364 break
365 365 return s
366 366
367 367 def _children(repo, narrow, s):
368 368 cs = set()
369 369 pr = repo.changelog.parentrevs
370 370 s = set(s)
371 371 for r in xrange(len(repo)):
372 372 for p in pr(r):
373 373 if p in s:
374 374 cs.add(r)
375 375 return cs
376 376
377 377 def children(repo, subset, x):
378 378 """``children(set)``
379 379 Child changesets of changesets in set.
380 380 """
381 381 s = getset(repo, range(len(repo)), x)
382 382 cs = _children(repo, subset, s)
383 383 return [r for r in subset if r in cs]
384 384
385 385 def closed(repo, subset, x):
386 386 """``closed()``
387 387 Changeset is closed.
388 388 """
389 389 # i18n: "closed" is a keyword
390 390 getargs(x, 0, 0, _("closed takes no arguments"))
391 391 return [r for r in subset if repo[r].extra().get('close')]
392 392
393 393 def contains(repo, subset, x):
394 394 """``contains(pattern)``
395 395 Revision contains a file matching pattern. See :hg:`help patterns`
396 396 for information about file patterns.
397 397 """
398 398 # i18n: "contains" is a keyword
399 399 pat = getstring(x, _("contains requires a pattern"))
400 400 m = None
401 401 s = []
402 402 if not matchmod.patkind(pat):
403 403 for r in subset:
404 404 if pat in repo[r]:
405 405 s.append(r)
406 406 else:
407 407 for r in subset:
408 408 c = repo[r]
409 409 if not m or matchmod.patkind(pat) == 'set':
410 410 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
411 411 for f in c.manifest():
412 412 if m(f):
413 413 s.append(r)
414 414 break
415 415 return s
416 416
417 417 def date(repo, subset, x):
418 418 """``date(interval)``
419 419 Changesets within the interval, see :hg:`help dates`.
420 420 """
421 421 # i18n: "date" is a keyword
422 422 ds = getstring(x, _("date requires a string"))
423 423 dm = util.matchdate(ds)
424 424 return [r for r in subset if dm(repo[r].date()[0])]
425 425
426 426 def desc(repo, subset, x):
427 427 """``desc(string)``
428 428 Search commit message for string. The match is case-insensitive.
429 429 """
430 430 # i18n: "desc" is a keyword
431 431 ds = encoding.lower(getstring(x, _("desc requires a string")))
432 432 l = []
433 433 for r in subset:
434 434 c = repo[r]
435 435 if ds in encoding.lower(c.description()):
436 436 l.append(r)
437 437 return l
438 438
439 439 def _descendants(repo, subset, x, followfirst=False):
440 440 args = getset(repo, range(len(repo)), x)
441 441 if not args:
442 442 return []
443 443 s = set(_revdescendants(repo, args, followfirst)) | set(args)
444 444 return [r for r in subset if r in s]
445 445
446 446 def descendants(repo, subset, x):
447 447 """``descendants(set)``
448 448 Changesets which are descendants of changesets in set.
449 449 """
450 450 return _descendants(repo, subset, x)
451 451
452 452 def _firstdescendants(repo, subset, x):
453 453 # ``_firstdescendants(set)``
454 454 # Like ``descendants(set)`` but follows only the first parents.
455 455 return _descendants(repo, subset, x, followfirst=True)
456 456
457 457 def draft(repo, subset, x):
458 458 """``draft()``
459 459 Changeset in draft phase."""
460 460 getargs(x, 0, 0, _("draft takes no arguments"))
461 461 return [r for r in subset if repo._phaserev[r] == phases.draft]
462 462
463 463 def filelog(repo, subset, x):
464 464 """``filelog(pattern)``
465 465 Changesets connected to the specified filelog.
466 466 """
467 467
468 468 pat = getstring(x, _("filelog requires a pattern"))
469 469 m = matchmod.match(repo.root, repo.getcwd(), [pat], default='relpath',
470 470 ctx=repo[None])
471 471 s = set()
472 472
473 473 if not matchmod.patkind(pat):
474 474 for f in m.files():
475 475 fl = repo.file(f)
476 476 for fr in fl:
477 477 s.add(fl.linkrev(fr))
478 478 else:
479 479 for f in repo[None]:
480 480 if m(f):
481 481 fl = repo.file(f)
482 482 for fr in fl:
483 483 s.add(fl.linkrev(fr))
484 484
485 485 return [r for r in subset if r in s]
486 486
487 487 def first(repo, subset, x):
488 488 """``first(set, [n])``
489 489 An alias for limit().
490 490 """
491 491 return limit(repo, subset, x)
492 492
493 493 def _follow(repo, subset, x, name, followfirst=False):
494 494 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
495 495 c = repo['.']
496 496 if l:
497 497 x = getstring(l[0], _("%s expected a filename") % name)
498 498 if x in c:
499 499 cx = c[x]
500 500 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
501 501 # include the revision responsible for the most recent version
502 502 s.add(cx.linkrev())
503 503 else:
504 504 return []
505 505 else:
506 506 s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()])
507 507
508 508 return [r for r in subset if r in s]
509 509
510 510 def follow(repo, subset, x):
511 511 """``follow([file])``
512 512 An alias for ``::.`` (ancestors of the working copy's first parent).
513 513 If a filename is specified, the history of the given file is followed,
514 514 including copies.
515 515 """
516 516 return _follow(repo, subset, x, 'follow')
517 517
518 518 def _followfirst(repo, subset, x):
519 519 # ``followfirst([file])``
520 520 # Like ``follow([file])`` but follows only the first parent of
521 521 # every revision or file revision.
522 522 return _follow(repo, subset, x, '_followfirst', followfirst=True)
523 523
524 524 def getall(repo, subset, x):
525 525 """``all()``
526 526 All changesets, the same as ``0:tip``.
527 527 """
528 528 # i18n: "all" is a keyword
529 529 getargs(x, 0, 0, _("all takes no arguments"))
530 530 return subset
531 531
532 532 def grep(repo, subset, x):
533 533 """``grep(regex)``
534 534 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
535 535 to ensure special escape characters are handled correctly. Unlike
536 536 ``keyword(string)``, the match is case-sensitive.
537 537 """
538 538 try:
539 539 # i18n: "grep" is a keyword
540 540 gr = re.compile(getstring(x, _("grep requires a string")))
541 541 except re.error, e:
542 542 raise error.ParseError(_('invalid match pattern: %s') % e)
543 543 l = []
544 544 for r in subset:
545 545 c = repo[r]
546 546 for e in c.files() + [c.user(), c.description()]:
547 547 if gr.search(e):
548 548 l.append(r)
549 549 break
550 550 return l
551 551
552 552 def _matchfiles(repo, subset, x):
553 553 # _matchfiles takes a revset list of prefixed arguments:
554 554 #
555 555 # [p:foo, i:bar, x:baz]
556 556 #
557 557 # builds a match object from them and filters subset. Allowed
558 558 # prefixes are 'p:' for regular patterns, 'i:' for include
559 559 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
560 560 # a revision identifier, or the empty string to reference the
561 561 # working directory, from which the match object is
562 # initialized. At most one 'r:' argument can be passed.
562 # initialized. Use 'd:' to set the default matching mode, default
563 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
563 564
564 565 # i18n: "_matchfiles" is a keyword
565 566 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
566 567 pats, inc, exc = [], [], []
567 568 hasset = False
568 rev = None
569 rev, default = None, None
569 570 for arg in l:
570 571 s = getstring(arg, _("_matchfiles requires string arguments"))
571 572 prefix, value = s[:2], s[2:]
572 573 if prefix == 'p:':
573 574 pats.append(value)
574 575 elif prefix == 'i:':
575 576 inc.append(value)
576 577 elif prefix == 'x:':
577 578 exc.append(value)
578 579 elif prefix == 'r:':
579 580 if rev is not None:
580 581 raise error.ParseError(_('_matchfiles expected at most one '
581 582 'revision'))
582 583 rev = value
584 elif prefix == 'd:':
585 if default is not None:
586 raise error.ParseError(_('_matchfiles expected at most one '
587 'default mode'))
588 default = value
583 589 else:
584 590 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
585 591 if not hasset and matchmod.patkind(value) == 'set':
586 592 hasset = True
593 if not default:
594 default = 'glob'
587 595 m = None
588 596 s = []
589 597 for r in subset:
590 598 c = repo[r]
591 599 if not m or (hasset and rev is None):
592 600 ctx = c
593 601 if rev is not None:
594 602 ctx = repo[rev or None]
595 603 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
596 exclude=exc, ctx=ctx)
604 exclude=exc, ctx=ctx, default=default)
597 605 for f in c.files():
598 606 if m(f):
599 607 s.append(r)
600 608 break
601 609 return s
602 610
603 611 def hasfile(repo, subset, x):
604 612 """``file(pattern)``
605 613 Changesets affecting files matched by pattern.
606 614 """
607 615 # i18n: "file" is a keyword
608 616 pat = getstring(x, _("file requires a pattern"))
609 617 return _matchfiles(repo, subset, ('string', 'p:' + pat))
610 618
611 619 def head(repo, subset, x):
612 620 """``head()``
613 621 Changeset is a named branch head.
614 622 """
615 623 # i18n: "head" is a keyword
616 624 getargs(x, 0, 0, _("head takes no arguments"))
617 625 hs = set()
618 626 for b, ls in repo.branchmap().iteritems():
619 627 hs.update(repo[h].rev() for h in ls)
620 628 return [r for r in subset if r in hs]
621 629
622 630 def heads(repo, subset, x):
623 631 """``heads(set)``
624 632 Members of set with no children in set.
625 633 """
626 634 s = getset(repo, subset, x)
627 635 ps = set(parents(repo, subset, x))
628 636 return [r for r in s if r not in ps]
629 637
630 638 def keyword(repo, subset, x):
631 639 """``keyword(string)``
632 640 Search commit message, user name, and names of changed files for
633 641 string. The match is case-insensitive.
634 642 """
635 643 # i18n: "keyword" is a keyword
636 644 kw = encoding.lower(getstring(x, _("keyword requires a string")))
637 645 l = []
638 646 for r in subset:
639 647 c = repo[r]
640 648 t = " ".join(c.files() + [c.user(), c.description()])
641 649 if kw in encoding.lower(t):
642 650 l.append(r)
643 651 return l
644 652
645 653 def limit(repo, subset, x):
646 654 """``limit(set, [n])``
647 655 First n members of set, defaulting to 1.
648 656 """
649 657 # i18n: "limit" is a keyword
650 658 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
651 659 try:
652 660 lim = 1
653 661 if len(l) == 2:
654 662 # i18n: "limit" is a keyword
655 663 lim = int(getstring(l[1], _("limit requires a number")))
656 664 except (TypeError, ValueError):
657 665 # i18n: "limit" is a keyword
658 666 raise error.ParseError(_("limit expects a number"))
659 667 ss = set(subset)
660 668 os = getset(repo, range(len(repo)), l[0])[:lim]
661 669 return [r for r in os if r in ss]
662 670
663 671 def last(repo, subset, x):
664 672 """``last(set, [n])``
665 673 Last n members of set, defaulting to 1.
666 674 """
667 675 # i18n: "last" is a keyword
668 676 l = getargs(x, 1, 2, _("last requires one or two arguments"))
669 677 try:
670 678 lim = 1
671 679 if len(l) == 2:
672 680 # i18n: "last" is a keyword
673 681 lim = int(getstring(l[1], _("last requires a number")))
674 682 except (TypeError, ValueError):
675 683 # i18n: "last" is a keyword
676 684 raise error.ParseError(_("last expects a number"))
677 685 ss = set(subset)
678 686 os = getset(repo, range(len(repo)), l[0])[-lim:]
679 687 return [r for r in os if r in ss]
680 688
681 689 def maxrev(repo, subset, x):
682 690 """``max(set)``
683 691 Changeset with highest revision number in set.
684 692 """
685 693 os = getset(repo, range(len(repo)), x)
686 694 if os:
687 695 m = max(os)
688 696 if m in subset:
689 697 return [m]
690 698 return []
691 699
692 700 def merge(repo, subset, x):
693 701 """``merge()``
694 702 Changeset is a merge changeset.
695 703 """
696 704 # i18n: "merge" is a keyword
697 705 getargs(x, 0, 0, _("merge takes no arguments"))
698 706 cl = repo.changelog
699 707 return [r for r in subset if cl.parentrevs(r)[1] != -1]
700 708
701 709 def minrev(repo, subset, x):
702 710 """``min(set)``
703 711 Changeset with lowest revision number in set.
704 712 """
705 713 os = getset(repo, range(len(repo)), x)
706 714 if os:
707 715 m = min(os)
708 716 if m in subset:
709 717 return [m]
710 718 return []
711 719
712 720 def modifies(repo, subset, x):
713 721 """``modifies(pattern)``
714 722 Changesets modifying files matched by pattern.
715 723 """
716 724 # i18n: "modifies" is a keyword
717 725 pat = getstring(x, _("modifies requires a pattern"))
718 726 return checkstatus(repo, subset, pat, 0)
719 727
720 728 def node(repo, subset, x):
721 729 """``id(string)``
722 730 Revision non-ambiguously specified by the given hex string prefix.
723 731 """
724 732 # i18n: "id" is a keyword
725 733 l = getargs(x, 1, 1, _("id requires one argument"))
726 734 # i18n: "id" is a keyword
727 735 n = getstring(l[0], _("id requires a string"))
728 736 if len(n) == 40:
729 737 rn = repo[n].rev()
730 738 else:
731 739 rn = repo.changelog.rev(repo.changelog._partialmatch(n))
732 740 return [r for r in subset if r == rn]
733 741
734 742 def outgoing(repo, subset, x):
735 743 """``outgoing([path])``
736 744 Changesets not found in the specified destination repository, or the
737 745 default push location.
738 746 """
739 747 import hg # avoid start-up nasties
740 748 # i18n: "outgoing" is a keyword
741 749 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
742 750 # i18n: "outgoing" is a keyword
743 751 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
744 752 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
745 753 dest, branches = hg.parseurl(dest)
746 754 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
747 755 if revs:
748 756 revs = [repo.lookup(rev) for rev in revs]
749 757 other = hg.peer(repo, {}, dest)
750 758 repo.ui.pushbuffer()
751 759 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
752 760 repo.ui.popbuffer()
753 761 cl = repo.changelog
754 762 o = set([cl.rev(r) for r in outgoing.missing])
755 763 return [r for r in subset if r in o]
756 764
757 765 def p1(repo, subset, x):
758 766 """``p1([set])``
759 767 First parent of changesets in set, or the working directory.
760 768 """
761 769 if x is None:
762 770 p = repo[x].p1().rev()
763 771 return [r for r in subset if r == p]
764 772
765 773 ps = set()
766 774 cl = repo.changelog
767 775 for r in getset(repo, range(len(repo)), x):
768 776 ps.add(cl.parentrevs(r)[0])
769 777 return [r for r in subset if r in ps]
770 778
771 779 def p2(repo, subset, x):
772 780 """``p2([set])``
773 781 Second parent of changesets in set, or the working directory.
774 782 """
775 783 if x is None:
776 784 ps = repo[x].parents()
777 785 try:
778 786 p = ps[1].rev()
779 787 return [r for r in subset if r == p]
780 788 except IndexError:
781 789 return []
782 790
783 791 ps = set()
784 792 cl = repo.changelog
785 793 for r in getset(repo, range(len(repo)), x):
786 794 ps.add(cl.parentrevs(r)[1])
787 795 return [r for r in subset if r in ps]
788 796
789 797 def parents(repo, subset, x):
790 798 """``parents([set])``
791 799 The set of all parents for all changesets in set, or the working directory.
792 800 """
793 801 if x is None:
794 802 ps = tuple(p.rev() for p in repo[x].parents())
795 803 return [r for r in subset if r in ps]
796 804
797 805 ps = set()
798 806 cl = repo.changelog
799 807 for r in getset(repo, range(len(repo)), x):
800 808 ps.update(cl.parentrevs(r))
801 809 return [r for r in subset if r in ps]
802 810
803 811 def parentspec(repo, subset, x, n):
804 812 """``set^0``
805 813 The set.
806 814 ``set^1`` (or ``set^``), ``set^2``
807 815 First or second parent, respectively, of all changesets in set.
808 816 """
809 817 try:
810 818 n = int(n[1])
811 819 if n not in (0, 1, 2):
812 820 raise ValueError
813 821 except (TypeError, ValueError):
814 822 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
815 823 ps = set()
816 824 cl = repo.changelog
817 825 for r in getset(repo, subset, x):
818 826 if n == 0:
819 827 ps.add(r)
820 828 elif n == 1:
821 829 ps.add(cl.parentrevs(r)[0])
822 830 elif n == 2:
823 831 parents = cl.parentrevs(r)
824 832 if len(parents) > 1:
825 833 ps.add(parents[1])
826 834 return [r for r in subset if r in ps]
827 835
828 836 def present(repo, subset, x):
829 837 """``present(set)``
830 838 An empty set, if any revision in set isn't found; otherwise,
831 839 all revisions in set.
832 840 """
833 841 try:
834 842 return getset(repo, subset, x)
835 843 except error.RepoLookupError:
836 844 return []
837 845
838 846 def public(repo, subset, x):
839 847 """``public()``
840 848 Changeset in public phase."""
841 849 getargs(x, 0, 0, _("public takes no arguments"))
842 850 return [r for r in subset if repo._phaserev[r] == phases.public]
843 851
844 852 def remote(repo, subset, x):
845 853 """``remote([id [,path]])``
846 854 Local revision that corresponds to the given identifier in a
847 855 remote repository, if present. Here, the '.' identifier is a
848 856 synonym for the current local branch.
849 857 """
850 858
851 859 import hg # avoid start-up nasties
852 860 # i18n: "remote" is a keyword
853 861 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
854 862
855 863 q = '.'
856 864 if len(l) > 0:
857 865 # i18n: "remote" is a keyword
858 866 q = getstring(l[0], _("remote requires a string id"))
859 867 if q == '.':
860 868 q = repo['.'].branch()
861 869
862 870 dest = ''
863 871 if len(l) > 1:
864 872 # i18n: "remote" is a keyword
865 873 dest = getstring(l[1], _("remote requires a repository path"))
866 874 dest = repo.ui.expandpath(dest or 'default')
867 875 dest, branches = hg.parseurl(dest)
868 876 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
869 877 if revs:
870 878 revs = [repo.lookup(rev) for rev in revs]
871 879 other = hg.peer(repo, {}, dest)
872 880 n = other.lookup(q)
873 881 if n in repo:
874 882 r = repo[n].rev()
875 883 if r in subset:
876 884 return [r]
877 885 return []
878 886
879 887 def removes(repo, subset, x):
880 888 """``removes(pattern)``
881 889 Changesets which remove files matching pattern.
882 890 """
883 891 # i18n: "removes" is a keyword
884 892 pat = getstring(x, _("removes requires a pattern"))
885 893 return checkstatus(repo, subset, pat, 2)
886 894
887 895 def rev(repo, subset, x):
888 896 """``rev(number)``
889 897 Revision with the given numeric identifier.
890 898 """
891 899 # i18n: "rev" is a keyword
892 900 l = getargs(x, 1, 1, _("rev requires one argument"))
893 901 try:
894 902 # i18n: "rev" is a keyword
895 903 l = int(getstring(l[0], _("rev requires a number")))
896 904 except (TypeError, ValueError):
897 905 # i18n: "rev" is a keyword
898 906 raise error.ParseError(_("rev expects a number"))
899 907 return [r for r in subset if r == l]
900 908
901 909 def matching(repo, subset, x):
902 910 """``matching(revision [, field])``
903 911 Changesets in which a given set of fields match the set of fields in the
904 912 selected revision or set.
905 913 To match more than one field pass the list of fields to match separated
906 914 by spaces (e.g. 'author description').
907 915 Valid fields are most regular revision fields and some special fields:
908 916 * regular fields:
909 917 - description, author, branch, date, files, phase, parents,
910 918 substate, user.
911 919 Note that author and user are synonyms.
912 920 * special fields: summary, metadata.
913 921 - summary: matches the first line of the description.
914 922 - metatadata: It is equivalent to matching 'description user date'
915 923 (i.e. it matches the main metadata fields).
916 924 metadata is the default field which is used when no fields are specified.
917 925 You can match more than one field at a time.
918 926 """
919 927 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
920 928
921 929 revs = getset(repo, xrange(len(repo)), l[0])
922 930
923 931 fieldlist = ['metadata']
924 932 if len(l) > 1:
925 933 fieldlist = getstring(l[1],
926 934 _("matching requires a string "
927 935 "as its second argument")).split()
928 936
929 937 # Make sure that there are no repeated fields, and expand the
930 938 # 'special' 'metadata' field type
931 939 fields = []
932 940 for field in fieldlist:
933 941 if field == 'metadata':
934 942 fields += ['user', 'description', 'date']
935 943 else:
936 944 if field == 'author':
937 945 field = 'user'
938 946 fields.append(field)
939 947 fields = set(fields)
940 948
941 949 # We may want to match more than one field
942 950 # Each field will be matched with its own "getfield" function
943 951 # which will be added to the getfieldfuncs array of functions
944 952 getfieldfuncs = []
945 953 _funcs = {
946 954 'user': lambda r: repo[r].user(),
947 955 'branch': lambda r: repo[r].branch(),
948 956 'date': lambda r: repo[r].date(),
949 957 'description': lambda r: repo[r].description(),
950 958 'files': lambda r: repo[r].files(),
951 959 'parents': lambda r: repo[r].parents(),
952 960 'phase': lambda r: repo[r].phase(),
953 961 'substate': lambda r: repo[r].substate,
954 962 'summary': lambda r: repo[r].description().splitlines()[0],
955 963 }
956 964 for info in fields:
957 965 getfield = _funcs.get(info, None)
958 966 if getfield is None:
959 967 raise error.ParseError(
960 968 _("unexpected field name passed to matching: %s") % info)
961 969 getfieldfuncs.append(getfield)
962 970
963 971 # convert the getfield array of functions into a "getinfo" function
964 972 # which returns an array of field values (or a single value if there
965 973 # is only one field to match)
966 974 if len(getfieldfuncs) == 1:
967 975 getinfo = getfieldfuncs[0]
968 976 else:
969 977 getinfo = lambda r: [f(r) for f in getfieldfuncs]
970 978
971 979 matches = []
972 980 for rev in revs:
973 981 target = getinfo(rev)
974 982 matches += [r for r in subset if getinfo(r) == target]
975 983 if len(revs) > 1:
976 984 matches = sorted(set(matches))
977 985 return matches
978 986
979 987 def reverse(repo, subset, x):
980 988 """``reverse(set)``
981 989 Reverse order of set.
982 990 """
983 991 l = getset(repo, subset, x)
984 992 l.reverse()
985 993 return l
986 994
987 995 def roots(repo, subset, x):
988 996 """``roots(set)``
989 997 Changesets with no parent changeset in set.
990 998 """
991 999 s = getset(repo, xrange(len(repo)), x)
992 1000 cs = _children(repo, s, s)
993 1001 return [r for r in s if r not in cs]
994 1002
995 1003 def secret(repo, subset, x):
996 1004 """``secret()``
997 1005 Changeset in secret phase."""
998 1006 getargs(x, 0, 0, _("secret takes no arguments"))
999 1007 return [r for r in subset if repo._phaserev[r] == phases.secret]
1000 1008
1001 1009 def sort(repo, subset, x):
1002 1010 """``sort(set[, [-]key...])``
1003 1011 Sort set by keys. The default sort order is ascending, specify a key
1004 1012 as ``-key`` to sort in descending order.
1005 1013
1006 1014 The keys can be:
1007 1015
1008 1016 - ``rev`` for the revision number,
1009 1017 - ``branch`` for the branch name,
1010 1018 - ``desc`` for the commit message (description),
1011 1019 - ``user`` for user name (``author`` can be used as an alias),
1012 1020 - ``date`` for the commit date
1013 1021 """
1014 1022 # i18n: "sort" is a keyword
1015 1023 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1016 1024 keys = "rev"
1017 1025 if len(l) == 2:
1018 1026 keys = getstring(l[1], _("sort spec must be a string"))
1019 1027
1020 1028 s = l[0]
1021 1029 keys = keys.split()
1022 1030 l = []
1023 1031 def invert(s):
1024 1032 return "".join(chr(255 - ord(c)) for c in s)
1025 1033 for r in getset(repo, subset, s):
1026 1034 c = repo[r]
1027 1035 e = []
1028 1036 for k in keys:
1029 1037 if k == 'rev':
1030 1038 e.append(r)
1031 1039 elif k == '-rev':
1032 1040 e.append(-r)
1033 1041 elif k == 'branch':
1034 1042 e.append(c.branch())
1035 1043 elif k == '-branch':
1036 1044 e.append(invert(c.branch()))
1037 1045 elif k == 'desc':
1038 1046 e.append(c.description())
1039 1047 elif k == '-desc':
1040 1048 e.append(invert(c.description()))
1041 1049 elif k in 'user author':
1042 1050 e.append(c.user())
1043 1051 elif k in '-user -author':
1044 1052 e.append(invert(c.user()))
1045 1053 elif k == 'date':
1046 1054 e.append(c.date()[0])
1047 1055 elif k == '-date':
1048 1056 e.append(-c.date()[0])
1049 1057 else:
1050 1058 raise error.ParseError(_("unknown sort key %r") % k)
1051 1059 e.append(r)
1052 1060 l.append(e)
1053 1061 l.sort()
1054 1062 return [e[-1] for e in l]
1055 1063
1056 1064 def tag(repo, subset, x):
1057 1065 """``tag([name])``
1058 1066 The specified tag by name, or all tagged revisions if no name is given.
1059 1067 """
1060 1068 # i18n: "tag" is a keyword
1061 1069 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1062 1070 cl = repo.changelog
1063 1071 if args:
1064 1072 tn = getstring(args[0],
1065 1073 # i18n: "tag" is a keyword
1066 1074 _('the argument to tag must be a string'))
1067 1075 if not repo.tags().get(tn, None):
1068 1076 raise util.Abort(_("tag '%s' does not exist") % tn)
1069 1077 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
1070 1078 else:
1071 1079 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1072 1080 return [r for r in subset if r in s]
1073 1081
1074 1082 def tagged(repo, subset, x):
1075 1083 return tag(repo, subset, x)
1076 1084
1077 1085 def user(repo, subset, x):
1078 1086 """``user(string)``
1079 1087 User name contains string. The match is case-insensitive.
1080 1088 """
1081 1089 return author(repo, subset, x)
1082 1090
1083 1091 # for internal use
1084 1092 def _list(repo, subset, x):
1085 1093 s = getstring(x, "internal error")
1086 1094 if not s:
1087 1095 return []
1088 1096 if not isinstance(subset, set):
1089 1097 subset = set(subset)
1090 1098 ls = [repo[r].rev() for r in s.split('\0')]
1091 1099 return [r for r in ls if r in subset]
1092 1100
1093 1101 symbols = {
1094 1102 "adds": adds,
1095 1103 "all": getall,
1096 1104 "ancestor": ancestor,
1097 1105 "ancestors": ancestors,
1098 1106 "_firstancestors": _firstancestors,
1099 1107 "author": author,
1100 1108 "bisect": bisect,
1101 1109 "bisected": bisected,
1102 1110 "bookmark": bookmark,
1103 1111 "branch": branch,
1104 1112 "children": children,
1105 1113 "closed": closed,
1106 1114 "contains": contains,
1107 1115 "date": date,
1108 1116 "desc": desc,
1109 1117 "descendants": descendants,
1110 1118 "_firstdescendants": _firstdescendants,
1111 1119 "draft": draft,
1112 1120 "file": hasfile,
1113 1121 "filelog": filelog,
1114 1122 "first": first,
1115 1123 "follow": follow,
1116 1124 "_followfirst": _followfirst,
1117 1125 "grep": grep,
1118 1126 "head": head,
1119 1127 "heads": heads,
1120 1128 "id": node,
1121 1129 "keyword": keyword,
1122 1130 "last": last,
1123 1131 "limit": limit,
1124 1132 "_matchfiles": _matchfiles,
1125 1133 "max": maxrev,
1126 1134 "merge": merge,
1127 1135 "min": minrev,
1128 1136 "modifies": modifies,
1129 1137 "outgoing": outgoing,
1130 1138 "p1": p1,
1131 1139 "p2": p2,
1132 1140 "parents": parents,
1133 1141 "present": present,
1134 1142 "public": public,
1135 1143 "remote": remote,
1136 1144 "removes": removes,
1137 1145 "rev": rev,
1138 1146 "reverse": reverse,
1139 1147 "roots": roots,
1140 1148 "sort": sort,
1141 1149 "secret": secret,
1142 1150 "matching": matching,
1143 1151 "tag": tag,
1144 1152 "tagged": tagged,
1145 1153 "user": user,
1146 1154 "_list": _list,
1147 1155 }
1148 1156
1149 1157 methods = {
1150 1158 "range": rangeset,
1151 1159 "string": stringset,
1152 1160 "symbol": symbolset,
1153 1161 "and": andset,
1154 1162 "or": orset,
1155 1163 "not": notset,
1156 1164 "list": listset,
1157 1165 "func": func,
1158 1166 "ancestor": ancestorspec,
1159 1167 "parent": parentspec,
1160 1168 "parentpost": p1,
1161 1169 }
1162 1170
1163 1171 def optimize(x, small):
1164 1172 if x is None:
1165 1173 return 0, x
1166 1174
1167 1175 smallbonus = 1
1168 1176 if small:
1169 1177 smallbonus = .5
1170 1178
1171 1179 op = x[0]
1172 1180 if op == 'minus':
1173 1181 return optimize(('and', x[1], ('not', x[2])), small)
1174 1182 elif op == 'dagrange':
1175 1183 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
1176 1184 ('func', ('symbol', 'ancestors'), x[2])), small)
1177 1185 elif op == 'dagrangepre':
1178 1186 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
1179 1187 elif op == 'dagrangepost':
1180 1188 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
1181 1189 elif op == 'rangepre':
1182 1190 return optimize(('range', ('string', '0'), x[1]), small)
1183 1191 elif op == 'rangepost':
1184 1192 return optimize(('range', x[1], ('string', 'tip')), small)
1185 1193 elif op == 'negate':
1186 1194 return optimize(('string',
1187 1195 '-' + getstring(x[1], _("can't negate that"))), small)
1188 1196 elif op in 'string symbol negate':
1189 1197 return smallbonus, x # single revisions are small
1190 1198 elif op == 'and' or op == 'dagrange':
1191 1199 wa, ta = optimize(x[1], True)
1192 1200 wb, tb = optimize(x[2], True)
1193 1201 w = min(wa, wb)
1194 1202 if wa > wb:
1195 1203 return w, (op, tb, ta)
1196 1204 return w, (op, ta, tb)
1197 1205 elif op == 'or':
1198 1206 wa, ta = optimize(x[1], False)
1199 1207 wb, tb = optimize(x[2], False)
1200 1208 if wb < wa:
1201 1209 wb, wa = wa, wb
1202 1210 return max(wa, wb), (op, ta, tb)
1203 1211 elif op == 'not':
1204 1212 o = optimize(x[1], not small)
1205 1213 return o[0], (op, o[1])
1206 1214 elif op == 'parentpost':
1207 1215 o = optimize(x[1], small)
1208 1216 return o[0], (op, o[1])
1209 1217 elif op == 'group':
1210 1218 return optimize(x[1], small)
1211 1219 elif op in 'range list parent ancestorspec':
1212 1220 if op == 'parent':
1213 1221 # x^:y means (x^) : y, not x ^ (:y)
1214 1222 post = ('parentpost', x[1])
1215 1223 if x[2][0] == 'dagrangepre':
1216 1224 return optimize(('dagrange', post, x[2][1]), small)
1217 1225 elif x[2][0] == 'rangepre':
1218 1226 return optimize(('range', post, x[2][1]), small)
1219 1227
1220 1228 wa, ta = optimize(x[1], small)
1221 1229 wb, tb = optimize(x[2], small)
1222 1230 return wa + wb, (op, ta, tb)
1223 1231 elif op == 'func':
1224 1232 f = getstring(x[1], _("not a symbol"))
1225 1233 wa, ta = optimize(x[2], small)
1226 1234 if f in ("author branch closed date desc file grep keyword "
1227 1235 "outgoing user"):
1228 1236 w = 10 # slow
1229 1237 elif f in "modifies adds removes":
1230 1238 w = 30 # slower
1231 1239 elif f == "contains":
1232 1240 w = 100 # very slow
1233 1241 elif f == "ancestor":
1234 1242 w = 1 * smallbonus
1235 1243 elif f in "reverse limit first":
1236 1244 w = 0
1237 1245 elif f in "sort":
1238 1246 w = 10 # assume most sorts look at changelog
1239 1247 else:
1240 1248 w = 1
1241 1249 return w + wa, (op, x[1], ta)
1242 1250 return 1, x
1243 1251
1244 1252 class revsetalias(object):
1245 1253 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
1246 1254 args = None
1247 1255
1248 1256 def __init__(self, name, value):
1249 1257 '''Aliases like:
1250 1258
1251 1259 h = heads(default)
1252 1260 b($1) = ancestors($1) - ancestors(default)
1253 1261 '''
1254 1262 m = self.funcre.search(name)
1255 1263 if m:
1256 1264 self.name = m.group(1)
1257 1265 self.tree = ('func', ('symbol', m.group(1)))
1258 1266 self.args = [x.strip() for x in m.group(2).split(',')]
1259 1267 for arg in self.args:
1260 1268 value = value.replace(arg, repr(arg))
1261 1269 else:
1262 1270 self.name = name
1263 1271 self.tree = ('symbol', name)
1264 1272
1265 1273 self.replacement, pos = parse(value)
1266 1274 if pos != len(value):
1267 1275 raise error.ParseError(_('invalid token'), pos)
1268 1276
1269 1277 def _getalias(aliases, tree):
1270 1278 """If tree looks like an unexpanded alias, return it. Return None
1271 1279 otherwise.
1272 1280 """
1273 1281 if isinstance(tree, tuple) and tree:
1274 1282 if tree[0] == 'symbol' and len(tree) == 2:
1275 1283 name = tree[1]
1276 1284 alias = aliases.get(name)
1277 1285 if alias and alias.args is None and alias.tree == tree:
1278 1286 return alias
1279 1287 if tree[0] == 'func' and len(tree) > 1:
1280 1288 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
1281 1289 name = tree[1][1]
1282 1290 alias = aliases.get(name)
1283 1291 if alias and alias.args is not None and alias.tree == tree[:2]:
1284 1292 return alias
1285 1293 return None
1286 1294
1287 1295 def _expandargs(tree, args):
1288 1296 """Replace all occurences of ('string', name) with the
1289 1297 substitution value of the same name in args, recursively.
1290 1298 """
1291 1299 if not isinstance(tree, tuple):
1292 1300 return tree
1293 1301 if len(tree) == 2 and tree[0] == 'string':
1294 1302 return args.get(tree[1], tree)
1295 1303 return tuple(_expandargs(t, args) for t in tree)
1296 1304
1297 1305 def _expandaliases(aliases, tree, expanding):
1298 1306 """Expand aliases in tree, recursively.
1299 1307
1300 1308 'aliases' is a dictionary mapping user defined aliases to
1301 1309 revsetalias objects.
1302 1310 """
1303 1311 if not isinstance(tree, tuple):
1304 1312 # Do not expand raw strings
1305 1313 return tree
1306 1314 alias = _getalias(aliases, tree)
1307 1315 if alias is not None:
1308 1316 if alias in expanding:
1309 1317 raise error.ParseError(_('infinite expansion of revset alias "%s" '
1310 1318 'detected') % alias.name)
1311 1319 expanding.append(alias)
1312 1320 result = alias.replacement
1313 1321 if alias.args is not None:
1314 1322 l = getlist(tree[2])
1315 1323 if len(l) != len(alias.args):
1316 1324 raise error.ParseError(
1317 1325 _('invalid number of arguments: %s') % len(l))
1318 1326 result = _expandargs(result, dict(zip(alias.args, l)))
1319 1327 # Recurse in place, the base expression may have been rewritten
1320 1328 result = _expandaliases(aliases, result, expanding)
1321 1329 expanding.pop()
1322 1330 else:
1323 1331 result = tuple(_expandaliases(aliases, t, expanding)
1324 1332 for t in tree)
1325 1333 return result
1326 1334
1327 1335 def findaliases(ui, tree):
1328 1336 aliases = {}
1329 1337 for k, v in ui.configitems('revsetalias'):
1330 1338 alias = revsetalias(k, v)
1331 1339 aliases[alias.name] = alias
1332 1340 return _expandaliases(aliases, tree, [])
1333 1341
1334 1342 parse = parser.parser(tokenize, elements).parse
1335 1343
1336 1344 def match(ui, spec):
1337 1345 if not spec:
1338 1346 raise error.ParseError(_("empty query"))
1339 1347 tree, pos = parse(spec)
1340 1348 if (pos != len(spec)):
1341 1349 raise error.ParseError(_("invalid token"), pos)
1342 1350 if ui:
1343 1351 tree = findaliases(ui, tree)
1344 1352 weight, tree = optimize(tree, True)
1345 1353 def mfunc(repo, subset):
1346 1354 return getset(repo, subset, tree)
1347 1355 return mfunc
1348 1356
1349 1357 def formatspec(expr, *args):
1350 1358 '''
1351 1359 This is a convenience function for using revsets internally, and
1352 1360 escapes arguments appropriately. Aliases are intentionally ignored
1353 1361 so that intended expression behavior isn't accidentally subverted.
1354 1362
1355 1363 Supported arguments:
1356 1364
1357 1365 %r = revset expression, parenthesized
1358 1366 %d = int(arg), no quoting
1359 1367 %s = string(arg), escaped and single-quoted
1360 1368 %b = arg.branch(), escaped and single-quoted
1361 1369 %n = hex(arg), single-quoted
1362 1370 %% = a literal '%'
1363 1371
1364 1372 Prefixing the type with 'l' specifies a parenthesized list of that type.
1365 1373
1366 1374 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
1367 1375 '(10 or 11):: and ((this()) or (that()))'
1368 1376 >>> formatspec('%d:: and not %d::', 10, 20)
1369 1377 '10:: and not 20::'
1370 1378 >>> formatspec('%ld or %ld', [], [1])
1371 1379 "_list('') or 1"
1372 1380 >>> formatspec('keyword(%s)', 'foo\\xe9')
1373 1381 "keyword('foo\\\\xe9')"
1374 1382 >>> b = lambda: 'default'
1375 1383 >>> b.branch = b
1376 1384 >>> formatspec('branch(%b)', b)
1377 1385 "branch('default')"
1378 1386 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
1379 1387 "root(_list('a\\x00b\\x00c\\x00d'))"
1380 1388 '''
1381 1389
1382 1390 def quote(s):
1383 1391 return repr(str(s))
1384 1392
1385 1393 def argtype(c, arg):
1386 1394 if c == 'd':
1387 1395 return str(int(arg))
1388 1396 elif c == 's':
1389 1397 return quote(arg)
1390 1398 elif c == 'r':
1391 1399 parse(arg) # make sure syntax errors are confined
1392 1400 return '(%s)' % arg
1393 1401 elif c == 'n':
1394 1402 return quote(nodemod.hex(arg))
1395 1403 elif c == 'b':
1396 1404 return quote(arg.branch())
1397 1405
1398 1406 def listexp(s, t):
1399 1407 l = len(s)
1400 1408 if l == 0:
1401 1409 return "_list('')"
1402 1410 elif l == 1:
1403 1411 return argtype(t, s[0])
1404 1412 elif t == 'd':
1405 1413 return "_list('%s')" % "\0".join(str(int(a)) for a in s)
1406 1414 elif t == 's':
1407 1415 return "_list('%s')" % "\0".join(s)
1408 1416 elif t == 'n':
1409 1417 return "_list('%s')" % "\0".join(nodemod.hex(a) for a in s)
1410 1418 elif t == 'b':
1411 1419 return "_list('%s')" % "\0".join(a.branch() for a in s)
1412 1420
1413 1421 m = l // 2
1414 1422 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
1415 1423
1416 1424 ret = ''
1417 1425 pos = 0
1418 1426 arg = 0
1419 1427 while pos < len(expr):
1420 1428 c = expr[pos]
1421 1429 if c == '%':
1422 1430 pos += 1
1423 1431 d = expr[pos]
1424 1432 if d == '%':
1425 1433 ret += d
1426 1434 elif d in 'dsnbr':
1427 1435 ret += argtype(d, args[arg])
1428 1436 arg += 1
1429 1437 elif d == 'l':
1430 1438 # a list of some type
1431 1439 pos += 1
1432 1440 d = expr[pos]
1433 1441 ret += listexp(list(args[arg]), d)
1434 1442 arg += 1
1435 1443 else:
1436 1444 raise util.Abort('unexpected revspec format character %s' % d)
1437 1445 else:
1438 1446 ret += c
1439 1447 pos += 1
1440 1448
1441 1449 return ret
1442 1450
1443 1451 def prettyformat(tree):
1444 1452 def _prettyformat(tree, level, lines):
1445 1453 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
1446 1454 lines.append((level, str(tree)))
1447 1455 else:
1448 1456 lines.append((level, '(%s' % tree[0]))
1449 1457 for s in tree[1:]:
1450 1458 _prettyformat(s, level + 1, lines)
1451 1459 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
1452 1460
1453 1461 lines = []
1454 1462 _prettyformat(tree, 0, lines)
1455 1463 output = '\n'.join((' '*l + s) for l, s in lines)
1456 1464 return output
1457 1465
1458 1466 # tell hggettext to extract docstrings from these functions:
1459 1467 i18nfunctions = symbols.values()
@@ -1,2003 +1,2028 b''
1 1 @ (34) head
2 2 |
3 3 | o (33) head
4 4 | |
5 5 o | (32) expand
6 6 |\ \
7 7 | o \ (31) expand
8 8 | |\ \
9 9 | | o \ (30) expand
10 10 | | |\ \
11 11 | | | o | (29) regular commit
12 12 | | | | |
13 13 | | o | | (28) merge zero known
14 14 | | |\ \ \
15 15 o | | | | | (27) collapse
16 16 |/ / / / /
17 17 | | o---+ (26) merge one known; far right
18 18 | | | | |
19 19 +---o | | (25) merge one known; far left
20 20 | | | | |
21 21 | | o | | (24) merge one known; immediate right
22 22 | | |\| |
23 23 | | o | | (23) merge one known; immediate left
24 24 | |/| | |
25 25 +---o---+ (22) merge two known; one far left, one far right
26 26 | | / /
27 27 o | | | (21) expand
28 28 |\ \ \ \
29 29 | o---+-+ (20) merge two known; two far right
30 30 | / / /
31 31 o | | | (19) expand
32 32 |\ \ \ \
33 33 +---+---o (18) merge two known; two far left
34 34 | | | |
35 35 | o | | (17) expand
36 36 | |\ \ \
37 37 | | o---+ (16) merge two known; one immediate right, one near right
38 38 | | |/ /
39 39 o | | | (15) expand
40 40 |\ \ \ \
41 41 | o-----+ (14) merge two known; one immediate right, one far right
42 42 | |/ / /
43 43 o | | | (13) expand
44 44 |\ \ \ \
45 45 +---o | | (12) merge two known; one immediate right, one far left
46 46 | | |/ /
47 47 | o | | (11) expand
48 48 | |\ \ \
49 49 | | o---+ (10) merge two known; one immediate left, one near right
50 50 | |/ / /
51 51 o | | | (9) expand
52 52 |\ \ \ \
53 53 | o-----+ (8) merge two known; one immediate left, one far right
54 54 |/ / / /
55 55 o | | | (7) expand
56 56 |\ \ \ \
57 57 +---o | | (6) merge two known; one immediate left, one far left
58 58 | |/ / /
59 59 | o | | (5) expand
60 60 | |\ \ \
61 61 | | o | | (4) merge two known; one immediate left, one immediate right
62 62 | |/|/ /
63 63 | o / / (3) collapse
64 64 |/ / /
65 65 o / / (2) collapse
66 66 |/ /
67 67 o / (1) collapse
68 68 |/
69 69 o (0) root
70 70
71 71
72 72 $ "$TESTDIR/hghave" no-outer-repo || exit 80
73 73
74 74 $ commit()
75 75 > {
76 76 > rev=$1
77 77 > msg=$2
78 78 > shift 2
79 79 > if [ "$#" -gt 0 ]; then
80 80 > hg debugsetparents "$@"
81 81 > fi
82 82 > echo $rev > a
83 83 > hg commit -Aqd "$rev 0" -m "($rev) $msg"
84 84 > }
85 85
86 86 $ cat > printrevset.py <<EOF
87 87 > from mercurial import extensions, revset, commands
88 88 > from hgext import graphlog
89 89 >
90 90 > def uisetup(ui):
91 91 > def printrevset(orig, ui, repo, *pats, **opts):
92 92 > if opts.get('print_revset'):
93 93 > expr = graphlog.getlogrevs(repo, pats, opts)[1]
94 94 > if expr:
95 95 > tree = revset.parse(expr)[0]
96 96 > else:
97 97 > tree = []
98 98 > ui.write('%r\n' % (opts.get('rev', []),))
99 99 > ui.write(revset.prettyformat(tree) + '\n')
100 100 > return 0
101 101 > return orig(ui, repo, *pats, **opts)
102 102 > entry = extensions.wrapcommand(commands.table, 'log', printrevset)
103 103 > entry[1].append(('', 'print-revset', False,
104 104 > 'print generated revset and exit (DEPRECATED)'))
105 105 > EOF
106 106
107 107 $ echo "[extensions]" >> $HGRCPATH
108 108 $ echo "graphlog=" >> $HGRCPATH
109 109 $ echo "printrevset=`pwd`/printrevset.py" >> $HGRCPATH
110 110
111 111 $ hg init repo
112 112 $ cd repo
113 113
114 114 Empty repo:
115 115
116 116 $ hg glog
117 117
118 118
119 119 Building DAG:
120 120
121 121 $ commit 0 "root"
122 122 $ commit 1 "collapse" 0
123 123 $ commit 2 "collapse" 1
124 124 $ commit 3 "collapse" 2
125 125 $ commit 4 "merge two known; one immediate left, one immediate right" 1 3
126 126 $ commit 5 "expand" 3 4
127 127 $ commit 6 "merge two known; one immediate left, one far left" 2 5
128 128 $ commit 7 "expand" 2 5
129 129 $ commit 8 "merge two known; one immediate left, one far right" 0 7
130 130 $ commit 9 "expand" 7 8
131 131 $ commit 10 "merge two known; one immediate left, one near right" 0 6
132 132 $ commit 11 "expand" 6 10
133 133 $ commit 12 "merge two known; one immediate right, one far left" 1 9
134 134 $ commit 13 "expand" 9 11
135 135 $ commit 14 "merge two known; one immediate right, one far right" 0 12
136 136 $ commit 15 "expand" 13 14
137 137 $ commit 16 "merge two known; one immediate right, one near right" 0 1
138 138 $ commit 17 "expand" 12 16
139 139 $ commit 18 "merge two known; two far left" 1 15
140 140 $ commit 19 "expand" 15 17
141 141 $ commit 20 "merge two known; two far right" 0 18
142 142 $ commit 21 "expand" 19 20
143 143 $ commit 22 "merge two known; one far left, one far right" 18 21
144 144 $ commit 23 "merge one known; immediate left" 1 22
145 145 $ commit 24 "merge one known; immediate right" 0 23
146 146 $ commit 25 "merge one known; far left" 21 24
147 147 $ commit 26 "merge one known; far right" 18 25
148 148 $ commit 27 "collapse" 21
149 149 $ commit 28 "merge zero known" 1 26
150 150 $ commit 29 "regular commit" 0
151 151 $ commit 30 "expand" 28 29
152 152 $ commit 31 "expand" 21 30
153 153 $ commit 32 "expand" 27 31
154 154 $ commit 33 "head" 18
155 155 $ commit 34 "head" 32
156 156
157 157
158 158 $ hg glog -q
159 159 @ 34:fea3ac5810e0
160 160 |
161 161 | o 33:68608f5145f9
162 162 | |
163 163 o | 32:d06dffa21a31
164 164 |\ \
165 165 | o \ 31:621d83e11f67
166 166 | |\ \
167 167 | | o \ 30:6e11cd4b648f
168 168 | | |\ \
169 169 | | | o | 29:cd9bb2be7593
170 170 | | | | |
171 171 | | o | | 28:44ecd0b9ae99
172 172 | | |\ \ \
173 173 o | | | | | 27:886ed638191b
174 174 |/ / / / /
175 175 | | o---+ 26:7f25b6c2f0b9
176 176 | | | | |
177 177 +---o | | 25:91da8ed57247
178 178 | | | | |
179 179 | | o | | 24:a9c19a3d96b7
180 180 | | |\| |
181 181 | | o | | 23:a01cddf0766d
182 182 | |/| | |
183 183 +---o---+ 22:e0d9cccacb5d
184 184 | | / /
185 185 o | | | 21:d42a756af44d
186 186 |\ \ \ \
187 187 | o---+-+ 20:d30ed6450e32
188 188 | / / /
189 189 o | | | 19:31ddc2c1573b
190 190 |\ \ \ \
191 191 +---+---o 18:1aa84d96232a
192 192 | | | |
193 193 | o | | 17:44765d7c06e0
194 194 | |\ \ \
195 195 | | o---+ 16:3677d192927d
196 196 | | |/ /
197 197 o | | | 15:1dda3f72782d
198 198 |\ \ \ \
199 199 | o-----+ 14:8eac370358ef
200 200 | |/ / /
201 201 o | | | 13:22d8966a97e3
202 202 |\ \ \ \
203 203 +---o | | 12:86b91144a6e9
204 204 | | |/ /
205 205 | o | | 11:832d76e6bdf2
206 206 | |\ \ \
207 207 | | o---+ 10:74c64d036d72
208 208 | |/ / /
209 209 o | | | 9:7010c0af0a35
210 210 |\ \ \ \
211 211 | o-----+ 8:7a0b11f71937
212 212 |/ / / /
213 213 o | | | 7:b632bb1b1224
214 214 |\ \ \ \
215 215 +---o | | 6:b105a072e251
216 216 | |/ / /
217 217 | o | | 5:4409d547b708
218 218 | |\ \ \
219 219 | | o | | 4:26a8bac39d9f
220 220 | |/|/ /
221 221 | o / / 3:27eef8ed80b4
222 222 |/ / /
223 223 o / / 2:3d9a33b8d1e1
224 224 |/ /
225 225 o / 1:6db2ef61d156
226 226 |/
227 227 o 0:e6eb3150255d
228 228
229 229
230 230 $ hg glog
231 231 @ changeset: 34:fea3ac5810e0
232 232 | tag: tip
233 233 | parent: 32:d06dffa21a31
234 234 | user: test
235 235 | date: Thu Jan 01 00:00:34 1970 +0000
236 236 | summary: (34) head
237 237 |
238 238 | o changeset: 33:68608f5145f9
239 239 | | parent: 18:1aa84d96232a
240 240 | | user: test
241 241 | | date: Thu Jan 01 00:00:33 1970 +0000
242 242 | | summary: (33) head
243 243 | |
244 244 o | changeset: 32:d06dffa21a31
245 245 |\ \ parent: 27:886ed638191b
246 246 | | | parent: 31:621d83e11f67
247 247 | | | user: test
248 248 | | | date: Thu Jan 01 00:00:32 1970 +0000
249 249 | | | summary: (32) expand
250 250 | | |
251 251 | o | changeset: 31:621d83e11f67
252 252 | |\ \ parent: 21:d42a756af44d
253 253 | | | | parent: 30:6e11cd4b648f
254 254 | | | | user: test
255 255 | | | | date: Thu Jan 01 00:00:31 1970 +0000
256 256 | | | | summary: (31) expand
257 257 | | | |
258 258 | | o | changeset: 30:6e11cd4b648f
259 259 | | |\ \ parent: 28:44ecd0b9ae99
260 260 | | | | | parent: 29:cd9bb2be7593
261 261 | | | | | user: test
262 262 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
263 263 | | | | | summary: (30) expand
264 264 | | | | |
265 265 | | | o | changeset: 29:cd9bb2be7593
266 266 | | | | | parent: 0:e6eb3150255d
267 267 | | | | | user: test
268 268 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
269 269 | | | | | summary: (29) regular commit
270 270 | | | | |
271 271 | | o | | changeset: 28:44ecd0b9ae99
272 272 | | |\ \ \ parent: 1:6db2ef61d156
273 273 | | | | | | parent: 26:7f25b6c2f0b9
274 274 | | | | | | user: test
275 275 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
276 276 | | | | | | summary: (28) merge zero known
277 277 | | | | | |
278 278 o | | | | | changeset: 27:886ed638191b
279 279 |/ / / / / parent: 21:d42a756af44d
280 280 | | | | | user: test
281 281 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
282 282 | | | | | summary: (27) collapse
283 283 | | | | |
284 284 | | o---+ changeset: 26:7f25b6c2f0b9
285 285 | | | | | parent: 18:1aa84d96232a
286 286 | | | | | parent: 25:91da8ed57247
287 287 | | | | | user: test
288 288 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
289 289 | | | | | summary: (26) merge one known; far right
290 290 | | | | |
291 291 +---o | | changeset: 25:91da8ed57247
292 292 | | | | | parent: 21:d42a756af44d
293 293 | | | | | parent: 24:a9c19a3d96b7
294 294 | | | | | user: test
295 295 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
296 296 | | | | | summary: (25) merge one known; far left
297 297 | | | | |
298 298 | | o | | changeset: 24:a9c19a3d96b7
299 299 | | |\| | parent: 0:e6eb3150255d
300 300 | | | | | parent: 23:a01cddf0766d
301 301 | | | | | user: test
302 302 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
303 303 | | | | | summary: (24) merge one known; immediate right
304 304 | | | | |
305 305 | | o | | changeset: 23:a01cddf0766d
306 306 | |/| | | parent: 1:6db2ef61d156
307 307 | | | | | parent: 22:e0d9cccacb5d
308 308 | | | | | user: test
309 309 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
310 310 | | | | | summary: (23) merge one known; immediate left
311 311 | | | | |
312 312 +---o---+ changeset: 22:e0d9cccacb5d
313 313 | | | | parent: 18:1aa84d96232a
314 314 | | / / parent: 21:d42a756af44d
315 315 | | | | user: test
316 316 | | | | date: Thu Jan 01 00:00:22 1970 +0000
317 317 | | | | summary: (22) merge two known; one far left, one far right
318 318 | | | |
319 319 o | | | changeset: 21:d42a756af44d
320 320 |\ \ \ \ parent: 19:31ddc2c1573b
321 321 | | | | | parent: 20:d30ed6450e32
322 322 | | | | | user: test
323 323 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
324 324 | | | | | summary: (21) expand
325 325 | | | | |
326 326 | o---+-+ changeset: 20:d30ed6450e32
327 327 | | | | parent: 0:e6eb3150255d
328 328 | / / / parent: 18:1aa84d96232a
329 329 | | | | user: test
330 330 | | | | date: Thu Jan 01 00:00:20 1970 +0000
331 331 | | | | summary: (20) merge two known; two far right
332 332 | | | |
333 333 o | | | changeset: 19:31ddc2c1573b
334 334 |\ \ \ \ parent: 15:1dda3f72782d
335 335 | | | | | parent: 17:44765d7c06e0
336 336 | | | | | user: test
337 337 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
338 338 | | | | | summary: (19) expand
339 339 | | | | |
340 340 +---+---o changeset: 18:1aa84d96232a
341 341 | | | | parent: 1:6db2ef61d156
342 342 | | | | parent: 15:1dda3f72782d
343 343 | | | | user: test
344 344 | | | | date: Thu Jan 01 00:00:18 1970 +0000
345 345 | | | | summary: (18) merge two known; two far left
346 346 | | | |
347 347 | o | | changeset: 17:44765d7c06e0
348 348 | |\ \ \ parent: 12:86b91144a6e9
349 349 | | | | | parent: 16:3677d192927d
350 350 | | | | | user: test
351 351 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
352 352 | | | | | summary: (17) expand
353 353 | | | | |
354 354 | | o---+ changeset: 16:3677d192927d
355 355 | | | | | parent: 0:e6eb3150255d
356 356 | | |/ / parent: 1:6db2ef61d156
357 357 | | | | user: test
358 358 | | | | date: Thu Jan 01 00:00:16 1970 +0000
359 359 | | | | summary: (16) merge two known; one immediate right, one near right
360 360 | | | |
361 361 o | | | changeset: 15:1dda3f72782d
362 362 |\ \ \ \ parent: 13:22d8966a97e3
363 363 | | | | | parent: 14:8eac370358ef
364 364 | | | | | user: test
365 365 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
366 366 | | | | | summary: (15) expand
367 367 | | | | |
368 368 | o-----+ changeset: 14:8eac370358ef
369 369 | | | | | parent: 0:e6eb3150255d
370 370 | |/ / / parent: 12:86b91144a6e9
371 371 | | | | user: test
372 372 | | | | date: Thu Jan 01 00:00:14 1970 +0000
373 373 | | | | summary: (14) merge two known; one immediate right, one far right
374 374 | | | |
375 375 o | | | changeset: 13:22d8966a97e3
376 376 |\ \ \ \ parent: 9:7010c0af0a35
377 377 | | | | | parent: 11:832d76e6bdf2
378 378 | | | | | user: test
379 379 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
380 380 | | | | | summary: (13) expand
381 381 | | | | |
382 382 +---o | | changeset: 12:86b91144a6e9
383 383 | | |/ / parent: 1:6db2ef61d156
384 384 | | | | parent: 9:7010c0af0a35
385 385 | | | | user: test
386 386 | | | | date: Thu Jan 01 00:00:12 1970 +0000
387 387 | | | | summary: (12) merge two known; one immediate right, one far left
388 388 | | | |
389 389 | o | | changeset: 11:832d76e6bdf2
390 390 | |\ \ \ parent: 6:b105a072e251
391 391 | | | | | parent: 10:74c64d036d72
392 392 | | | | | user: test
393 393 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
394 394 | | | | | summary: (11) expand
395 395 | | | | |
396 396 | | o---+ changeset: 10:74c64d036d72
397 397 | | | | | parent: 0:e6eb3150255d
398 398 | |/ / / parent: 6:b105a072e251
399 399 | | | | user: test
400 400 | | | | date: Thu Jan 01 00:00:10 1970 +0000
401 401 | | | | summary: (10) merge two known; one immediate left, one near right
402 402 | | | |
403 403 o | | | changeset: 9:7010c0af0a35
404 404 |\ \ \ \ parent: 7:b632bb1b1224
405 405 | | | | | parent: 8:7a0b11f71937
406 406 | | | | | user: test
407 407 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
408 408 | | | | | summary: (9) expand
409 409 | | | | |
410 410 | o-----+ changeset: 8:7a0b11f71937
411 411 | | | | | parent: 0:e6eb3150255d
412 412 |/ / / / parent: 7:b632bb1b1224
413 413 | | | | user: test
414 414 | | | | date: Thu Jan 01 00:00:08 1970 +0000
415 415 | | | | summary: (8) merge two known; one immediate left, one far right
416 416 | | | |
417 417 o | | | changeset: 7:b632bb1b1224
418 418 |\ \ \ \ parent: 2:3d9a33b8d1e1
419 419 | | | | | parent: 5:4409d547b708
420 420 | | | | | user: test
421 421 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
422 422 | | | | | summary: (7) expand
423 423 | | | | |
424 424 +---o | | changeset: 6:b105a072e251
425 425 | |/ / / parent: 2:3d9a33b8d1e1
426 426 | | | | parent: 5:4409d547b708
427 427 | | | | user: test
428 428 | | | | date: Thu Jan 01 00:00:06 1970 +0000
429 429 | | | | summary: (6) merge two known; one immediate left, one far left
430 430 | | | |
431 431 | o | | changeset: 5:4409d547b708
432 432 | |\ \ \ parent: 3:27eef8ed80b4
433 433 | | | | | parent: 4:26a8bac39d9f
434 434 | | | | | user: test
435 435 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
436 436 | | | | | summary: (5) expand
437 437 | | | | |
438 438 | | o | | changeset: 4:26a8bac39d9f
439 439 | |/|/ / parent: 1:6db2ef61d156
440 440 | | | | parent: 3:27eef8ed80b4
441 441 | | | | user: test
442 442 | | | | date: Thu Jan 01 00:00:04 1970 +0000
443 443 | | | | summary: (4) merge two known; one immediate left, one immediate right
444 444 | | | |
445 445 | o | | changeset: 3:27eef8ed80b4
446 446 |/ / / user: test
447 447 | | | date: Thu Jan 01 00:00:03 1970 +0000
448 448 | | | summary: (3) collapse
449 449 | | |
450 450 o | | changeset: 2:3d9a33b8d1e1
451 451 |/ / user: test
452 452 | | date: Thu Jan 01 00:00:02 1970 +0000
453 453 | | summary: (2) collapse
454 454 | |
455 455 o | changeset: 1:6db2ef61d156
456 456 |/ user: test
457 457 | date: Thu Jan 01 00:00:01 1970 +0000
458 458 | summary: (1) collapse
459 459 |
460 460 o changeset: 0:e6eb3150255d
461 461 user: test
462 462 date: Thu Jan 01 00:00:00 1970 +0000
463 463 summary: (0) root
464 464
465 465
466 466 File glog:
467 467 $ hg glog a
468 468 @ changeset: 34:fea3ac5810e0
469 469 | tag: tip
470 470 | parent: 32:d06dffa21a31
471 471 | user: test
472 472 | date: Thu Jan 01 00:00:34 1970 +0000
473 473 | summary: (34) head
474 474 |
475 475 | o changeset: 33:68608f5145f9
476 476 | | parent: 18:1aa84d96232a
477 477 | | user: test
478 478 | | date: Thu Jan 01 00:00:33 1970 +0000
479 479 | | summary: (33) head
480 480 | |
481 481 o | changeset: 32:d06dffa21a31
482 482 |\ \ parent: 27:886ed638191b
483 483 | | | parent: 31:621d83e11f67
484 484 | | | user: test
485 485 | | | date: Thu Jan 01 00:00:32 1970 +0000
486 486 | | | summary: (32) expand
487 487 | | |
488 488 | o | changeset: 31:621d83e11f67
489 489 | |\ \ parent: 21:d42a756af44d
490 490 | | | | parent: 30:6e11cd4b648f
491 491 | | | | user: test
492 492 | | | | date: Thu Jan 01 00:00:31 1970 +0000
493 493 | | | | summary: (31) expand
494 494 | | | |
495 495 | | o | changeset: 30:6e11cd4b648f
496 496 | | |\ \ parent: 28:44ecd0b9ae99
497 497 | | | | | parent: 29:cd9bb2be7593
498 498 | | | | | user: test
499 499 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
500 500 | | | | | summary: (30) expand
501 501 | | | | |
502 502 | | | o | changeset: 29:cd9bb2be7593
503 503 | | | | | parent: 0:e6eb3150255d
504 504 | | | | | user: test
505 505 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
506 506 | | | | | summary: (29) regular commit
507 507 | | | | |
508 508 | | o | | changeset: 28:44ecd0b9ae99
509 509 | | |\ \ \ parent: 1:6db2ef61d156
510 510 | | | | | | parent: 26:7f25b6c2f0b9
511 511 | | | | | | user: test
512 512 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
513 513 | | | | | | summary: (28) merge zero known
514 514 | | | | | |
515 515 o | | | | | changeset: 27:886ed638191b
516 516 |/ / / / / parent: 21:d42a756af44d
517 517 | | | | | user: test
518 518 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
519 519 | | | | | summary: (27) collapse
520 520 | | | | |
521 521 | | o---+ changeset: 26:7f25b6c2f0b9
522 522 | | | | | parent: 18:1aa84d96232a
523 523 | | | | | parent: 25:91da8ed57247
524 524 | | | | | user: test
525 525 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
526 526 | | | | | summary: (26) merge one known; far right
527 527 | | | | |
528 528 +---o | | changeset: 25:91da8ed57247
529 529 | | | | | parent: 21:d42a756af44d
530 530 | | | | | parent: 24:a9c19a3d96b7
531 531 | | | | | user: test
532 532 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
533 533 | | | | | summary: (25) merge one known; far left
534 534 | | | | |
535 535 | | o | | changeset: 24:a9c19a3d96b7
536 536 | | |\| | parent: 0:e6eb3150255d
537 537 | | | | | parent: 23:a01cddf0766d
538 538 | | | | | user: test
539 539 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
540 540 | | | | | summary: (24) merge one known; immediate right
541 541 | | | | |
542 542 | | o | | changeset: 23:a01cddf0766d
543 543 | |/| | | parent: 1:6db2ef61d156
544 544 | | | | | parent: 22:e0d9cccacb5d
545 545 | | | | | user: test
546 546 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
547 547 | | | | | summary: (23) merge one known; immediate left
548 548 | | | | |
549 549 +---o---+ changeset: 22:e0d9cccacb5d
550 550 | | | | parent: 18:1aa84d96232a
551 551 | | / / parent: 21:d42a756af44d
552 552 | | | | user: test
553 553 | | | | date: Thu Jan 01 00:00:22 1970 +0000
554 554 | | | | summary: (22) merge two known; one far left, one far right
555 555 | | | |
556 556 o | | | changeset: 21:d42a756af44d
557 557 |\ \ \ \ parent: 19:31ddc2c1573b
558 558 | | | | | parent: 20:d30ed6450e32
559 559 | | | | | user: test
560 560 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
561 561 | | | | | summary: (21) expand
562 562 | | | | |
563 563 | o---+-+ changeset: 20:d30ed6450e32
564 564 | | | | parent: 0:e6eb3150255d
565 565 | / / / parent: 18:1aa84d96232a
566 566 | | | | user: test
567 567 | | | | date: Thu Jan 01 00:00:20 1970 +0000
568 568 | | | | summary: (20) merge two known; two far right
569 569 | | | |
570 570 o | | | changeset: 19:31ddc2c1573b
571 571 |\ \ \ \ parent: 15:1dda3f72782d
572 572 | | | | | parent: 17:44765d7c06e0
573 573 | | | | | user: test
574 574 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
575 575 | | | | | summary: (19) expand
576 576 | | | | |
577 577 +---+---o changeset: 18:1aa84d96232a
578 578 | | | | parent: 1:6db2ef61d156
579 579 | | | | parent: 15:1dda3f72782d
580 580 | | | | user: test
581 581 | | | | date: Thu Jan 01 00:00:18 1970 +0000
582 582 | | | | summary: (18) merge two known; two far left
583 583 | | | |
584 584 | o | | changeset: 17:44765d7c06e0
585 585 | |\ \ \ parent: 12:86b91144a6e9
586 586 | | | | | parent: 16:3677d192927d
587 587 | | | | | user: test
588 588 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
589 589 | | | | | summary: (17) expand
590 590 | | | | |
591 591 | | o---+ changeset: 16:3677d192927d
592 592 | | | | | parent: 0:e6eb3150255d
593 593 | | |/ / parent: 1:6db2ef61d156
594 594 | | | | user: test
595 595 | | | | date: Thu Jan 01 00:00:16 1970 +0000
596 596 | | | | summary: (16) merge two known; one immediate right, one near right
597 597 | | | |
598 598 o | | | changeset: 15:1dda3f72782d
599 599 |\ \ \ \ parent: 13:22d8966a97e3
600 600 | | | | | parent: 14:8eac370358ef
601 601 | | | | | user: test
602 602 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
603 603 | | | | | summary: (15) expand
604 604 | | | | |
605 605 | o-----+ changeset: 14:8eac370358ef
606 606 | | | | | parent: 0:e6eb3150255d
607 607 | |/ / / parent: 12:86b91144a6e9
608 608 | | | | user: test
609 609 | | | | date: Thu Jan 01 00:00:14 1970 +0000
610 610 | | | | summary: (14) merge two known; one immediate right, one far right
611 611 | | | |
612 612 o | | | changeset: 13:22d8966a97e3
613 613 |\ \ \ \ parent: 9:7010c0af0a35
614 614 | | | | | parent: 11:832d76e6bdf2
615 615 | | | | | user: test
616 616 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
617 617 | | | | | summary: (13) expand
618 618 | | | | |
619 619 +---o | | changeset: 12:86b91144a6e9
620 620 | | |/ / parent: 1:6db2ef61d156
621 621 | | | | parent: 9:7010c0af0a35
622 622 | | | | user: test
623 623 | | | | date: Thu Jan 01 00:00:12 1970 +0000
624 624 | | | | summary: (12) merge two known; one immediate right, one far left
625 625 | | | |
626 626 | o | | changeset: 11:832d76e6bdf2
627 627 | |\ \ \ parent: 6:b105a072e251
628 628 | | | | | parent: 10:74c64d036d72
629 629 | | | | | user: test
630 630 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
631 631 | | | | | summary: (11) expand
632 632 | | | | |
633 633 | | o---+ changeset: 10:74c64d036d72
634 634 | | | | | parent: 0:e6eb3150255d
635 635 | |/ / / parent: 6:b105a072e251
636 636 | | | | user: test
637 637 | | | | date: Thu Jan 01 00:00:10 1970 +0000
638 638 | | | | summary: (10) merge two known; one immediate left, one near right
639 639 | | | |
640 640 o | | | changeset: 9:7010c0af0a35
641 641 |\ \ \ \ parent: 7:b632bb1b1224
642 642 | | | | | parent: 8:7a0b11f71937
643 643 | | | | | user: test
644 644 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
645 645 | | | | | summary: (9) expand
646 646 | | | | |
647 647 | o-----+ changeset: 8:7a0b11f71937
648 648 | | | | | parent: 0:e6eb3150255d
649 649 |/ / / / parent: 7:b632bb1b1224
650 650 | | | | user: test
651 651 | | | | date: Thu Jan 01 00:00:08 1970 +0000
652 652 | | | | summary: (8) merge two known; one immediate left, one far right
653 653 | | | |
654 654 o | | | changeset: 7:b632bb1b1224
655 655 |\ \ \ \ parent: 2:3d9a33b8d1e1
656 656 | | | | | parent: 5:4409d547b708
657 657 | | | | | user: test
658 658 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
659 659 | | | | | summary: (7) expand
660 660 | | | | |
661 661 +---o | | changeset: 6:b105a072e251
662 662 | |/ / / parent: 2:3d9a33b8d1e1
663 663 | | | | parent: 5:4409d547b708
664 664 | | | | user: test
665 665 | | | | date: Thu Jan 01 00:00:06 1970 +0000
666 666 | | | | summary: (6) merge two known; one immediate left, one far left
667 667 | | | |
668 668 | o | | changeset: 5:4409d547b708
669 669 | |\ \ \ parent: 3:27eef8ed80b4
670 670 | | | | | parent: 4:26a8bac39d9f
671 671 | | | | | user: test
672 672 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
673 673 | | | | | summary: (5) expand
674 674 | | | | |
675 675 | | o | | changeset: 4:26a8bac39d9f
676 676 | |/|/ / parent: 1:6db2ef61d156
677 677 | | | | parent: 3:27eef8ed80b4
678 678 | | | | user: test
679 679 | | | | date: Thu Jan 01 00:00:04 1970 +0000
680 680 | | | | summary: (4) merge two known; one immediate left, one immediate right
681 681 | | | |
682 682 | o | | changeset: 3:27eef8ed80b4
683 683 |/ / / user: test
684 684 | | | date: Thu Jan 01 00:00:03 1970 +0000
685 685 | | | summary: (3) collapse
686 686 | | |
687 687 o | | changeset: 2:3d9a33b8d1e1
688 688 |/ / user: test
689 689 | | date: Thu Jan 01 00:00:02 1970 +0000
690 690 | | summary: (2) collapse
691 691 | |
692 692 o | changeset: 1:6db2ef61d156
693 693 |/ user: test
694 694 | date: Thu Jan 01 00:00:01 1970 +0000
695 695 | summary: (1) collapse
696 696 |
697 697 o changeset: 0:e6eb3150255d
698 698 user: test
699 699 date: Thu Jan 01 00:00:00 1970 +0000
700 700 summary: (0) root
701 701
702 702
703 703 File glog per revset:
704 704
705 705 $ hg glog -r 'file("a")'
706 706 @ changeset: 34:fea3ac5810e0
707 707 | tag: tip
708 708 | parent: 32:d06dffa21a31
709 709 | user: test
710 710 | date: Thu Jan 01 00:00:34 1970 +0000
711 711 | summary: (34) head
712 712 |
713 713 | o changeset: 33:68608f5145f9
714 714 | | parent: 18:1aa84d96232a
715 715 | | user: test
716 716 | | date: Thu Jan 01 00:00:33 1970 +0000
717 717 | | summary: (33) head
718 718 | |
719 719 o | changeset: 32:d06dffa21a31
720 720 |\ \ parent: 27:886ed638191b
721 721 | | | parent: 31:621d83e11f67
722 722 | | | user: test
723 723 | | | date: Thu Jan 01 00:00:32 1970 +0000
724 724 | | | summary: (32) expand
725 725 | | |
726 726 | o | changeset: 31:621d83e11f67
727 727 | |\ \ parent: 21:d42a756af44d
728 728 | | | | parent: 30:6e11cd4b648f
729 729 | | | | user: test
730 730 | | | | date: Thu Jan 01 00:00:31 1970 +0000
731 731 | | | | summary: (31) expand
732 732 | | | |
733 733 | | o | changeset: 30:6e11cd4b648f
734 734 | | |\ \ parent: 28:44ecd0b9ae99
735 735 | | | | | parent: 29:cd9bb2be7593
736 736 | | | | | user: test
737 737 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
738 738 | | | | | summary: (30) expand
739 739 | | | | |
740 740 | | | o | changeset: 29:cd9bb2be7593
741 741 | | | | | parent: 0:e6eb3150255d
742 742 | | | | | user: test
743 743 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
744 744 | | | | | summary: (29) regular commit
745 745 | | | | |
746 746 | | o | | changeset: 28:44ecd0b9ae99
747 747 | | |\ \ \ parent: 1:6db2ef61d156
748 748 | | | | | | parent: 26:7f25b6c2f0b9
749 749 | | | | | | user: test
750 750 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
751 751 | | | | | | summary: (28) merge zero known
752 752 | | | | | |
753 753 o | | | | | changeset: 27:886ed638191b
754 754 |/ / / / / parent: 21:d42a756af44d
755 755 | | | | | user: test
756 756 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
757 757 | | | | | summary: (27) collapse
758 758 | | | | |
759 759 | | o---+ changeset: 26:7f25b6c2f0b9
760 760 | | | | | parent: 18:1aa84d96232a
761 761 | | | | | parent: 25:91da8ed57247
762 762 | | | | | user: test
763 763 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
764 764 | | | | | summary: (26) merge one known; far right
765 765 | | | | |
766 766 +---o | | changeset: 25:91da8ed57247
767 767 | | | | | parent: 21:d42a756af44d
768 768 | | | | | parent: 24:a9c19a3d96b7
769 769 | | | | | user: test
770 770 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
771 771 | | | | | summary: (25) merge one known; far left
772 772 | | | | |
773 773 | | o | | changeset: 24:a9c19a3d96b7
774 774 | | |\| | parent: 0:e6eb3150255d
775 775 | | | | | parent: 23:a01cddf0766d
776 776 | | | | | user: test
777 777 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
778 778 | | | | | summary: (24) merge one known; immediate right
779 779 | | | | |
780 780 | | o | | changeset: 23:a01cddf0766d
781 781 | |/| | | parent: 1:6db2ef61d156
782 782 | | | | | parent: 22:e0d9cccacb5d
783 783 | | | | | user: test
784 784 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
785 785 | | | | | summary: (23) merge one known; immediate left
786 786 | | | | |
787 787 +---o---+ changeset: 22:e0d9cccacb5d
788 788 | | | | parent: 18:1aa84d96232a
789 789 | | / / parent: 21:d42a756af44d
790 790 | | | | user: test
791 791 | | | | date: Thu Jan 01 00:00:22 1970 +0000
792 792 | | | | summary: (22) merge two known; one far left, one far right
793 793 | | | |
794 794 o | | | changeset: 21:d42a756af44d
795 795 |\ \ \ \ parent: 19:31ddc2c1573b
796 796 | | | | | parent: 20:d30ed6450e32
797 797 | | | | | user: test
798 798 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
799 799 | | | | | summary: (21) expand
800 800 | | | | |
801 801 | o---+-+ changeset: 20:d30ed6450e32
802 802 | | | | parent: 0:e6eb3150255d
803 803 | / / / parent: 18:1aa84d96232a
804 804 | | | | user: test
805 805 | | | | date: Thu Jan 01 00:00:20 1970 +0000
806 806 | | | | summary: (20) merge two known; two far right
807 807 | | | |
808 808 o | | | changeset: 19:31ddc2c1573b
809 809 |\ \ \ \ parent: 15:1dda3f72782d
810 810 | | | | | parent: 17:44765d7c06e0
811 811 | | | | | user: test
812 812 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
813 813 | | | | | summary: (19) expand
814 814 | | | | |
815 815 +---+---o changeset: 18:1aa84d96232a
816 816 | | | | parent: 1:6db2ef61d156
817 817 | | | | parent: 15:1dda3f72782d
818 818 | | | | user: test
819 819 | | | | date: Thu Jan 01 00:00:18 1970 +0000
820 820 | | | | summary: (18) merge two known; two far left
821 821 | | | |
822 822 | o | | changeset: 17:44765d7c06e0
823 823 | |\ \ \ parent: 12:86b91144a6e9
824 824 | | | | | parent: 16:3677d192927d
825 825 | | | | | user: test
826 826 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
827 827 | | | | | summary: (17) expand
828 828 | | | | |
829 829 | | o---+ changeset: 16:3677d192927d
830 830 | | | | | parent: 0:e6eb3150255d
831 831 | | |/ / parent: 1:6db2ef61d156
832 832 | | | | user: test
833 833 | | | | date: Thu Jan 01 00:00:16 1970 +0000
834 834 | | | | summary: (16) merge two known; one immediate right, one near right
835 835 | | | |
836 836 o | | | changeset: 15:1dda3f72782d
837 837 |\ \ \ \ parent: 13:22d8966a97e3
838 838 | | | | | parent: 14:8eac370358ef
839 839 | | | | | user: test
840 840 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
841 841 | | | | | summary: (15) expand
842 842 | | | | |
843 843 | o-----+ changeset: 14:8eac370358ef
844 844 | | | | | parent: 0:e6eb3150255d
845 845 | |/ / / parent: 12:86b91144a6e9
846 846 | | | | user: test
847 847 | | | | date: Thu Jan 01 00:00:14 1970 +0000
848 848 | | | | summary: (14) merge two known; one immediate right, one far right
849 849 | | | |
850 850 o | | | changeset: 13:22d8966a97e3
851 851 |\ \ \ \ parent: 9:7010c0af0a35
852 852 | | | | | parent: 11:832d76e6bdf2
853 853 | | | | | user: test
854 854 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
855 855 | | | | | summary: (13) expand
856 856 | | | | |
857 857 +---o | | changeset: 12:86b91144a6e9
858 858 | | |/ / parent: 1:6db2ef61d156
859 859 | | | | parent: 9:7010c0af0a35
860 860 | | | | user: test
861 861 | | | | date: Thu Jan 01 00:00:12 1970 +0000
862 862 | | | | summary: (12) merge two known; one immediate right, one far left
863 863 | | | |
864 864 | o | | changeset: 11:832d76e6bdf2
865 865 | |\ \ \ parent: 6:b105a072e251
866 866 | | | | | parent: 10:74c64d036d72
867 867 | | | | | user: test
868 868 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
869 869 | | | | | summary: (11) expand
870 870 | | | | |
871 871 | | o---+ changeset: 10:74c64d036d72
872 872 | | | | | parent: 0:e6eb3150255d
873 873 | |/ / / parent: 6:b105a072e251
874 874 | | | | user: test
875 875 | | | | date: Thu Jan 01 00:00:10 1970 +0000
876 876 | | | | summary: (10) merge two known; one immediate left, one near right
877 877 | | | |
878 878 o | | | changeset: 9:7010c0af0a35
879 879 |\ \ \ \ parent: 7:b632bb1b1224
880 880 | | | | | parent: 8:7a0b11f71937
881 881 | | | | | user: test
882 882 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
883 883 | | | | | summary: (9) expand
884 884 | | | | |
885 885 | o-----+ changeset: 8:7a0b11f71937
886 886 | | | | | parent: 0:e6eb3150255d
887 887 |/ / / / parent: 7:b632bb1b1224
888 888 | | | | user: test
889 889 | | | | date: Thu Jan 01 00:00:08 1970 +0000
890 890 | | | | summary: (8) merge two known; one immediate left, one far right
891 891 | | | |
892 892 o | | | changeset: 7:b632bb1b1224
893 893 |\ \ \ \ parent: 2:3d9a33b8d1e1
894 894 | | | | | parent: 5:4409d547b708
895 895 | | | | | user: test
896 896 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
897 897 | | | | | summary: (7) expand
898 898 | | | | |
899 899 +---o | | changeset: 6:b105a072e251
900 900 | |/ / / parent: 2:3d9a33b8d1e1
901 901 | | | | parent: 5:4409d547b708
902 902 | | | | user: test
903 903 | | | | date: Thu Jan 01 00:00:06 1970 +0000
904 904 | | | | summary: (6) merge two known; one immediate left, one far left
905 905 | | | |
906 906 | o | | changeset: 5:4409d547b708
907 907 | |\ \ \ parent: 3:27eef8ed80b4
908 908 | | | | | parent: 4:26a8bac39d9f
909 909 | | | | | user: test
910 910 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
911 911 | | | | | summary: (5) expand
912 912 | | | | |
913 913 | | o | | changeset: 4:26a8bac39d9f
914 914 | |/|/ / parent: 1:6db2ef61d156
915 915 | | | | parent: 3:27eef8ed80b4
916 916 | | | | user: test
917 917 | | | | date: Thu Jan 01 00:00:04 1970 +0000
918 918 | | | | summary: (4) merge two known; one immediate left, one immediate right
919 919 | | | |
920 920 | o | | changeset: 3:27eef8ed80b4
921 921 |/ / / user: test
922 922 | | | date: Thu Jan 01 00:00:03 1970 +0000
923 923 | | | summary: (3) collapse
924 924 | | |
925 925 o | | changeset: 2:3d9a33b8d1e1
926 926 |/ / user: test
927 927 | | date: Thu Jan 01 00:00:02 1970 +0000
928 928 | | summary: (2) collapse
929 929 | |
930 930 o | changeset: 1:6db2ef61d156
931 931 |/ user: test
932 932 | date: Thu Jan 01 00:00:01 1970 +0000
933 933 | summary: (1) collapse
934 934 |
935 935 o changeset: 0:e6eb3150255d
936 936 user: test
937 937 date: Thu Jan 01 00:00:00 1970 +0000
938 938 summary: (0) root
939 939
940 940
941 941
942 942 File glog per revset (only merges):
943 943
944 944 $ hg log -G -r 'file("a")' -m
945 945 o changeset: 32:d06dffa21a31
946 946 |\ parent: 27:886ed638191b
947 947 | | parent: 31:621d83e11f67
948 948 | | user: test
949 949 | | date: Thu Jan 01 00:00:32 1970 +0000
950 950 | | summary: (32) expand
951 951 | |
952 952 o | changeset: 31:621d83e11f67
953 953 |\| parent: 21:d42a756af44d
954 954 | | parent: 30:6e11cd4b648f
955 955 | | user: test
956 956 | | date: Thu Jan 01 00:00:31 1970 +0000
957 957 | | summary: (31) expand
958 958 | |
959 959 o | changeset: 30:6e11cd4b648f
960 960 |\ \ parent: 28:44ecd0b9ae99
961 961 | | | parent: 29:cd9bb2be7593
962 962 | | | user: test
963 963 | | | date: Thu Jan 01 00:00:30 1970 +0000
964 964 | | | summary: (30) expand
965 965 | | |
966 966 o | | changeset: 28:44ecd0b9ae99
967 967 |\ \ \ parent: 1:6db2ef61d156
968 968 | | | | parent: 26:7f25b6c2f0b9
969 969 | | | | user: test
970 970 | | | | date: Thu Jan 01 00:00:28 1970 +0000
971 971 | | | | summary: (28) merge zero known
972 972 | | | |
973 973 o | | | changeset: 26:7f25b6c2f0b9
974 974 |\ \ \ \ parent: 18:1aa84d96232a
975 975 | | | | | parent: 25:91da8ed57247
976 976 | | | | | user: test
977 977 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
978 978 | | | | | summary: (26) merge one known; far right
979 979 | | | | |
980 980 | o-----+ changeset: 25:91da8ed57247
981 981 | | | | | parent: 21:d42a756af44d
982 982 | | | | | parent: 24:a9c19a3d96b7
983 983 | | | | | user: test
984 984 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
985 985 | | | | | summary: (25) merge one known; far left
986 986 | | | | |
987 987 | o | | | changeset: 24:a9c19a3d96b7
988 988 | |\ \ \ \ parent: 0:e6eb3150255d
989 989 | | | | | | parent: 23:a01cddf0766d
990 990 | | | | | | user: test
991 991 | | | | | | date: Thu Jan 01 00:00:24 1970 +0000
992 992 | | | | | | summary: (24) merge one known; immediate right
993 993 | | | | | |
994 994 | o---+ | | changeset: 23:a01cddf0766d
995 995 | | | | | | parent: 1:6db2ef61d156
996 996 | | | | | | parent: 22:e0d9cccacb5d
997 997 | | | | | | user: test
998 998 | | | | | | date: Thu Jan 01 00:00:23 1970 +0000
999 999 | | | | | | summary: (23) merge one known; immediate left
1000 1000 | | | | | |
1001 1001 | o-------+ changeset: 22:e0d9cccacb5d
1002 1002 | | | | | | parent: 18:1aa84d96232a
1003 1003 |/ / / / / parent: 21:d42a756af44d
1004 1004 | | | | | user: test
1005 1005 | | | | | date: Thu Jan 01 00:00:22 1970 +0000
1006 1006 | | | | | summary: (22) merge two known; one far left, one far right
1007 1007 | | | | |
1008 1008 | | | | o changeset: 21:d42a756af44d
1009 1009 | | | | |\ parent: 19:31ddc2c1573b
1010 1010 | | | | | | parent: 20:d30ed6450e32
1011 1011 | | | | | | user: test
1012 1012 | | | | | | date: Thu Jan 01 00:00:21 1970 +0000
1013 1013 | | | | | | summary: (21) expand
1014 1014 | | | | | |
1015 1015 +-+-------o changeset: 20:d30ed6450e32
1016 1016 | | | | | parent: 0:e6eb3150255d
1017 1017 | | | | | parent: 18:1aa84d96232a
1018 1018 | | | | | user: test
1019 1019 | | | | | date: Thu Jan 01 00:00:20 1970 +0000
1020 1020 | | | | | summary: (20) merge two known; two far right
1021 1021 | | | | |
1022 1022 | | | | o changeset: 19:31ddc2c1573b
1023 1023 | | | | |\ parent: 15:1dda3f72782d
1024 1024 | | | | | | parent: 17:44765d7c06e0
1025 1025 | | | | | | user: test
1026 1026 | | | | | | date: Thu Jan 01 00:00:19 1970 +0000
1027 1027 | | | | | | summary: (19) expand
1028 1028 | | | | | |
1029 1029 o---+---+ | changeset: 18:1aa84d96232a
1030 1030 | | | | | parent: 1:6db2ef61d156
1031 1031 / / / / / parent: 15:1dda3f72782d
1032 1032 | | | | | user: test
1033 1033 | | | | | date: Thu Jan 01 00:00:18 1970 +0000
1034 1034 | | | | | summary: (18) merge two known; two far left
1035 1035 | | | | |
1036 1036 | | | | o changeset: 17:44765d7c06e0
1037 1037 | | | | |\ parent: 12:86b91144a6e9
1038 1038 | | | | | | parent: 16:3677d192927d
1039 1039 | | | | | | user: test
1040 1040 | | | | | | date: Thu Jan 01 00:00:17 1970 +0000
1041 1041 | | | | | | summary: (17) expand
1042 1042 | | | | | |
1043 1043 +-+-------o changeset: 16:3677d192927d
1044 1044 | | | | | parent: 0:e6eb3150255d
1045 1045 | | | | | parent: 1:6db2ef61d156
1046 1046 | | | | | user: test
1047 1047 | | | | | date: Thu Jan 01 00:00:16 1970 +0000
1048 1048 | | | | | summary: (16) merge two known; one immediate right, one near right
1049 1049 | | | | |
1050 1050 | | | o | changeset: 15:1dda3f72782d
1051 1051 | | | |\ \ parent: 13:22d8966a97e3
1052 1052 | | | | | | parent: 14:8eac370358ef
1053 1053 | | | | | | user: test
1054 1054 | | | | | | date: Thu Jan 01 00:00:15 1970 +0000
1055 1055 | | | | | | summary: (15) expand
1056 1056 | | | | | |
1057 1057 +-------o | changeset: 14:8eac370358ef
1058 1058 | | | | |/ parent: 0:e6eb3150255d
1059 1059 | | | | | parent: 12:86b91144a6e9
1060 1060 | | | | | user: test
1061 1061 | | | | | date: Thu Jan 01 00:00:14 1970 +0000
1062 1062 | | | | | summary: (14) merge two known; one immediate right, one far right
1063 1063 | | | | |
1064 1064 | | | o | changeset: 13:22d8966a97e3
1065 1065 | | | |\ \ parent: 9:7010c0af0a35
1066 1066 | | | | | | parent: 11:832d76e6bdf2
1067 1067 | | | | | | user: test
1068 1068 | | | | | | date: Thu Jan 01 00:00:13 1970 +0000
1069 1069 | | | | | | summary: (13) expand
1070 1070 | | | | | |
1071 1071 | +---+---o changeset: 12:86b91144a6e9
1072 1072 | | | | | parent: 1:6db2ef61d156
1073 1073 | | | | | parent: 9:7010c0af0a35
1074 1074 | | | | | user: test
1075 1075 | | | | | date: Thu Jan 01 00:00:12 1970 +0000
1076 1076 | | | | | summary: (12) merge two known; one immediate right, one far left
1077 1077 | | | | |
1078 1078 | | | | o changeset: 11:832d76e6bdf2
1079 1079 | | | | |\ parent: 6:b105a072e251
1080 1080 | | | | | | parent: 10:74c64d036d72
1081 1081 | | | | | | user: test
1082 1082 | | | | | | date: Thu Jan 01 00:00:11 1970 +0000
1083 1083 | | | | | | summary: (11) expand
1084 1084 | | | | | |
1085 1085 +---------o changeset: 10:74c64d036d72
1086 1086 | | | | |/ parent: 0:e6eb3150255d
1087 1087 | | | | | parent: 6:b105a072e251
1088 1088 | | | | | user: test
1089 1089 | | | | | date: Thu Jan 01 00:00:10 1970 +0000
1090 1090 | | | | | summary: (10) merge two known; one immediate left, one near right
1091 1091 | | | | |
1092 1092 | | | o | changeset: 9:7010c0af0a35
1093 1093 | | | |\ \ parent: 7:b632bb1b1224
1094 1094 | | | | | | parent: 8:7a0b11f71937
1095 1095 | | | | | | user: test
1096 1096 | | | | | | date: Thu Jan 01 00:00:09 1970 +0000
1097 1097 | | | | | | summary: (9) expand
1098 1098 | | | | | |
1099 1099 +-------o | changeset: 8:7a0b11f71937
1100 1100 | | | |/ / parent: 0:e6eb3150255d
1101 1101 | | | | | parent: 7:b632bb1b1224
1102 1102 | | | | | user: test
1103 1103 | | | | | date: Thu Jan 01 00:00:08 1970 +0000
1104 1104 | | | | | summary: (8) merge two known; one immediate left, one far right
1105 1105 | | | | |
1106 1106 | | | o | changeset: 7:b632bb1b1224
1107 1107 | | | |\ \ parent: 2:3d9a33b8d1e1
1108 1108 | | | | | | parent: 5:4409d547b708
1109 1109 | | | | | | user: test
1110 1110 | | | | | | date: Thu Jan 01 00:00:07 1970 +0000
1111 1111 | | | | | | summary: (7) expand
1112 1112 | | | | | |
1113 1113 | | | +---o changeset: 6:b105a072e251
1114 1114 | | | | |/ parent: 2:3d9a33b8d1e1
1115 1115 | | | | | parent: 5:4409d547b708
1116 1116 | | | | | user: test
1117 1117 | | | | | date: Thu Jan 01 00:00:06 1970 +0000
1118 1118 | | | | | summary: (6) merge two known; one immediate left, one far left
1119 1119 | | | | |
1120 1120 | | | o | changeset: 5:4409d547b708
1121 1121 | | | |\ \ parent: 3:27eef8ed80b4
1122 1122 | | | | | | parent: 4:26a8bac39d9f
1123 1123 | | | | | | user: test
1124 1124 | | | | | | date: Thu Jan 01 00:00:05 1970 +0000
1125 1125 | | | | | | summary: (5) expand
1126 1126 | | | | | |
1127 1127 | +---o | | changeset: 4:26a8bac39d9f
1128 1128 | | | |/ / parent: 1:6db2ef61d156
1129 1129 | | | | | parent: 3:27eef8ed80b4
1130 1130 | | | | | user: test
1131 1131 | | | | | date: Thu Jan 01 00:00:04 1970 +0000
1132 1132 | | | | | summary: (4) merge two known; one immediate left, one immediate right
1133 1133 | | | | |
1134 1134
1135 1135
1136 1136 Empty revision range - display nothing:
1137 1137 $ hg glog -r 1..0
1138 1138
1139 1139 From outer space:
1140 1140 $ cd ..
1141 1141 $ hg glog -l1 repo
1142 1142 @ changeset: 34:fea3ac5810e0
1143 1143 | tag: tip
1144 1144 | parent: 32:d06dffa21a31
1145 1145 | user: test
1146 1146 | date: Thu Jan 01 00:00:34 1970 +0000
1147 1147 | summary: (34) head
1148 1148 |
1149 1149 $ hg glog -l1 repo/a
1150 1150 @ changeset: 34:fea3ac5810e0
1151 1151 | tag: tip
1152 1152 | parent: 32:d06dffa21a31
1153 1153 | user: test
1154 1154 | date: Thu Jan 01 00:00:34 1970 +0000
1155 1155 | summary: (34) head
1156 1156 |
1157 1157 $ hg glog -l1 repo/missing
1158 1158
1159 1159 File log with revs != cset revs:
1160 1160 $ hg init flog
1161 1161 $ cd flog
1162 1162 $ echo one >one
1163 1163 $ hg add one
1164 1164 $ hg commit -mone
1165 1165 $ echo two >two
1166 1166 $ hg add two
1167 1167 $ hg commit -mtwo
1168 1168 $ echo more >two
1169 1169 $ hg commit -mmore
1170 1170 $ hg glog two
1171 1171 @ changeset: 2:12c28321755b
1172 1172 | tag: tip
1173 1173 | user: test
1174 1174 | date: Thu Jan 01 00:00:00 1970 +0000
1175 1175 | summary: more
1176 1176 |
1177 1177 o changeset: 1:5ac72c0599bf
1178 1178 | user: test
1179 1179 | date: Thu Jan 01 00:00:00 1970 +0000
1180 1180 | summary: two
1181 1181 |
1182 1182
1183 1183 Issue1896: File log with explicit style
1184 1184 $ hg glog --style=default one
1185 1185 o changeset: 0:3d578b4a1f53
1186 1186 user: test
1187 1187 date: Thu Jan 01 00:00:00 1970 +0000
1188 1188 summary: one
1189 1189
1190 1190 Issue2395: glog --style header and footer
1191 1191 $ hg glog --style=xml one
1192 1192 <?xml version="1.0"?>
1193 1193 <log>
1194 1194 o <logentry revision="0" node="3d578b4a1f537d5fcf7301bfa9c0b97adfaa6fb1">
1195 1195 <author email="test">test</author>
1196 1196 <date>1970-01-01T00:00:00+00:00</date>
1197 1197 <msg xml:space="preserve">one</msg>
1198 1198 </logentry>
1199 1199 </log>
1200 1200
1201 1201 $ cd ..
1202 1202
1203 1203 Incoming and outgoing:
1204 1204
1205 1205 $ hg clone -U -r31 repo repo2
1206 1206 adding changesets
1207 1207 adding manifests
1208 1208 adding file changes
1209 1209 added 31 changesets with 31 changes to 1 files
1210 1210 $ cd repo2
1211 1211
1212 1212 $ hg incoming --graph ../repo
1213 1213 comparing with ../repo
1214 1214 searching for changes
1215 1215 o changeset: 34:fea3ac5810e0
1216 1216 | tag: tip
1217 1217 | parent: 32:d06dffa21a31
1218 1218 | user: test
1219 1219 | date: Thu Jan 01 00:00:34 1970 +0000
1220 1220 | summary: (34) head
1221 1221 |
1222 1222 | o changeset: 33:68608f5145f9
1223 1223 | parent: 18:1aa84d96232a
1224 1224 | user: test
1225 1225 | date: Thu Jan 01 00:00:33 1970 +0000
1226 1226 | summary: (33) head
1227 1227 |
1228 1228 o changeset: 32:d06dffa21a31
1229 1229 | parent: 27:886ed638191b
1230 1230 | parent: 31:621d83e11f67
1231 1231 | user: test
1232 1232 | date: Thu Jan 01 00:00:32 1970 +0000
1233 1233 | summary: (32) expand
1234 1234 |
1235 1235 o changeset: 27:886ed638191b
1236 1236 parent: 21:d42a756af44d
1237 1237 user: test
1238 1238 date: Thu Jan 01 00:00:27 1970 +0000
1239 1239 summary: (27) collapse
1240 1240
1241 1241 $ cd ..
1242 1242
1243 1243 $ hg -R repo outgoing --graph repo2
1244 1244 comparing with repo2
1245 1245 searching for changes
1246 1246 @ changeset: 34:fea3ac5810e0
1247 1247 | tag: tip
1248 1248 | parent: 32:d06dffa21a31
1249 1249 | user: test
1250 1250 | date: Thu Jan 01 00:00:34 1970 +0000
1251 1251 | summary: (34) head
1252 1252 |
1253 1253 | o changeset: 33:68608f5145f9
1254 1254 | parent: 18:1aa84d96232a
1255 1255 | user: test
1256 1256 | date: Thu Jan 01 00:00:33 1970 +0000
1257 1257 | summary: (33) head
1258 1258 |
1259 1259 o changeset: 32:d06dffa21a31
1260 1260 | parent: 27:886ed638191b
1261 1261 | parent: 31:621d83e11f67
1262 1262 | user: test
1263 1263 | date: Thu Jan 01 00:00:32 1970 +0000
1264 1264 | summary: (32) expand
1265 1265 |
1266 1266 o changeset: 27:886ed638191b
1267 1267 parent: 21:d42a756af44d
1268 1268 user: test
1269 1269 date: Thu Jan 01 00:00:27 1970 +0000
1270 1270 summary: (27) collapse
1271 1271
1272 1272
1273 1273 File + limit with revs != cset revs:
1274 1274 $ cd repo
1275 1275 $ touch b
1276 1276 $ hg ci -Aqm0
1277 1277 $ hg glog -l2 a
1278 1278 o changeset: 34:fea3ac5810e0
1279 1279 | parent: 32:d06dffa21a31
1280 1280 | user: test
1281 1281 | date: Thu Jan 01 00:00:34 1970 +0000
1282 1282 | summary: (34) head
1283 1283 |
1284 1284 | o changeset: 33:68608f5145f9
1285 1285 | | parent: 18:1aa84d96232a
1286 1286 | | user: test
1287 1287 | | date: Thu Jan 01 00:00:33 1970 +0000
1288 1288 | | summary: (33) head
1289 1289 | |
1290 1290
1291 1291 File + limit + -ra:b, (b - a) < limit:
1292 1292 $ hg glog -l3000 -r32:tip a
1293 1293 o changeset: 34:fea3ac5810e0
1294 1294 | parent: 32:d06dffa21a31
1295 1295 | user: test
1296 1296 | date: Thu Jan 01 00:00:34 1970 +0000
1297 1297 | summary: (34) head
1298 1298 |
1299 1299 | o changeset: 33:68608f5145f9
1300 1300 | | parent: 18:1aa84d96232a
1301 1301 | | user: test
1302 1302 | | date: Thu Jan 01 00:00:33 1970 +0000
1303 1303 | | summary: (33) head
1304 1304 | |
1305 1305 o | changeset: 32:d06dffa21a31
1306 1306 |\ \ parent: 27:886ed638191b
1307 1307 | | | parent: 31:621d83e11f67
1308 1308 | | | user: test
1309 1309 | | | date: Thu Jan 01 00:00:32 1970 +0000
1310 1310 | | | summary: (32) expand
1311 1311 | | |
1312 1312
1313 1313 Point out a common and an uncommon unshown parent
1314 1314
1315 1315 $ hg glog -r 'rev(8) or rev(9)'
1316 1316 o changeset: 9:7010c0af0a35
1317 1317 |\ parent: 7:b632bb1b1224
1318 1318 | | parent: 8:7a0b11f71937
1319 1319 | | user: test
1320 1320 | | date: Thu Jan 01 00:00:09 1970 +0000
1321 1321 | | summary: (9) expand
1322 1322 | |
1323 1323 o | changeset: 8:7a0b11f71937
1324 1324 |\| parent: 0:e6eb3150255d
1325 1325 | | parent: 7:b632bb1b1224
1326 1326 | | user: test
1327 1327 | | date: Thu Jan 01 00:00:08 1970 +0000
1328 1328 | | summary: (8) merge two known; one immediate left, one far right
1329 1329 | |
1330 1330
1331 1331 File + limit + -ra:b, b < tip:
1332 1332
1333 1333 $ hg glog -l1 -r32:34 a
1334 1334 o changeset: 34:fea3ac5810e0
1335 1335 | parent: 32:d06dffa21a31
1336 1336 | user: test
1337 1337 | date: Thu Jan 01 00:00:34 1970 +0000
1338 1338 | summary: (34) head
1339 1339 |
1340 1340
1341 1341 file(File) + limit + -ra:b, b < tip:
1342 1342
1343 1343 $ hg glog -l1 -r32:34 -r 'file("a")'
1344 1344 o changeset: 34:fea3ac5810e0
1345 1345 | parent: 32:d06dffa21a31
1346 1346 | user: test
1347 1347 | date: Thu Jan 01 00:00:34 1970 +0000
1348 1348 | summary: (34) head
1349 1349 |
1350 1350
1351 1351 limit(file(File) and a::b), b < tip:
1352 1352
1353 1353 $ hg glog -r 'limit(file("a") and 32::34, 1)'
1354 1354 o changeset: 32:d06dffa21a31
1355 1355 |\ parent: 27:886ed638191b
1356 1356 | | parent: 31:621d83e11f67
1357 1357 | | user: test
1358 1358 | | date: Thu Jan 01 00:00:32 1970 +0000
1359 1359 | | summary: (32) expand
1360 1360 | |
1361 1361
1362 1362 File + limit + -ra:b, b < tip:
1363 1363
1364 1364 $ hg glog -r 'limit(file("a") and 34::32, 1)'
1365 1365
1366 1366 File + limit + -ra:b, b < tip, (b - a) < limit:
1367 1367
1368 1368 $ hg glog -l10 -r33:34 a
1369 1369 o changeset: 34:fea3ac5810e0
1370 1370 | parent: 32:d06dffa21a31
1371 1371 | user: test
1372 1372 | date: Thu Jan 01 00:00:34 1970 +0000
1373 1373 | summary: (34) head
1374 1374 |
1375 1375 | o changeset: 33:68608f5145f9
1376 1376 | | parent: 18:1aa84d96232a
1377 1377 | | user: test
1378 1378 | | date: Thu Jan 01 00:00:33 1970 +0000
1379 1379 | | summary: (33) head
1380 1380 | |
1381 1381
1382 1382 Do not crash or produce strange graphs if history is buggy
1383 1383
1384 1384 $ hg branch branch
1385 1385 marked working directory as branch branch
1386 1386 (branches are permanent and global, did you want a bookmark?)
1387 1387 $ commit 36 "buggy merge: identical parents" 35 35
1388 1388 $ hg glog -l5
1389 1389 @ changeset: 36:08a19a744424
1390 1390 | branch: branch
1391 1391 | tag: tip
1392 1392 | parent: 35:9159c3644c5e
1393 1393 | parent: 35:9159c3644c5e
1394 1394 | user: test
1395 1395 | date: Thu Jan 01 00:00:36 1970 +0000
1396 1396 | summary: (36) buggy merge: identical parents
1397 1397 |
1398 1398 o changeset: 35:9159c3644c5e
1399 1399 | user: test
1400 1400 | date: Thu Jan 01 00:00:00 1970 +0000
1401 1401 | summary: 0
1402 1402 |
1403 1403 o changeset: 34:fea3ac5810e0
1404 1404 | parent: 32:d06dffa21a31
1405 1405 | user: test
1406 1406 | date: Thu Jan 01 00:00:34 1970 +0000
1407 1407 | summary: (34) head
1408 1408 |
1409 1409 | o changeset: 33:68608f5145f9
1410 1410 | | parent: 18:1aa84d96232a
1411 1411 | | user: test
1412 1412 | | date: Thu Jan 01 00:00:33 1970 +0000
1413 1413 | | summary: (33) head
1414 1414 | |
1415 1415 o | changeset: 32:d06dffa21a31
1416 1416 |\ \ parent: 27:886ed638191b
1417 1417 | | | parent: 31:621d83e11f67
1418 1418 | | | user: test
1419 1419 | | | date: Thu Jan 01 00:00:32 1970 +0000
1420 1420 | | | summary: (32) expand
1421 1421 | | |
1422 1422
1423 1423 Test log -G options
1424 1424
1425 1425 $ testlog() {
1426 1426 > hg log -G --print-revset "$@"
1427 1427 > hg log --template 'nodetag {rev}\n' "$@" | grep nodetag \
1428 1428 > | sed 's/.*nodetag/nodetag/' > log.nodes
1429 1429 > hg log -G --template 'nodetag {rev}\n' "$@" | grep nodetag \
1430 1430 > | sed 's/.*nodetag/nodetag/' > glog.nodes
1431 1431 > diff -u log.nodes glog.nodes
1432 1432 > }
1433 1433
1434 1434 glog always reorders nodes which explains the difference with log
1435 1435
1436 1436 $ testlog -r 27 -r 25 -r 21 -r 34 -r 32 -r 31
1437 1437 ['27', '25', '21', '34', '32', '31']
1438 1438 []
1439 1439 --- log.nodes * (glob)
1440 1440 +++ glog.nodes * (glob)
1441 1441 @@ -1,6 +1,6 @@
1442 1442 -nodetag 27
1443 1443 -nodetag 25
1444 1444 -nodetag 21
1445 1445 nodetag 34
1446 1446 nodetag 32
1447 1447 nodetag 31
1448 1448 +nodetag 27
1449 1449 +nodetag 25
1450 1450 +nodetag 21
1451 1451 [1]
1452 1452 $ testlog -u test -u not-a-user
1453 1453 []
1454 1454 (group
1455 1455 (group
1456 1456 (or
1457 1457 (func
1458 1458 ('symbol', 'user')
1459 1459 ('string', 'test'))
1460 1460 (func
1461 1461 ('symbol', 'user')
1462 1462 ('string', 'not-a-user')))))
1463 1463 $ testlog -b not-a-branch
1464 1464 abort: unknown revision 'not-a-branch'!
1465 1465 abort: unknown revision 'not-a-branch'!
1466 1466 abort: unknown revision 'not-a-branch'!
1467 1467 $ testlog -b 35 -b 36 --only-branch branch
1468 1468 []
1469 1469 (group
1470 1470 (group
1471 1471 (or
1472 1472 (or
1473 1473 (func
1474 1474 ('symbol', 'branch')
1475 1475 ('string', 'default'))
1476 1476 (func
1477 1477 ('symbol', 'branch')
1478 1478 ('string', 'branch')))
1479 1479 (func
1480 1480 ('symbol', 'branch')
1481 1481 ('string', 'branch')))))
1482 1482 $ testlog -k expand -k merge
1483 1483 []
1484 1484 (group
1485 1485 (group
1486 1486 (or
1487 1487 (func
1488 1488 ('symbol', 'keyword')
1489 1489 ('string', 'expand'))
1490 1490 (func
1491 1491 ('symbol', 'keyword')
1492 1492 ('string', 'merge')))))
1493 1493 $ testlog --only-merges
1494 1494 []
1495 1495 (group
1496 1496 (func
1497 1497 ('symbol', 'merge')
1498 1498 None))
1499 1499 $ testlog --no-merges
1500 1500 []
1501 1501 (group
1502 1502 (not
1503 1503 (func
1504 1504 ('symbol', 'merge')
1505 1505 None)))
1506 1506 $ testlog --date '2 0 to 4 0'
1507 1507 []
1508 1508 (group
1509 1509 (func
1510 1510 ('symbol', 'date')
1511 1511 ('string', '2 0 to 4 0')))
1512 1512 $ hg log -G -d 'brace ) in a date'
1513 1513 abort: invalid date: 'brace ) in a date'
1514 1514 [255]
1515 1515 $ testlog --prune 31 --prune 32
1516 1516 []
1517 1517 (group
1518 1518 (group
1519 1519 (and
1520 1520 (not
1521 1521 (group
1522 1522 (or
1523 1523 ('string', '31')
1524 1524 (func
1525 1525 ('symbol', 'ancestors')
1526 1526 ('string', '31')))))
1527 1527 (not
1528 1528 (group
1529 1529 (or
1530 1530 ('string', '32')
1531 1531 (func
1532 1532 ('symbol', 'ancestors')
1533 1533 ('string', '32'))))))))
1534 1534
1535 1535 Dedicated repo for --follow and paths filtering. The g is crafted to
1536 1536 have 2 filelog topological heads in a linear changeset graph.
1537 1537
1538 1538 $ cd ..
1539 1539 $ hg init follow
1540 1540 $ cd follow
1541 1541 $ testlog --follow
1542 1542 []
1543 1543 []
1544 1544 $ echo a > a
1545 1545 $ echo aa > aa
1546 1546 $ echo f > f
1547 1547 $ hg ci -Am "add a" a aa f
1548 1548 $ hg cp a b
1549 1549 $ hg cp f g
1550 1550 $ hg ci -m "copy a b"
1551 1551 $ mkdir dir
1552 1552 $ hg mv b dir
1553 1553 $ echo g >> g
1554 1554 $ echo f >> f
1555 1555 $ hg ci -m "mv b dir/b"
1556 1556 $ hg mv a b
1557 1557 $ hg cp -f f g
1558 1558 $ echo a > d
1559 1559 $ hg add d
1560 1560 $ hg ci -m "mv a b; add d"
1561 1561 $ hg mv dir/b e
1562 1562 $ hg ci -m "mv dir/b e"
1563 1563 $ hg glog --template '({rev}) {desc|firstline}\n'
1564 1564 @ (4) mv dir/b e
1565 1565 |
1566 1566 o (3) mv a b; add d
1567 1567 |
1568 1568 o (2) mv b dir/b
1569 1569 |
1570 1570 o (1) copy a b
1571 1571 |
1572 1572 o (0) add a
1573 1573
1574 1574
1575 1575 $ testlog a
1576 1576 []
1577 1577 (group
1578 1578 (group
1579 1579 (func
1580 1580 ('symbol', 'filelog')
1581 1581 ('string', 'a'))))
1582 1582 $ testlog a b
1583 1583 []
1584 1584 (group
1585 1585 (group
1586 1586 (or
1587 1587 (func
1588 1588 ('symbol', 'filelog')
1589 1589 ('string', 'a'))
1590 1590 (func
1591 1591 ('symbol', 'filelog')
1592 1592 ('string', 'b')))))
1593 1593
1594 1594 Test falling back to slow path for non-existing files
1595 1595
1596 1596 $ testlog a c
1597 1597 []
1598 1598 (group
1599 1599 (func
1600 1600 ('symbol', '_matchfiles')
1601 1601 (list
1602 1602 (list
1603 ('string', 'r:')
1603 (list
1604 ('string', 'r:')
1605 ('string', 'd:relpath'))
1604 1606 ('string', 'p:a'))
1605 1607 ('string', 'p:c'))))
1606 1608
1607 1609 Test multiple --include/--exclude/paths
1608 1610
1609 1611 $ testlog --include a --include e --exclude b --exclude e a e
1610 1612 []
1611 1613 (group
1612 1614 (func
1613 1615 ('symbol', '_matchfiles')
1614 1616 (list
1615 1617 (list
1616 1618 (list
1617 1619 (list
1618 1620 (list
1619 1621 (list
1620 ('string', 'r:')
1622 (list
1623 ('string', 'r:')
1624 ('string', 'd:relpath'))
1621 1625 ('string', 'p:a'))
1622 1626 ('string', 'p:e'))
1623 1627 ('string', 'i:a'))
1624 1628 ('string', 'i:e'))
1625 1629 ('string', 'x:b'))
1626 1630 ('string', 'x:e'))))
1627 1631
1628 1632 Test glob expansion of pats
1629 1633
1630 1634 $ expandglobs=`python -c "import mercurial.util; \
1631 1635 > print mercurial.util.expandglobs and 'true' or 'false'"`
1632 1636 $ if [ $expandglobs = "true" ]; then
1633 1637 > testlog 'a*';
1634 1638 > else
1635 1639 > testlog a*;
1636 1640 > fi;
1637 1641 []
1638 1642 (group
1639 1643 (group
1640 1644 (func
1641 1645 ('symbol', 'filelog')
1642 1646 ('string', 'aa'))))
1643 1647
1644 1648 Test --follow on a directory
1645 1649
1646 1650 $ testlog -f dir
1647 1651 abort: cannot follow file not in parent revision: "dir"
1648 1652 abort: cannot follow file not in parent revision: "dir"
1649 1653 abort: cannot follow file not in parent revision: "dir"
1650 1654
1651 1655 Test --follow on file not in parent revision
1652 1656
1653 1657 $ testlog -f a
1654 1658 abort: cannot follow file not in parent revision: "a"
1655 1659 abort: cannot follow file not in parent revision: "a"
1656 1660 abort: cannot follow file not in parent revision: "a"
1657 1661
1658 1662 Test --follow and patterns
1659 1663
1660 1664 $ testlog -f 'glob:*'
1661 1665 abort: can only follow copies/renames for explicit filenames
1662 1666 abort: can only follow copies/renames for explicit filenames
1663 1667 abort: can only follow copies/renames for explicit filenames
1664 1668
1665 1669 Test --follow on a single rename
1666 1670
1667 1671 $ hg up -q 2
1668 1672 $ testlog -f a
1669 1673 []
1670 1674 (group
1671 1675 (group
1672 1676 (func
1673 1677 ('symbol', 'follow')
1674 1678 ('string', 'a'))))
1675 1679
1676 1680 Test --follow and multiple renames
1677 1681
1678 1682 $ hg up -q tip
1679 1683 $ testlog -f e
1680 1684 []
1681 1685 (group
1682 1686 (group
1683 1687 (func
1684 1688 ('symbol', 'follow')
1685 1689 ('string', 'e'))))
1686 1690
1687 1691 Test --follow and multiple filelog heads
1688 1692
1689 1693 $ hg up -q 2
1690 1694 $ testlog -f g
1691 1695 []
1692 1696 (group
1693 1697 (group
1694 1698 (func
1695 1699 ('symbol', 'follow')
1696 1700 ('string', 'g'))))
1697 1701 $ cat log.nodes
1698 1702 nodetag 2
1699 1703 nodetag 1
1700 1704 nodetag 0
1701 1705 $ hg up -q tip
1702 1706 $ testlog -f g
1703 1707 []
1704 1708 (group
1705 1709 (group
1706 1710 (func
1707 1711 ('symbol', 'follow')
1708 1712 ('string', 'g'))))
1709 1713 $ cat log.nodes
1710 1714 nodetag 3
1711 1715 nodetag 2
1712 1716 nodetag 0
1713 1717
1714 1718 Test --follow and multiple files
1715 1719
1716 1720 $ testlog -f g e
1717 1721 []
1718 1722 (group
1719 1723 (group
1720 1724 (or
1721 1725 (func
1722 1726 ('symbol', 'follow')
1723 1727 ('string', 'g'))
1724 1728 (func
1725 1729 ('symbol', 'follow')
1726 1730 ('string', 'e')))))
1727 1731 $ cat log.nodes
1728 1732 nodetag 4
1729 1733 nodetag 3
1730 1734 nodetag 2
1731 1735 nodetag 1
1732 1736 nodetag 0
1733 1737
1734 1738 Test --follow-first
1735 1739
1736 1740 $ hg up -q 3
1737 1741 $ echo ee > e
1738 1742 $ hg ci -Am "add another e" e
1739 1743 created new head
1740 1744 $ hg merge --tool internal:other 4
1741 1745 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
1742 1746 (branch merge, don't forget to commit)
1743 1747 $ echo merge > e
1744 1748 $ hg ci -m "merge 5 and 4"
1745 1749 $ testlog --follow-first
1746 1750 []
1747 1751 (group
1748 1752 (func
1749 1753 ('symbol', '_firstancestors')
1750 1754 ('symbol', '6')))
1751 1755
1752 1756 Cannot compare with log --follow-first FILE as it never worked
1753 1757
1754 1758 $ hg log -G --print-revset --follow-first e
1755 1759 []
1756 1760 (group
1757 1761 (group
1758 1762 (func
1759 1763 ('symbol', '_followfirst')
1760 1764 ('string', 'e'))))
1761 1765 $ hg log -G --follow-first e --template '{rev} {desc|firstline}\n'
1762 1766 @ 6 merge 5 and 4
1763 1767 |\
1764 1768 o | 5 add another e
1765 1769 | |
1766 1770
1767 1771 Test --copies
1768 1772
1769 1773 $ hg log -G --copies --template "{rev} {desc|firstline} \
1770 1774 > copies: {file_copies_switch}\n"
1771 1775 @ 6 merge 5 and 4 copies:
1772 1776 |\
1773 1777 | o 5 add another e copies:
1774 1778 | |
1775 1779 o | 4 mv dir/b e copies: e (dir/b)
1776 1780 |/
1777 1781 o 3 mv a b; add d copies: b (a)g (f)
1778 1782 |
1779 1783 o 2 mv b dir/b copies: dir/b (b)
1780 1784 |
1781 1785 o 1 copy a b copies: b (a)g (f)
1782 1786 |
1783 1787 o 0 add a copies:
1784 1788
1785 1789 Test "set:..." and parent revision
1786 1790
1787 1791 $ hg up -q 4
1788 1792 $ testlog "set:copied()"
1789 1793 []
1790 1794 (group
1791 1795 (func
1792 1796 ('symbol', '_matchfiles')
1793 1797 (list
1794 ('string', 'r:')
1798 (list
1799 ('string', 'r:')
1800 ('string', 'd:relpath'))
1795 1801 ('string', 'p:set:copied()'))))
1796 1802 $ testlog --include "set:copied()"
1797 1803 []
1798 1804 (group
1799 1805 (func
1800 1806 ('symbol', '_matchfiles')
1801 1807 (list
1802 ('string', 'r:')
1808 (list
1809 ('string', 'r:')
1810 ('string', 'd:relpath'))
1803 1811 ('string', 'i:set:copied()'))))
1804 1812 $ testlog -r "sort(file('set:copied()'), -rev)"
1805 1813 ["sort(file('set:copied()'), -rev)"]
1806 1814 []
1807 1815
1808 1816 Test --removed
1809 1817
1810 1818 $ testlog --removed
1811 1819 []
1812 1820 []
1813 1821 $ testlog --removed a
1814 1822 []
1815 1823 (group
1816 1824 (func
1817 1825 ('symbol', '_matchfiles')
1818 1826 (list
1819 ('string', 'r:')
1827 (list
1828 ('string', 'r:')
1829 ('string', 'd:relpath'))
1820 1830 ('string', 'p:a'))))
1821 1831 $ testlog --removed --follow a
1822 1832 abort: can only follow copies/renames for explicit filenames
1823 1833 abort: can only follow copies/renames for explicit filenames
1824 1834 abort: can only follow copies/renames for explicit filenames
1825 1835
1826 1836 Test --patch and --stat with --follow and --follow-first
1827 1837
1828 1838 $ hg up -q 3
1829 1839 $ hg log -G --git --patch b
1830 1840 o changeset: 1:216d4c92cf98
1831 1841 | user: test
1832 1842 | date: Thu Jan 01 00:00:00 1970 +0000
1833 1843 | summary: copy a b
1834 1844 |
1835 1845 | diff --git a/a b/b
1836 1846 | copy from a
1837 1847 | copy to b
1838 1848 |
1839 1849
1840 1850 $ hg log -G --git --stat b
1841 1851 o changeset: 1:216d4c92cf98
1842 1852 | user: test
1843 1853 | date: Thu Jan 01 00:00:00 1970 +0000
1844 1854 | summary: copy a b
1845 1855 |
1846 1856 | a | 0
1847 1857 | 1 files changed, 0 insertions(+), 0 deletions(-)
1848 1858 |
1849 1859
1850 1860 $ hg log -G --git --patch --follow b
1851 1861 o changeset: 1:216d4c92cf98
1852 1862 | user: test
1853 1863 | date: Thu Jan 01 00:00:00 1970 +0000
1854 1864 | summary: copy a b
1855 1865 |
1856 1866 | diff --git a/a b/b
1857 1867 | copy from a
1858 1868 | copy to b
1859 1869 |
1860 1870 o changeset: 0:f8035bb17114
1861 1871 user: test
1862 1872 date: Thu Jan 01 00:00:00 1970 +0000
1863 1873 summary: add a
1864 1874
1865 1875 diff --git a/a b/a
1866 1876 new file mode 100644
1867 1877 --- /dev/null
1868 1878 +++ b/a
1869 1879 @@ -0,0 +1,1 @@
1870 1880 +a
1871 1881
1872 1882
1873 1883 $ hg log -G --git --stat --follow b
1874 1884 o changeset: 1:216d4c92cf98
1875 1885 | user: test
1876 1886 | date: Thu Jan 01 00:00:00 1970 +0000
1877 1887 | summary: copy a b
1878 1888 |
1879 1889 | a | 0
1880 1890 | 1 files changed, 0 insertions(+), 0 deletions(-)
1881 1891 |
1882 1892 o changeset: 0:f8035bb17114
1883 1893 user: test
1884 1894 date: Thu Jan 01 00:00:00 1970 +0000
1885 1895 summary: add a
1886 1896
1887 1897 a | 1 +
1888 1898 1 files changed, 1 insertions(+), 0 deletions(-)
1889 1899
1890 1900
1891 1901 $ hg up -q 6
1892 1902 $ hg log -G --git --patch --follow-first e
1893 1903 @ changeset: 6:fc281d8ff18d
1894 1904 |\ tag: tip
1895 1905 | | parent: 5:99b31f1c2782
1896 1906 | | parent: 4:17d952250a9d
1897 1907 | | user: test
1898 1908 | | date: Thu Jan 01 00:00:00 1970 +0000
1899 1909 | | summary: merge 5 and 4
1900 1910 | |
1901 1911 | | diff --git a/e b/e
1902 1912 | | --- a/e
1903 1913 | | +++ b/e
1904 1914 | | @@ -1,1 +1,1 @@
1905 1915 | | -ee
1906 1916 | | +merge
1907 1917 | |
1908 1918 o | changeset: 5:99b31f1c2782
1909 1919 | | parent: 3:5918b8d165d1
1910 1920 | | user: test
1911 1921 | | date: Thu Jan 01 00:00:00 1970 +0000
1912 1922 | | summary: add another e
1913 1923 | |
1914 1924 | | diff --git a/e b/e
1915 1925 | | new file mode 100644
1916 1926 | | --- /dev/null
1917 1927 | | +++ b/e
1918 1928 | | @@ -0,0 +1,1 @@
1919 1929 | | +ee
1920 1930 | |
1921 1931
1922 1932 Test old-style --rev
1923 1933
1924 1934 $ hg tag 'foo-bar'
1925 1935 $ testlog -r 'foo-bar'
1926 1936 ['foo-bar']
1927 1937 []
1928 1938
1929 1939 Test --follow and forward --rev
1930 1940
1931 1941 $ hg up -q 6
1932 1942 $ echo g > g
1933 1943 $ hg ci -Am 'add g' g
1934 1944 created new head
1935 1945 $ hg up -q 2
1936 1946 $ hg log -G --template "{rev} {desc|firstline}\n"
1937 1947 o 8 add g
1938 1948 |
1939 1949 | o 7 Added tag foo-bar for changeset fc281d8ff18d
1940 1950 |/
1941 1951 o 6 merge 5 and 4
1942 1952 |\
1943 1953 | o 5 add another e
1944 1954 | |
1945 1955 o | 4 mv dir/b e
1946 1956 |/
1947 1957 o 3 mv a b; add d
1948 1958 |
1949 1959 @ 2 mv b dir/b
1950 1960 |
1951 1961 o 1 copy a b
1952 1962 |
1953 1963 o 0 add a
1954 1964
1955 1965 $ testlog --follow -r6 -r8 -r5 -r7 -r4
1956 1966 ['6', '8', '5', '7', '4']
1957 1967 (group
1958 1968 (func
1959 1969 ('symbol', 'descendants')
1960 1970 ('symbol', '6')))
1961 1971 --- log.nodes * (glob)
1962 1972 +++ glog.nodes * (glob)
1963 1973 @@ -1,3 +1,3 @@
1964 1974 -nodetag 6
1965 1975 nodetag 8
1966 1976 nodetag 7
1967 1977 +nodetag 6
1968 1978 [1]
1969 1979
1970 1980 Test --follow-first and forward --rev
1971 1981
1972 1982 $ testlog --follow-first -r6 -r8 -r5 -r7 -r4
1973 1983 ['6', '8', '5', '7', '4']
1974 1984 (group
1975 1985 (func
1976 1986 ('symbol', '_firstdescendants')
1977 1987 ('symbol', '6')))
1978 1988 --- log.nodes * (glob)
1979 1989 +++ glog.nodes * (glob)
1980 1990 @@ -1,3 +1,3 @@
1981 1991 -nodetag 6
1982 1992 nodetag 8
1983 1993 nodetag 7
1984 1994 +nodetag 6
1985 1995 [1]
1986 1996
1987 1997 Test --follow and backward --rev
1988 1998
1989 1999 $ testlog --follow -r6 -r5 -r7 -r8 -r4
1990 2000 ['6', '5', '7', '8', '4']
1991 2001 (group
1992 2002 (func
1993 2003 ('symbol', 'ancestors')
1994 2004 ('symbol', '6')))
1995 2005
1996 2006 Test --follow-first and backward --rev
1997 2007
1998 2008 $ testlog --follow-first -r6 -r5 -r7 -r8 -r4
1999 2009 ['6', '5', '7', '8', '4']
2000 2010 (group
2001 2011 (func
2002 2012 ('symbol', '_firstancestors')
2003 2013 ('symbol', '6')))
2014
2015 Test subdir
2016
2017 $ hg up -q 3
2018 $ cd dir
2019 $ testlog .
2020 []
2021 (group
2022 (func
2023 ('symbol', '_matchfiles')
2024 (list
2025 (list
2026 ('string', 'r:')
2027 ('string', 'd:relpath'))
2028 ('string', 'p:.'))))
@@ -1,709 +1,709 b''
1 1 $ "$TESTDIR/hghave" no-msys || exit 80 # MSYS will translate /a/b/c/ as if it was a real file path
2 2
3 3 $ HGENCODING=utf-8
4 4 $ export HGENCODING
5 5
6 6 $ try() {
7 7 > hg debugrevspec --debug "$@"
8 8 > }
9 9
10 10 $ log() {
11 11 > hg log --template '{rev}\n' -r "$1"
12 12 > }
13 13
14 14 $ hg init repo
15 15 $ cd repo
16 16
17 17 $ echo a > a
18 18 $ hg branch a
19 19 marked working directory as branch a
20 20 (branches are permanent and global, did you want a bookmark?)
21 21 $ hg ci -Aqm0
22 22
23 23 $ echo b > b
24 24 $ hg branch b
25 25 marked working directory as branch b
26 26 (branches are permanent and global, did you want a bookmark?)
27 27 $ hg ci -Aqm1
28 28
29 29 $ rm a
30 30 $ hg branch a-b-c-
31 31 marked working directory as branch a-b-c-
32 32 (branches are permanent and global, did you want a bookmark?)
33 33 $ hg ci -Aqm2 -u Bob
34 34
35 35 $ hg co 1
36 36 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
37 37 $ hg branch +a+b+c+
38 38 marked working directory as branch +a+b+c+
39 39 (branches are permanent and global, did you want a bookmark?)
40 40 $ hg ci -Aqm3
41 41
42 42 $ hg co 2 # interleave
43 43 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
44 44 $ echo bb > b
45 45 $ hg branch -- -a-b-c-
46 46 marked working directory as branch -a-b-c-
47 47 (branches are permanent and global, did you want a bookmark?)
48 48 $ hg ci -Aqm4 -d "May 12 2005"
49 49
50 50 $ hg co 3
51 51 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
52 52 $ hg branch /a/b/c/
53 53 marked working directory as branch /a/b/c/
54 54 (branches are permanent and global, did you want a bookmark?)
55 55 $ hg ci -Aqm"5 bug"
56 56
57 57 $ hg merge 4
58 58 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
59 59 (branch merge, don't forget to commit)
60 60 $ hg branch _a_b_c_
61 61 marked working directory as branch _a_b_c_
62 62 (branches are permanent and global, did you want a bookmark?)
63 63 $ hg ci -Aqm"6 issue619"
64 64
65 65 $ hg branch .a.b.c.
66 66 marked working directory as branch .a.b.c.
67 67 (branches are permanent and global, did you want a bookmark?)
68 68 $ hg ci -Aqm7
69 69
70 70 $ hg branch all
71 71 marked working directory as branch all
72 72 (branches are permanent and global, did you want a bookmark?)
73 73 $ hg ci --close-branch -Aqm8
74 74 abort: can only close branch heads
75 75 [255]
76 76
77 77 $ hg co 4
78 78 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
79 79 $ hg branch Γ©
80 80 marked working directory as branch \xc3\xa9 (esc)
81 81 (branches are permanent and global, did you want a bookmark?)
82 82 $ hg ci -Aqm9
83 83
84 84 $ hg tag -r6 1.0
85 85
86 86 $ hg clone --quiet -U -r 7 . ../remote1
87 87 $ hg clone --quiet -U -r 8 . ../remote2
88 88 $ echo "[paths]" >> .hg/hgrc
89 89 $ echo "default = ../remote1" >> .hg/hgrc
90 90
91 91 names that should work without quoting
92 92
93 93 $ try a
94 94 ('symbol', 'a')
95 95 0
96 96 $ try b-a
97 97 (minus
98 98 ('symbol', 'b')
99 99 ('symbol', 'a'))
100 100 1
101 101 $ try _a_b_c_
102 102 ('symbol', '_a_b_c_')
103 103 6
104 104 $ try _a_b_c_-a
105 105 (minus
106 106 ('symbol', '_a_b_c_')
107 107 ('symbol', 'a'))
108 108 6
109 109 $ try .a.b.c.
110 110 ('symbol', '.a.b.c.')
111 111 7
112 112 $ try .a.b.c.-a
113 113 (minus
114 114 ('symbol', '.a.b.c.')
115 115 ('symbol', 'a'))
116 116 7
117 117 $ try -- '-a-b-c-' # complains
118 118 hg: parse error at 7: not a prefix: end
119 119 [255]
120 120 $ log -a-b-c- # succeeds with fallback
121 121 4
122 122 $ try -- -a-b-c--a # complains
123 123 (minus
124 124 (minus
125 125 (minus
126 126 (negate
127 127 ('symbol', 'a'))
128 128 ('symbol', 'b'))
129 129 ('symbol', 'c'))
130 130 (negate
131 131 ('symbol', 'a')))
132 132 abort: unknown revision '-a'!
133 133 [255]
134 134 $ try Γ©
135 135 ('symbol', '\xc3\xa9')
136 136 9
137 137
138 138 quoting needed
139 139
140 140 $ try '"-a-b-c-"-a'
141 141 (minus
142 142 ('string', '-a-b-c-')
143 143 ('symbol', 'a'))
144 144 4
145 145
146 146 $ log '1 or 2'
147 147 1
148 148 2
149 149 $ log '1|2'
150 150 1
151 151 2
152 152 $ log '1 and 2'
153 153 $ log '1&2'
154 154 $ try '1&2|3' # precedence - and is higher
155 155 (or
156 156 (and
157 157 ('symbol', '1')
158 158 ('symbol', '2'))
159 159 ('symbol', '3'))
160 160 3
161 161 $ try '1|2&3'
162 162 (or
163 163 ('symbol', '1')
164 164 (and
165 165 ('symbol', '2')
166 166 ('symbol', '3')))
167 167 1
168 168 $ try '1&2&3' # associativity
169 169 (and
170 170 (and
171 171 ('symbol', '1')
172 172 ('symbol', '2'))
173 173 ('symbol', '3'))
174 174 $ try '1|(2|3)'
175 175 (or
176 176 ('symbol', '1')
177 177 (group
178 178 (or
179 179 ('symbol', '2')
180 180 ('symbol', '3'))))
181 181 1
182 182 2
183 183 3
184 184 $ log '1.0' # tag
185 185 6
186 186 $ log 'a' # branch
187 187 0
188 188 $ log '2785f51ee'
189 189 0
190 190 $ log 'date(2005)'
191 191 4
192 192 $ log 'date(this is a test)'
193 193 hg: parse error at 10: unexpected token: symbol
194 194 [255]
195 195 $ log 'date()'
196 196 hg: parse error: date requires a string
197 197 [255]
198 198 $ log 'date'
199 199 hg: parse error: can't use date here
200 200 [255]
201 201 $ log 'date('
202 202 hg: parse error at 5: not a prefix: end
203 203 [255]
204 204 $ log 'date(tip)'
205 205 abort: invalid date: 'tip'
206 206 [255]
207 207 $ log '"date"'
208 208 abort: unknown revision 'date'!
209 209 [255]
210 210 $ log 'date(2005) and 1::'
211 211 4
212 212
213 213 $ log 'ancestor(1)'
214 214 hg: parse error: ancestor requires two arguments
215 215 [255]
216 216 $ log 'ancestor(4,5)'
217 217 1
218 218 $ log 'ancestor(4,5) and 4'
219 219 $ log 'ancestors(5)'
220 220 0
221 221 1
222 222 3
223 223 5
224 224 $ log 'author(bob)'
225 225 2
226 226 $ log 'branch(Γ©)'
227 227 8
228 228 9
229 229 $ log 'children(ancestor(4,5))'
230 230 2
231 231 3
232 232 $ log 'closed()'
233 233 $ log 'contains(a)'
234 234 0
235 235 1
236 236 3
237 237 5
238 238 $ log 'desc(B)'
239 239 5
240 240 $ log 'descendants(2 or 3)'
241 241 2
242 242 3
243 243 4
244 244 5
245 245 6
246 246 7
247 247 8
248 248 9
249 $ log 'file(b)'
249 $ log 'file("b*")'
250 250 1
251 251 4
252 252 $ log 'follow()'
253 253 0
254 254 1
255 255 2
256 256 4
257 257 8
258 258 9
259 259 $ log 'grep("issue\d+")'
260 260 6
261 261 $ try 'grep("(")' # invalid regular expression
262 262 (func
263 263 ('symbol', 'grep')
264 264 ('string', '('))
265 265 hg: parse error: invalid match pattern: unbalanced parenthesis
266 266 [255]
267 267 $ try 'grep("\bissue\d+")'
268 268 (func
269 269 ('symbol', 'grep')
270 270 ('string', '\x08issue\\d+'))
271 271 $ try 'grep(r"\bissue\d+")'
272 272 (func
273 273 ('symbol', 'grep')
274 274 ('string', '\\bissue\\d+'))
275 275 6
276 276 $ try 'grep(r"\")'
277 277 hg: parse error at 7: unterminated string
278 278 [255]
279 279 $ log 'head()'
280 280 0
281 281 1
282 282 2
283 283 3
284 284 4
285 285 5
286 286 6
287 287 7
288 288 9
289 289 $ log 'heads(6::)'
290 290 7
291 291 $ log 'keyword(issue)'
292 292 6
293 293 $ log 'limit(head(), 1)'
294 294 0
295 295 $ log 'max(contains(a))'
296 296 5
297 297 $ log 'min(contains(a))'
298 298 0
299 299 $ log 'merge()'
300 300 6
301 301 $ log 'modifies(b)'
302 302 4
303 303 $ log 'id(5)'
304 304 2
305 305 $ log 'outgoing()'
306 306 8
307 307 9
308 308 $ log 'outgoing("../remote1")'
309 309 8
310 310 9
311 311 $ log 'outgoing("../remote2")'
312 312 3
313 313 5
314 314 6
315 315 7
316 316 9
317 317 $ log 'p1(merge())'
318 318 5
319 319 $ log 'p2(merge())'
320 320 4
321 321 $ log 'parents(merge())'
322 322 4
323 323 5
324 324 $ log 'removes(a)'
325 325 2
326 326 6
327 327 $ log 'roots(all())'
328 328 0
329 329 $ log 'reverse(2 or 3 or 4 or 5)'
330 330 5
331 331 4
332 332 3
333 333 2
334 334 $ log 'rev(5)'
335 335 5
336 336 $ log 'sort(limit(reverse(all()), 3))'
337 337 7
338 338 8
339 339 9
340 340 $ log 'sort(2 or 3 or 4 or 5, date)'
341 341 2
342 342 3
343 343 5
344 344 4
345 345 $ log 'tagged()'
346 346 6
347 347 $ log 'tag()'
348 348 6
349 349 $ log 'tag(1.0)'
350 350 6
351 351 $ log 'tag(tip)'
352 352 9
353 353 $ log 'tag(unknown)'
354 354 abort: tag 'unknown' does not exist
355 355 [255]
356 356 $ log 'branch(unknown)'
357 357 abort: unknown revision 'unknown'!
358 358 [255]
359 359 $ log 'user(bob)'
360 360 2
361 361
362 362 $ log '4::8'
363 363 4
364 364 8
365 365 $ log '4:8'
366 366 4
367 367 5
368 368 6
369 369 7
370 370 8
371 371
372 372 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
373 373 4
374 374 2
375 375 5
376 376
377 377 $ log 'not 0 and 0:2'
378 378 1
379 379 2
380 380 $ log 'not 1 and 0:2'
381 381 0
382 382 2
383 383 $ log 'not 2 and 0:2'
384 384 0
385 385 1
386 386 $ log '(1 and 2)::'
387 387 $ log '(1 and 2):'
388 388 $ log '(1 and 2):3'
389 389 $ log 'sort(head(), -rev)'
390 390 9
391 391 7
392 392 6
393 393 5
394 394 4
395 395 3
396 396 2
397 397 1
398 398 0
399 399 $ log '4::8 - 8'
400 400 4
401 401
402 402 issue2437
403 403
404 404 $ log '3 and p1(5)'
405 405 3
406 406 $ log '4 and p2(6)'
407 407 4
408 408 $ log '1 and parents(:2)'
409 409 1
410 410 $ log '2 and children(1:)'
411 411 2
412 412 $ log 'roots(all()) or roots(all())'
413 413 0
414 414 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
415 415 9
416 416 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
417 417 4
418 418
419 419 issue2654: report a parse error if the revset was not completely parsed
420 420
421 421 $ log '1 OR 2'
422 422 hg: parse error at 2: invalid token
423 423 [255]
424 424
425 425 or operator should preserve ordering:
426 426 $ log 'reverse(2::4) or tip'
427 427 4
428 428 2
429 429 9
430 430
431 431 parentrevspec
432 432
433 433 $ log 'merge()^0'
434 434 6
435 435 $ log 'merge()^'
436 436 5
437 437 $ log 'merge()^1'
438 438 5
439 439 $ log 'merge()^2'
440 440 4
441 441 $ log 'merge()^^'
442 442 3
443 443 $ log 'merge()^1^'
444 444 3
445 445 $ log 'merge()^^^'
446 446 1
447 447
448 448 $ log 'merge()~0'
449 449 6
450 450 $ log 'merge()~1'
451 451 5
452 452 $ log 'merge()~2'
453 453 3
454 454 $ log 'merge()~2^1'
455 455 1
456 456 $ log 'merge()~3'
457 457 1
458 458
459 459 $ log '(-3:tip)^'
460 460 4
461 461 6
462 462 8
463 463
464 464 $ log 'tip^foo'
465 465 hg: parse error: ^ expects a number 0, 1, or 2
466 466 [255]
467 467
468 468 aliases:
469 469
470 470 $ echo '[revsetalias]' >> .hg/hgrc
471 471 $ echo 'm = merge()' >> .hg/hgrc
472 472 $ echo 'sincem = descendants(m)' >> .hg/hgrc
473 473 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
474 474 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
475 475 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
476 476
477 477 $ try m
478 478 ('symbol', 'm')
479 479 (func
480 480 ('symbol', 'merge')
481 481 None)
482 482 6
483 483
484 484 test alias recursion
485 485
486 486 $ try sincem
487 487 ('symbol', 'sincem')
488 488 (func
489 489 ('symbol', 'descendants')
490 490 (func
491 491 ('symbol', 'merge')
492 492 None))
493 493 6
494 494 7
495 495
496 496 test infinite recursion
497 497
498 498 $ echo 'recurse1 = recurse2' >> .hg/hgrc
499 499 $ echo 'recurse2 = recurse1' >> .hg/hgrc
500 500 $ try recurse1
501 501 ('symbol', 'recurse1')
502 502 hg: parse error: infinite expansion of revset alias "recurse1" detected
503 503 [255]
504 504
505 505 test nesting and variable passing
506 506
507 507 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
508 508 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
509 509 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
510 510 $ try 'nested(2:5)'
511 511 (func
512 512 ('symbol', 'nested')
513 513 (range
514 514 ('symbol', '2')
515 515 ('symbol', '5')))
516 516 (func
517 517 ('symbol', 'max')
518 518 (range
519 519 ('symbol', '2')
520 520 ('symbol', '5')))
521 521 5
522 522
523 523 test variable isolation, variable placeholders are rewritten as string
524 524 then parsed and matched again as string. Check they do not leak too
525 525 far away.
526 526
527 527 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
528 528 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
529 529 $ try 'callinjection(2:5)'
530 530 (func
531 531 ('symbol', 'callinjection')
532 532 (range
533 533 ('symbol', '2')
534 534 ('symbol', '5')))
535 535 (func
536 536 ('symbol', 'descendants')
537 537 (func
538 538 ('symbol', 'max')
539 539 ('string', '$1')))
540 540 abort: unknown revision '$1'!
541 541 [255]
542 542
543 543 $ try 'd(2:5)'
544 544 (func
545 545 ('symbol', 'd')
546 546 (range
547 547 ('symbol', '2')
548 548 ('symbol', '5')))
549 549 (func
550 550 ('symbol', 'reverse')
551 551 (func
552 552 ('symbol', 'sort')
553 553 (list
554 554 (range
555 555 ('symbol', '2')
556 556 ('symbol', '5'))
557 557 ('symbol', 'date'))))
558 558 4
559 559 5
560 560 3
561 561 2
562 562 $ try 'rs(2 or 3, date)'
563 563 (func
564 564 ('symbol', 'rs')
565 565 (list
566 566 (or
567 567 ('symbol', '2')
568 568 ('symbol', '3'))
569 569 ('symbol', 'date')))
570 570 (func
571 571 ('symbol', 'reverse')
572 572 (func
573 573 ('symbol', 'sort')
574 574 (list
575 575 (or
576 576 ('symbol', '2')
577 577 ('symbol', '3'))
578 578 ('symbol', 'date'))))
579 579 3
580 580 2
581 581 $ try 'rs()'
582 582 (func
583 583 ('symbol', 'rs')
584 584 None)
585 585 hg: parse error: invalid number of arguments: 0
586 586 [255]
587 587 $ try 'rs(2)'
588 588 (func
589 589 ('symbol', 'rs')
590 590 ('symbol', '2'))
591 591 hg: parse error: invalid number of arguments: 1
592 592 [255]
593 593 $ try 'rs(2, data, 7)'
594 594 (func
595 595 ('symbol', 'rs')
596 596 (list
597 597 (list
598 598 ('symbol', '2')
599 599 ('symbol', 'data'))
600 600 ('symbol', '7')))
601 601 hg: parse error: invalid number of arguments: 3
602 602 [255]
603 603 $ try 'rs4(2 or 3, x, x, date)'
604 604 (func
605 605 ('symbol', 'rs4')
606 606 (list
607 607 (list
608 608 (list
609 609 (or
610 610 ('symbol', '2')
611 611 ('symbol', '3'))
612 612 ('symbol', 'x'))
613 613 ('symbol', 'x'))
614 614 ('symbol', 'date')))
615 615 (func
616 616 ('symbol', 'reverse')
617 617 (func
618 618 ('symbol', 'sort')
619 619 (list
620 620 (or
621 621 ('symbol', '2')
622 622 ('symbol', '3'))
623 623 ('symbol', 'date'))))
624 624 3
625 625 2
626 626
627 627 issue2549 - correct optimizations
628 628
629 629 $ log 'limit(1 or 2 or 3, 2) and not 2'
630 630 1
631 631 $ log 'max(1 or 2) and not 2'
632 632 $ log 'min(1 or 2) and not 1'
633 633 $ log 'last(1 or 2, 1) and not 2'
634 634
635 635 tests for 'remote()' predicate:
636 636 #. (csets in remote) (id) (remote)
637 637 1. less than local current branch "default"
638 638 2. same with local specified "default"
639 639 3. more than local specified specified
640 640
641 641 $ hg clone --quiet -U . ../remote3
642 642 $ cd ../remote3
643 643 $ hg update -q 7
644 644 $ echo r > r
645 645 $ hg ci -Aqm 10
646 646 $ log 'remote()'
647 647 7
648 648 $ log 'remote("a-b-c-")'
649 649 2
650 650 $ cd ../repo
651 651 $ log 'remote(".a.b.c.", "../remote3")'
652 652
653 653 $ cd ..
654 654
655 655 test author/desc/keyword in problematic encoding
656 656 # unicode: cp932:
657 657 # u30A2 0x83 0x41(= 'A')
658 658 # u30C2 0x83 0x61(= 'a')
659 659
660 660 $ hg init problematicencoding
661 661 $ cd problematicencoding
662 662
663 663 $ python > setup.sh <<EOF
664 664 > print u'''
665 665 > echo a > text
666 666 > hg add text
667 667 > hg --encoding utf-8 commit -u '\u30A2' -m none
668 668 > echo b > text
669 669 > hg --encoding utf-8 commit -u '\u30C2' -m none
670 670 > echo c > text
671 671 > hg --encoding utf-8 commit -u none -m '\u30A2'
672 672 > echo d > text
673 673 > hg --encoding utf-8 commit -u none -m '\u30C2'
674 674 > '''.encode('utf-8')
675 675 > EOF
676 676 $ sh < setup.sh
677 677
678 678 test in problematic encoding
679 679 $ python > test.sh <<EOF
680 680 > print u'''
681 681 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
682 682 > echo ====
683 683 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
684 684 > echo ====
685 685 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
686 686 > echo ====
687 687 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
688 688 > echo ====
689 689 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
690 690 > echo ====
691 691 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
692 692 > '''.encode('cp932')
693 693 > EOF
694 694 $ sh < test.sh
695 695 0
696 696 ====
697 697 1
698 698 ====
699 699 2
700 700 ====
701 701 3
702 702 ====
703 703 0
704 704 2
705 705 ====
706 706 1
707 707 3
708 708
709 709 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now