##// END OF EJS Templates
revsets: generate predicate help dynamically
Patrick Mezard -
r12821:165079e5 stable
parent child Browse files
Show More
@@ -1,4489 +1,4491 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from node import hex, nullid, nullrev, short
9 9 from lock import release
10 10 from i18n import _, gettext
11 11 import os, re, sys, difflib, time, tempfile
12 12 import hg, util, revlog, extensions, copies, error
13 13 import patch, help, mdiff, url, encoding, templatekw, discovery
14 14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
15 15 import merge as mergemod
16 16 import minirst, revset
17 17 import dagparser
18 18
19 19 # Commands start here, listed alphabetically
20 20
21 21 def add(ui, repo, *pats, **opts):
22 22 """add the specified files on the next commit
23 23
24 24 Schedule files to be version controlled and added to the
25 25 repository.
26 26
27 27 The files will be added to the repository at the next commit. To
28 28 undo an add before that, see :hg:`forget`.
29 29
30 30 If no names are given, add all files to the repository.
31 31
32 32 .. container:: verbose
33 33
34 34 An example showing how new (unknown) files are added
35 35 automatically by :hg:`add`::
36 36
37 37 $ ls
38 38 foo.c
39 39 $ hg status
40 40 ? foo.c
41 41 $ hg add
42 42 adding foo.c
43 43 $ hg status
44 44 A foo.c
45 45
46 46 Returns 0 if all files are successfully added.
47 47 """
48 48
49 49 m = cmdutil.match(repo, pats, opts)
50 50 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
51 51 opts.get('subrepos'), prefix="")
52 52 return rejected and 1 or 0
53 53
54 54 def addremove(ui, repo, *pats, **opts):
55 55 """add all new files, delete all missing files
56 56
57 57 Add all new files and remove all missing files from the
58 58 repository.
59 59
60 60 New files are ignored if they match any of the patterns in
61 61 .hgignore. As with add, these changes take effect at the next
62 62 commit.
63 63
64 64 Use the -s/--similarity option to detect renamed files. With a
65 65 parameter greater than 0, this compares every removed file with
66 66 every added file and records those similar enough as renames. This
67 67 option takes a percentage between 0 (disabled) and 100 (files must
68 68 be identical) as its parameter. Detecting renamed files this way
69 69 can be expensive. After using this option, :hg:`status -C` can be
70 70 used to check which files were identified as moved or renamed.
71 71
72 72 Returns 0 if all files are successfully added.
73 73 """
74 74 try:
75 75 sim = float(opts.get('similarity') or 100)
76 76 except ValueError:
77 77 raise util.Abort(_('similarity must be a number'))
78 78 if sim < 0 or sim > 100:
79 79 raise util.Abort(_('similarity must be between 0 and 100'))
80 80 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
81 81
82 82 def annotate(ui, repo, *pats, **opts):
83 83 """show changeset information by line for each file
84 84
85 85 List changes in files, showing the revision id responsible for
86 86 each line
87 87
88 88 This command is useful for discovering when a change was made and
89 89 by whom.
90 90
91 91 Without the -a/--text option, annotate will avoid processing files
92 92 it detects as binary. With -a, annotate will annotate the file
93 93 anyway, although the results will probably be neither useful
94 94 nor desirable.
95 95
96 96 Returns 0 on success.
97 97 """
98 98 if opts.get('follow'):
99 99 # --follow is deprecated and now just an alias for -f/--file
100 100 # to mimic the behavior of Mercurial before version 1.5
101 101 opts['file'] = 1
102 102
103 103 datefunc = ui.quiet and util.shortdate or util.datestr
104 104 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
105 105
106 106 if not pats:
107 107 raise util.Abort(_('at least one filename or pattern is required'))
108 108
109 109 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
110 110 ('number', lambda x: str(x[0].rev())),
111 111 ('changeset', lambda x: short(x[0].node())),
112 112 ('date', getdate),
113 113 ('file', lambda x: x[0].path()),
114 114 ]
115 115
116 116 if (not opts.get('user') and not opts.get('changeset')
117 117 and not opts.get('date') and not opts.get('file')):
118 118 opts['number'] = 1
119 119
120 120 linenumber = opts.get('line_number') is not None
121 121 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
122 122 raise util.Abort(_('at least one of -n/-c is required for -l'))
123 123
124 124 funcmap = [func for op, func in opmap if opts.get(op)]
125 125 if linenumber:
126 126 lastfunc = funcmap[-1]
127 127 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
128 128
129 129 ctx = repo[opts.get('rev')]
130 130 m = cmdutil.match(repo, pats, opts)
131 131 follow = not opts.get('no_follow')
132 132 for abs in ctx.walk(m):
133 133 fctx = ctx[abs]
134 134 if not opts.get('text') and util.binary(fctx.data()):
135 135 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
136 136 continue
137 137
138 138 lines = fctx.annotate(follow=follow, linenumber=linenumber)
139 139 pieces = []
140 140
141 141 for f in funcmap:
142 142 l = [f(n) for n, dummy in lines]
143 143 if l:
144 144 sized = [(x, encoding.colwidth(x)) for x in l]
145 145 ml = max([w for x, w in sized])
146 146 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
147 147
148 148 if pieces:
149 149 for p, l in zip(zip(*pieces), lines):
150 150 ui.write("%s: %s" % (" ".join(p), l[1]))
151 151
152 152 def archive(ui, repo, dest, **opts):
153 153 '''create an unversioned archive of a repository revision
154 154
155 155 By default, the revision used is the parent of the working
156 156 directory; use -r/--rev to specify a different revision.
157 157
158 158 The archive type is automatically detected based on file
159 159 extension (or override using -t/--type).
160 160
161 161 Valid types are:
162 162
163 163 :``files``: a directory full of files (default)
164 164 :``tar``: tar archive, uncompressed
165 165 :``tbz2``: tar archive, compressed using bzip2
166 166 :``tgz``: tar archive, compressed using gzip
167 167 :``uzip``: zip archive, uncompressed
168 168 :``zip``: zip archive, compressed using deflate
169 169
170 170 The exact name of the destination archive or directory is given
171 171 using a format string; see :hg:`help export` for details.
172 172
173 173 Each member added to an archive file has a directory prefix
174 174 prepended. Use -p/--prefix to specify a format string for the
175 175 prefix. The default is the basename of the archive, with suffixes
176 176 removed.
177 177
178 178 Returns 0 on success.
179 179 '''
180 180
181 181 ctx = repo[opts.get('rev')]
182 182 if not ctx:
183 183 raise util.Abort(_('no working directory: please specify a revision'))
184 184 node = ctx.node()
185 185 dest = cmdutil.make_filename(repo, dest, node)
186 186 if os.path.realpath(dest) == repo.root:
187 187 raise util.Abort(_('repository root cannot be destination'))
188 188
189 189 kind = opts.get('type') or archival.guesskind(dest) or 'files'
190 190 prefix = opts.get('prefix')
191 191
192 192 if dest == '-':
193 193 if kind == 'files':
194 194 raise util.Abort(_('cannot archive plain files to stdout'))
195 195 dest = sys.stdout
196 196 if not prefix:
197 197 prefix = os.path.basename(repo.root) + '-%h'
198 198
199 199 prefix = cmdutil.make_filename(repo, prefix, node)
200 200 matchfn = cmdutil.match(repo, [], opts)
201 201 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
202 202 matchfn, prefix, subrepos=opts.get('subrepos'))
203 203
204 204 def backout(ui, repo, node=None, rev=None, **opts):
205 205 '''reverse effect of earlier changeset
206 206
207 207 The backout command merges the reverse effect of the reverted
208 208 changeset into the working directory.
209 209
210 210 With the --merge option, it first commits the reverted changes
211 211 as a new changeset. This new changeset is a child of the reverted
212 212 changeset.
213 213 The --merge option remembers the parent of the working directory
214 214 before starting the backout, then merges the new head with that
215 215 changeset afterwards.
216 216 This will result in an explicit merge in the history.
217 217
218 218 If you backout a changeset other than the original parent of the
219 219 working directory, the result of this merge is not committed,
220 220 as with a normal merge. Otherwise, no merge is needed and the
221 221 commit is automatic.
222 222
223 223 Note that the default behavior (without --merge) has changed in
224 224 version 1.7. To restore the previous default behavior, use
225 225 :hg:`backout --merge` and then :hg:`update --clean .` to get rid of
226 226 the ongoing merge.
227 227
228 228 See :hg:`help dates` for a list of formats valid for -d/--date.
229 229
230 230 Returns 0 on success.
231 231 '''
232 232 if rev and node:
233 233 raise util.Abort(_("please specify just one revision"))
234 234
235 235 if not rev:
236 236 rev = node
237 237
238 238 if not rev:
239 239 raise util.Abort(_("please specify a revision to backout"))
240 240
241 241 date = opts.get('date')
242 242 if date:
243 243 opts['date'] = util.parsedate(date)
244 244
245 245 cmdutil.bail_if_changed(repo)
246 246 node = repo.lookup(rev)
247 247
248 248 op1, op2 = repo.dirstate.parents()
249 249 a = repo.changelog.ancestor(op1, node)
250 250 if a != node:
251 251 raise util.Abort(_('cannot backout change on a different branch'))
252 252
253 253 p1, p2 = repo.changelog.parents(node)
254 254 if p1 == nullid:
255 255 raise util.Abort(_('cannot backout a change with no parents'))
256 256 if p2 != nullid:
257 257 if not opts.get('parent'):
258 258 raise util.Abort(_('cannot backout a merge changeset without '
259 259 '--parent'))
260 260 p = repo.lookup(opts['parent'])
261 261 if p not in (p1, p2):
262 262 raise util.Abort(_('%s is not a parent of %s') %
263 263 (short(p), short(node)))
264 264 parent = p
265 265 else:
266 266 if opts.get('parent'):
267 267 raise util.Abort(_('cannot use --parent on non-merge changeset'))
268 268 parent = p1
269 269
270 270 # the backout should appear on the same branch
271 271 branch = repo.dirstate.branch()
272 272 hg.clean(repo, node, show_stats=False)
273 273 repo.dirstate.setbranch(branch)
274 274 revert_opts = opts.copy()
275 275 revert_opts['date'] = None
276 276 revert_opts['all'] = True
277 277 revert_opts['rev'] = hex(parent)
278 278 revert_opts['no_backup'] = None
279 279 revert(ui, repo, **revert_opts)
280 280 if not opts.get('merge') and op1 != node:
281 281 try:
282 282 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
283 283 return hg.update(repo, op1)
284 284 finally:
285 285 ui.setconfig('ui', 'forcemerge', '')
286 286
287 287 commit_opts = opts.copy()
288 288 commit_opts['addremove'] = False
289 289 if not commit_opts['message'] and not commit_opts['logfile']:
290 290 # we don't translate commit messages
291 291 commit_opts['message'] = "Backed out changeset %s" % short(node)
292 292 commit_opts['force_editor'] = True
293 293 commit(ui, repo, **commit_opts)
294 294 def nice(node):
295 295 return '%d:%s' % (repo.changelog.rev(node), short(node))
296 296 ui.status(_('changeset %s backs out changeset %s\n') %
297 297 (nice(repo.changelog.tip()), nice(node)))
298 298 if opts.get('merge') and op1 != node:
299 299 hg.clean(repo, op1, show_stats=False)
300 300 ui.status(_('merging with changeset %s\n')
301 301 % nice(repo.changelog.tip()))
302 302 try:
303 303 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
304 304 return hg.merge(repo, hex(repo.changelog.tip()))
305 305 finally:
306 306 ui.setconfig('ui', 'forcemerge', '')
307 307 return 0
308 308
309 309 def bisect(ui, repo, rev=None, extra=None, command=None,
310 310 reset=None, good=None, bad=None, skip=None, noupdate=None):
311 311 """subdivision search of changesets
312 312
313 313 This command helps to find changesets which introduce problems. To
314 314 use, mark the earliest changeset you know exhibits the problem as
315 315 bad, then mark the latest changeset which is free from the problem
316 316 as good. Bisect will update your working directory to a revision
317 317 for testing (unless the -U/--noupdate option is specified). Once
318 318 you have performed tests, mark the working directory as good or
319 319 bad, and bisect will either update to another candidate changeset
320 320 or announce that it has found the bad revision.
321 321
322 322 As a shortcut, you can also use the revision argument to mark a
323 323 revision as good or bad without checking it out first.
324 324
325 325 If you supply a command, it will be used for automatic bisection.
326 326 Its exit status will be used to mark revisions as good or bad:
327 327 status 0 means good, 125 means to skip the revision, 127
328 328 (command not found) will abort the bisection, and any other
329 329 non-zero exit status means the revision is bad.
330 330
331 331 Returns 0 on success.
332 332 """
333 333 def print_result(nodes, good):
334 334 displayer = cmdutil.show_changeset(ui, repo, {})
335 335 if len(nodes) == 1:
336 336 # narrowed it down to a single revision
337 337 if good:
338 338 ui.write(_("The first good revision is:\n"))
339 339 else:
340 340 ui.write(_("The first bad revision is:\n"))
341 341 displayer.show(repo[nodes[0]])
342 342 parents = repo[nodes[0]].parents()
343 343 if len(parents) > 1:
344 344 side = good and state['bad'] or state['good']
345 345 num = len(set(i.node() for i in parents) & set(side))
346 346 if num == 1:
347 347 common = parents[0].ancestor(parents[1])
348 348 ui.write(_('Not all ancestors of this changeset have been'
349 349 ' checked.\nTo check the other ancestors, start'
350 350 ' from the common ancestor, %s.\n' % common))
351 351 else:
352 352 # multiple possible revisions
353 353 if good:
354 354 ui.write(_("Due to skipped revisions, the first "
355 355 "good revision could be any of:\n"))
356 356 else:
357 357 ui.write(_("Due to skipped revisions, the first "
358 358 "bad revision could be any of:\n"))
359 359 for n in nodes:
360 360 displayer.show(repo[n])
361 361 displayer.close()
362 362
363 363 def check_state(state, interactive=True):
364 364 if not state['good'] or not state['bad']:
365 365 if (good or bad or skip or reset) and interactive:
366 366 return
367 367 if not state['good']:
368 368 raise util.Abort(_('cannot bisect (no known good revisions)'))
369 369 else:
370 370 raise util.Abort(_('cannot bisect (no known bad revisions)'))
371 371 return True
372 372
373 373 # backward compatibility
374 374 if rev in "good bad reset init".split():
375 375 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
376 376 cmd, rev, extra = rev, extra, None
377 377 if cmd == "good":
378 378 good = True
379 379 elif cmd == "bad":
380 380 bad = True
381 381 else:
382 382 reset = True
383 383 elif extra or good + bad + skip + reset + bool(command) > 1:
384 384 raise util.Abort(_('incompatible arguments'))
385 385
386 386 if reset:
387 387 p = repo.join("bisect.state")
388 388 if os.path.exists(p):
389 389 os.unlink(p)
390 390 return
391 391
392 392 state = hbisect.load_state(repo)
393 393
394 394 if command:
395 395 changesets = 1
396 396 try:
397 397 while changesets:
398 398 # update state
399 399 status = util.system(command)
400 400 if status == 125:
401 401 transition = "skip"
402 402 elif status == 0:
403 403 transition = "good"
404 404 # status < 0 means process was killed
405 405 elif status == 127:
406 406 raise util.Abort(_("failed to execute %s") % command)
407 407 elif status < 0:
408 408 raise util.Abort(_("%s killed") % command)
409 409 else:
410 410 transition = "bad"
411 411 ctx = repo[rev or '.']
412 412 state[transition].append(ctx.node())
413 413 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
414 414 check_state(state, interactive=False)
415 415 # bisect
416 416 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
417 417 # update to next check
418 418 cmdutil.bail_if_changed(repo)
419 419 hg.clean(repo, nodes[0], show_stats=False)
420 420 finally:
421 421 hbisect.save_state(repo, state)
422 422 print_result(nodes, good)
423 423 return
424 424
425 425 # update state
426 426
427 427 if rev:
428 428 nodes = [repo.lookup(i) for i in cmdutil.revrange(repo, [rev])]
429 429 else:
430 430 nodes = [repo.lookup('.')]
431 431
432 432 if good or bad or skip:
433 433 if good:
434 434 state['good'] += nodes
435 435 elif bad:
436 436 state['bad'] += nodes
437 437 elif skip:
438 438 state['skip'] += nodes
439 439 hbisect.save_state(repo, state)
440 440
441 441 if not check_state(state):
442 442 return
443 443
444 444 # actually bisect
445 445 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
446 446 if changesets == 0:
447 447 print_result(nodes, good)
448 448 else:
449 449 assert len(nodes) == 1 # only a single node can be tested next
450 450 node = nodes[0]
451 451 # compute the approximate number of remaining tests
452 452 tests, size = 0, 2
453 453 while size <= changesets:
454 454 tests, size = tests + 1, size * 2
455 455 rev = repo.changelog.rev(node)
456 456 ui.write(_("Testing changeset %d:%s "
457 457 "(%d changesets remaining, ~%d tests)\n")
458 458 % (rev, short(node), changesets, tests))
459 459 if not noupdate:
460 460 cmdutil.bail_if_changed(repo)
461 461 return hg.clean(repo, node)
462 462
463 463 def branch(ui, repo, label=None, **opts):
464 464 """set or show the current branch name
465 465
466 466 With no argument, show the current branch name. With one argument,
467 467 set the working directory branch name (the branch will not exist
468 468 in the repository until the next commit). Standard practice
469 469 recommends that primary development take place on the 'default'
470 470 branch.
471 471
472 472 Unless -f/--force is specified, branch will not let you set a
473 473 branch name that already exists, even if it's inactive.
474 474
475 475 Use -C/--clean to reset the working directory branch to that of
476 476 the parent of the working directory, negating a previous branch
477 477 change.
478 478
479 479 Use the command :hg:`update` to switch to an existing branch. Use
480 480 :hg:`commit --close-branch` to mark this branch as closed.
481 481
482 482 Returns 0 on success.
483 483 """
484 484
485 485 if opts.get('clean'):
486 486 label = repo[None].parents()[0].branch()
487 487 repo.dirstate.setbranch(label)
488 488 ui.status(_('reset working directory to branch %s\n') % label)
489 489 elif label:
490 490 utflabel = encoding.fromlocal(label)
491 491 if not opts.get('force') and utflabel in repo.branchtags():
492 492 if label not in [p.branch() for p in repo.parents()]:
493 493 raise util.Abort(_('a branch of the same name already exists'
494 494 " (use 'hg update' to switch to it)"))
495 495 repo.dirstate.setbranch(utflabel)
496 496 ui.status(_('marked working directory as branch %s\n') % label)
497 497 else:
498 498 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
499 499
500 500 def branches(ui, repo, active=False, closed=False):
501 501 """list repository named branches
502 502
503 503 List the repository's named branches, indicating which ones are
504 504 inactive. If -c/--closed is specified, also list branches which have
505 505 been marked closed (see :hg:`commit --close-branch`).
506 506
507 507 If -a/--active is specified, only show active branches. A branch
508 508 is considered active if it contains repository heads.
509 509
510 510 Use the command :hg:`update` to switch to an existing branch.
511 511
512 512 Returns 0.
513 513 """
514 514
515 515 hexfunc = ui.debugflag and hex or short
516 516 activebranches = [repo[n].branch() for n in repo.heads()]
517 517 def testactive(tag, node):
518 518 realhead = tag in activebranches
519 519 open = node in repo.branchheads(tag, closed=False)
520 520 return realhead and open
521 521 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
522 522 for tag, node in repo.branchtags().items()],
523 523 reverse=True)
524 524
525 525 for isactive, node, tag in branches:
526 526 if (not active) or isactive:
527 527 encodedtag = encoding.tolocal(tag)
528 528 if ui.quiet:
529 529 ui.write("%s\n" % encodedtag)
530 530 else:
531 531 hn = repo.lookup(node)
532 532 if isactive:
533 533 label = 'branches.active'
534 534 notice = ''
535 535 elif hn not in repo.branchheads(tag, closed=False):
536 536 if not closed:
537 537 continue
538 538 label = 'branches.closed'
539 539 notice = _(' (closed)')
540 540 else:
541 541 label = 'branches.inactive'
542 542 notice = _(' (inactive)')
543 543 if tag == repo.dirstate.branch():
544 544 label = 'branches.current'
545 545 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
546 546 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
547 547 encodedtag = ui.label(encodedtag, label)
548 548 ui.write("%s %s%s\n" % (encodedtag, rev, notice))
549 549
550 550 def bundle(ui, repo, fname, dest=None, **opts):
551 551 """create a changegroup file
552 552
553 553 Generate a compressed changegroup file collecting changesets not
554 554 known to be in another repository.
555 555
556 556 If you omit the destination repository, then hg assumes the
557 557 destination will have all the nodes you specify with --base
558 558 parameters. To create a bundle containing all changesets, use
559 559 -a/--all (or --base null).
560 560
561 561 You can change compression method with the -t/--type option.
562 562 The available compression methods are: none, bzip2, and
563 563 gzip (by default, bundles are compressed using bzip2).
564 564
565 565 The bundle file can then be transferred using conventional means
566 566 and applied to another repository with the unbundle or pull
567 567 command. This is useful when direct push and pull are not
568 568 available or when exporting an entire repository is undesirable.
569 569
570 570 Applying bundles preserves all changeset contents including
571 571 permissions, copy/rename information, and revision history.
572 572
573 573 Returns 0 on success, 1 if no changes found.
574 574 """
575 575 revs = opts.get('rev') or None
576 576 if opts.get('all'):
577 577 base = ['null']
578 578 else:
579 579 base = opts.get('base')
580 580 if base:
581 581 if dest:
582 582 raise util.Abort(_("--base is incompatible with specifying "
583 583 "a destination"))
584 584 base = [repo.lookup(rev) for rev in base]
585 585 # create the right base
586 586 # XXX: nodesbetween / changegroup* should be "fixed" instead
587 587 o = []
588 588 has = set((nullid,))
589 589 for n in base:
590 590 has.update(repo.changelog.reachable(n))
591 591 if revs:
592 592 revs = [repo.lookup(rev) for rev in revs]
593 593 visit = revs[:]
594 594 has.difference_update(visit)
595 595 else:
596 596 visit = repo.changelog.heads()
597 597 seen = {}
598 598 while visit:
599 599 n = visit.pop(0)
600 600 parents = [p for p in repo.changelog.parents(n) if p not in has]
601 601 if len(parents) == 0:
602 602 if n not in has:
603 603 o.append(n)
604 604 else:
605 605 for p in parents:
606 606 if p not in seen:
607 607 seen[p] = 1
608 608 visit.append(p)
609 609 else:
610 610 dest = ui.expandpath(dest or 'default-push', dest or 'default')
611 611 dest, branches = hg.parseurl(dest, opts.get('branch'))
612 612 other = hg.repository(hg.remoteui(repo, opts), dest)
613 613 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
614 614 if revs:
615 615 revs = [repo.lookup(rev) for rev in revs]
616 616 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
617 617
618 618 if not o:
619 619 ui.status(_("no changes found\n"))
620 620 return 1
621 621
622 622 if revs:
623 623 cg = repo.changegroupsubset(o, revs, 'bundle')
624 624 else:
625 625 cg = repo.changegroup(o, 'bundle')
626 626
627 627 bundletype = opts.get('type', 'bzip2').lower()
628 628 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
629 629 bundletype = btypes.get(bundletype)
630 630 if bundletype not in changegroup.bundletypes:
631 631 raise util.Abort(_('unknown bundle type specified with --type'))
632 632
633 633 changegroup.writebundle(cg, fname, bundletype)
634 634
635 635 def cat(ui, repo, file1, *pats, **opts):
636 636 """output the current or given revision of files
637 637
638 638 Print the specified files as they were at the given revision. If
639 639 no revision is given, the parent of the working directory is used,
640 640 or tip if no revision is checked out.
641 641
642 642 Output may be to a file, in which case the name of the file is
643 643 given using a format string. The formatting rules are the same as
644 644 for the export command, with the following additions:
645 645
646 646 :``%s``: basename of file being printed
647 647 :``%d``: dirname of file being printed, or '.' if in repository root
648 648 :``%p``: root-relative path name of file being printed
649 649
650 650 Returns 0 on success.
651 651 """
652 652 ctx = cmdutil.revsingle(repo, opts.get('rev'))
653 653 err = 1
654 654 m = cmdutil.match(repo, (file1,) + pats, opts)
655 655 for abs in ctx.walk(m):
656 656 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
657 657 data = ctx[abs].data()
658 658 if opts.get('decode'):
659 659 data = repo.wwritedata(abs, data)
660 660 fp.write(data)
661 661 err = 0
662 662 return err
663 663
664 664 def clone(ui, source, dest=None, **opts):
665 665 """make a copy of an existing repository
666 666
667 667 Create a copy of an existing repository in a new directory.
668 668
669 669 If no destination directory name is specified, it defaults to the
670 670 basename of the source.
671 671
672 672 The location of the source is added to the new repository's
673 673 .hg/hgrc file, as the default to be used for future pulls.
674 674
675 675 See :hg:`help urls` for valid source format details.
676 676
677 677 It is possible to specify an ``ssh://`` URL as the destination, but no
678 678 .hg/hgrc and working directory will be created on the remote side.
679 679 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
680 680
681 681 A set of changesets (tags, or branch names) to pull may be specified
682 682 by listing each changeset (tag, or branch name) with -r/--rev.
683 683 If -r/--rev is used, the cloned repository will contain only a subset
684 684 of the changesets of the source repository. Only the set of changesets
685 685 defined by all -r/--rev options (including all their ancestors)
686 686 will be pulled into the destination repository.
687 687 No subsequent changesets (including subsequent tags) will be present
688 688 in the destination.
689 689
690 690 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
691 691 local source repositories.
692 692
693 693 For efficiency, hardlinks are used for cloning whenever the source
694 694 and destination are on the same filesystem (note this applies only
695 695 to the repository data, not to the working directory). Some
696 696 filesystems, such as AFS, implement hardlinking incorrectly, but
697 697 do not report errors. In these cases, use the --pull option to
698 698 avoid hardlinking.
699 699
700 700 In some cases, you can clone repositories and the working directory
701 701 using full hardlinks with ::
702 702
703 703 $ cp -al REPO REPOCLONE
704 704
705 705 This is the fastest way to clone, but it is not always safe. The
706 706 operation is not atomic (making sure REPO is not modified during
707 707 the operation is up to you) and you have to make sure your editor
708 708 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
709 709 this is not compatible with certain extensions that place their
710 710 metadata under the .hg directory, such as mq.
711 711
712 712 Mercurial will update the working directory to the first applicable
713 713 revision from this list:
714 714
715 715 a) null if -U or the source repository has no changesets
716 716 b) if -u . and the source repository is local, the first parent of
717 717 the source repository's working directory
718 718 c) the changeset specified with -u (if a branch name, this means the
719 719 latest head of that branch)
720 720 d) the changeset specified with -r
721 721 e) the tipmost head specified with -b
722 722 f) the tipmost head specified with the url#branch source syntax
723 723 g) the tipmost head of the default branch
724 724 h) tip
725 725
726 726 Returns 0 on success.
727 727 """
728 728 if opts.get('noupdate') and opts.get('updaterev'):
729 729 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
730 730
731 731 r = hg.clone(hg.remoteui(ui, opts), source, dest,
732 732 pull=opts.get('pull'),
733 733 stream=opts.get('uncompressed'),
734 734 rev=opts.get('rev'),
735 735 update=opts.get('updaterev') or not opts.get('noupdate'),
736 736 branch=opts.get('branch'))
737 737
738 738 return r is None
739 739
740 740 def commit(ui, repo, *pats, **opts):
741 741 """commit the specified files or all outstanding changes
742 742
743 743 Commit changes to the given files into the repository. Unlike a
744 744 centralized RCS, this operation is a local operation. See
745 745 :hg:`push` for a way to actively distribute your changes.
746 746
747 747 If a list of files is omitted, all changes reported by :hg:`status`
748 748 will be committed.
749 749
750 750 If you are committing the result of a merge, do not provide any
751 751 filenames or -I/-X filters.
752 752
753 753 If no commit message is specified, Mercurial starts your
754 754 configured editor where you can enter a message. In case your
755 755 commit fails, you will find a backup of your message in
756 756 ``.hg/last-message.txt``.
757 757
758 758 See :hg:`help dates` for a list of formats valid for -d/--date.
759 759
760 760 Returns 0 on success, 1 if nothing changed.
761 761 """
762 762 extra = {}
763 763 if opts.get('close_branch'):
764 764 if repo['.'].node() not in repo.branchheads():
765 765 # The topo heads set is included in the branch heads set of the
766 766 # current branch, so it's sufficient to test branchheads
767 767 raise util.Abort(_('can only close branch heads'))
768 768 extra['close'] = 1
769 769 e = cmdutil.commiteditor
770 770 if opts.get('force_editor'):
771 771 e = cmdutil.commitforceeditor
772 772
773 773 def commitfunc(ui, repo, message, match, opts):
774 774 return repo.commit(message, opts.get('user'), opts.get('date'), match,
775 775 editor=e, extra=extra)
776 776
777 777 branch = repo[None].branch()
778 778 bheads = repo.branchheads(branch)
779 779
780 780 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
781 781 if not node:
782 782 ui.status(_("nothing changed\n"))
783 783 return 1
784 784
785 785 ctx = repo[node]
786 786 parents = ctx.parents()
787 787
788 788 if bheads and not [x for x in parents
789 789 if x.node() in bheads and x.branch() == branch]:
790 790 ui.status(_('created new head\n'))
791 791 # The message is not printed for initial roots. For the other
792 792 # changesets, it is printed in the following situations:
793 793 #
794 794 # Par column: for the 2 parents with ...
795 795 # N: null or no parent
796 796 # B: parent is on another named branch
797 797 # C: parent is a regular non head changeset
798 798 # H: parent was a branch head of the current branch
799 799 # Msg column: whether we print "created new head" message
800 800 # In the following, it is assumed that there already exists some
801 801 # initial branch heads of the current branch, otherwise nothing is
802 802 # printed anyway.
803 803 #
804 804 # Par Msg Comment
805 805 # NN y additional topo root
806 806 #
807 807 # BN y additional branch root
808 808 # CN y additional topo head
809 809 # HN n usual case
810 810 #
811 811 # BB y weird additional branch root
812 812 # CB y branch merge
813 813 # HB n merge with named branch
814 814 #
815 815 # CC y additional head from merge
816 816 # CH n merge with a head
817 817 #
818 818 # HH n head merge: head count decreases
819 819
820 820 if not opts.get('close_branch'):
821 821 for r in parents:
822 822 if r.extra().get('close') and r.branch() == branch:
823 823 ui.status(_('reopening closed branch head %d\n') % r)
824 824
825 825 if ui.debugflag:
826 826 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
827 827 elif ui.verbose:
828 828 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
829 829
830 830 def copy(ui, repo, *pats, **opts):
831 831 """mark files as copied for the next commit
832 832
833 833 Mark dest as having copies of source files. If dest is a
834 834 directory, copies are put in that directory. If dest is a file,
835 835 the source must be a single file.
836 836
837 837 By default, this command copies the contents of files as they
838 838 exist in the working directory. If invoked with -A/--after, the
839 839 operation is recorded, but no copying is performed.
840 840
841 841 This command takes effect with the next commit. To undo a copy
842 842 before that, see :hg:`revert`.
843 843
844 844 Returns 0 on success, 1 if errors are encountered.
845 845 """
846 846 wlock = repo.wlock(False)
847 847 try:
848 848 return cmdutil.copy(ui, repo, pats, opts)
849 849 finally:
850 850 wlock.release()
851 851
852 852 def debugancestor(ui, repo, *args):
853 853 """find the ancestor revision of two revisions in a given index"""
854 854 if len(args) == 3:
855 855 index, rev1, rev2 = args
856 856 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
857 857 lookup = r.lookup
858 858 elif len(args) == 2:
859 859 if not repo:
860 860 raise util.Abort(_("there is no Mercurial repository here "
861 861 "(.hg not found)"))
862 862 rev1, rev2 = args
863 863 r = repo.changelog
864 864 lookup = repo.lookup
865 865 else:
866 866 raise util.Abort(_('either two or three arguments required'))
867 867 a = r.ancestor(lookup(rev1), lookup(rev2))
868 868 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
869 869
870 870 def debugbuilddag(ui, repo, text,
871 871 mergeable_file=False,
872 872 appended_file=False,
873 873 overwritten_file=False,
874 874 new_file=False):
875 875 """builds a repo with a given dag from scratch in the current empty repo
876 876
877 877 Elements:
878 878
879 879 - "+n" is a linear run of n nodes based on the current default parent
880 880 - "." is a single node based on the current default parent
881 881 - "$" resets the default parent to null (implied at the start);
882 882 otherwise the default parent is always the last node created
883 883 - "<p" sets the default parent to the backref p
884 884 - "*p" is a fork at parent p, which is a backref
885 885 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
886 886 - "/p2" is a merge of the preceding node and p2
887 887 - ":tag" defines a local tag for the preceding node
888 888 - "@branch" sets the named branch for subsequent nodes
889 889 - "!command" runs the command using your shell
890 890 - "!!my command\\n" is like "!", but to the end of the line
891 891 - "#...\\n" is a comment up to the end of the line
892 892
893 893 Whitespace between the above elements is ignored.
894 894
895 895 A backref is either
896 896
897 897 - a number n, which references the node curr-n, where curr is the current
898 898 node, or
899 899 - the name of a local tag you placed earlier using ":tag", or
900 900 - empty to denote the default parent.
901 901
902 902 All string valued-elements are either strictly alphanumeric, or must
903 903 be enclosed in double quotes ("..."), with "\\" as escape character.
904 904
905 905 Note that the --overwritten-file and --appended-file options imply the
906 906 use of "HGMERGE=internal:local" during DAG buildup.
907 907 """
908 908
909 909 if not (mergeable_file or appended_file or overwritten_file or new_file):
910 910 raise util.Abort(_('need at least one of -m, -a, -o, -n'))
911 911
912 912 if len(repo.changelog) > 0:
913 913 raise util.Abort(_('repository is not empty'))
914 914
915 915 if overwritten_file or appended_file:
916 916 # we don't want to fail in merges during buildup
917 917 os.environ['HGMERGE'] = 'internal:local'
918 918
919 919 def writefile(fname, text, fmode="wb"):
920 920 f = open(fname, fmode)
921 921 try:
922 922 f.write(text)
923 923 finally:
924 924 f.close()
925 925
926 926 if mergeable_file:
927 927 linesperrev = 2
928 928 # determine number of revs in DAG
929 929 n = 0
930 930 for type, data in dagparser.parsedag(text):
931 931 if type == 'n':
932 932 n += 1
933 933 # make a file with k lines per rev
934 934 writefile("mf", "\n".join(str(i) for i in xrange(0, n * linesperrev))
935 935 + "\n")
936 936
937 937 at = -1
938 938 atbranch = 'default'
939 939 for type, data in dagparser.parsedag(text):
940 940 if type == 'n':
941 941 ui.status('node %s\n' % str(data))
942 942 id, ps = data
943 943 p1 = ps[0]
944 944 if p1 != at:
945 945 update(ui, repo, node=str(p1), clean=True)
946 946 at = p1
947 947 if repo.dirstate.branch() != atbranch:
948 948 branch(ui, repo, atbranch, force=True)
949 949 if len(ps) > 1:
950 950 p2 = ps[1]
951 951 merge(ui, repo, node=p2)
952 952
953 953 if mergeable_file:
954 954 f = open("mf", "rb+")
955 955 try:
956 956 lines = f.read().split("\n")
957 957 lines[id * linesperrev] += " r%i" % id
958 958 f.seek(0)
959 959 f.write("\n".join(lines))
960 960 finally:
961 961 f.close()
962 962
963 963 if appended_file:
964 964 writefile("af", "r%i\n" % id, "ab")
965 965
966 966 if overwritten_file:
967 967 writefile("of", "r%i\n" % id)
968 968
969 969 if new_file:
970 970 writefile("nf%i" % id, "r%i\n" % id)
971 971
972 972 commit(ui, repo, addremove=True, message="r%i" % id, date=(id, 0))
973 973 at = id
974 974 elif type == 'l':
975 975 id, name = data
976 976 ui.status('tag %s\n' % name)
977 977 tag(ui, repo, name, local=True)
978 978 elif type == 'a':
979 979 ui.status('branch %s\n' % data)
980 980 atbranch = data
981 981 elif type in 'cC':
982 982 r = util.system(data, cwd=repo.root)
983 983 if r:
984 984 desc, r = util.explain_exit(r)
985 985 raise util.Abort(_('%s command %s') % (data, desc))
986 986
987 987 def debugcommands(ui, cmd='', *args):
988 988 """list all available commands and options"""
989 989 for cmd, vals in sorted(table.iteritems()):
990 990 cmd = cmd.split('|')[0].strip('^')
991 991 opts = ', '.join([i[1] for i in vals[1]])
992 992 ui.write('%s: %s\n' % (cmd, opts))
993 993
994 994 def debugcomplete(ui, cmd='', **opts):
995 995 """returns the completion list associated with the given command"""
996 996
997 997 if opts.get('options'):
998 998 options = []
999 999 otables = [globalopts]
1000 1000 if cmd:
1001 1001 aliases, entry = cmdutil.findcmd(cmd, table, False)
1002 1002 otables.append(entry[1])
1003 1003 for t in otables:
1004 1004 for o in t:
1005 1005 if "(DEPRECATED)" in o[3]:
1006 1006 continue
1007 1007 if o[0]:
1008 1008 options.append('-%s' % o[0])
1009 1009 options.append('--%s' % o[1])
1010 1010 ui.write("%s\n" % "\n".join(options))
1011 1011 return
1012 1012
1013 1013 cmdlist = cmdutil.findpossible(cmd, table)
1014 1014 if ui.verbose:
1015 1015 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1016 1016 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1017 1017
1018 1018 def debugfsinfo(ui, path = "."):
1019 1019 """show information detected about current filesystem"""
1020 1020 open('.debugfsinfo', 'w').write('')
1021 1021 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1022 1022 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1023 1023 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1024 1024 and 'yes' or 'no'))
1025 1025 os.unlink('.debugfsinfo')
1026 1026
1027 1027 def debugrebuildstate(ui, repo, rev="tip"):
1028 1028 """rebuild the dirstate as it would look like for the given revision"""
1029 1029 ctx = repo[rev]
1030 1030 wlock = repo.wlock()
1031 1031 try:
1032 1032 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1033 1033 finally:
1034 1034 wlock.release()
1035 1035
1036 1036 def debugcheckstate(ui, repo):
1037 1037 """validate the correctness of the current dirstate"""
1038 1038 parent1, parent2 = repo.dirstate.parents()
1039 1039 m1 = repo[parent1].manifest()
1040 1040 m2 = repo[parent2].manifest()
1041 1041 errors = 0
1042 1042 for f in repo.dirstate:
1043 1043 state = repo.dirstate[f]
1044 1044 if state in "nr" and f not in m1:
1045 1045 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1046 1046 errors += 1
1047 1047 if state in "a" and f in m1:
1048 1048 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1049 1049 errors += 1
1050 1050 if state in "m" and f not in m1 and f not in m2:
1051 1051 ui.warn(_("%s in state %s, but not in either manifest\n") %
1052 1052 (f, state))
1053 1053 errors += 1
1054 1054 for f in m1:
1055 1055 state = repo.dirstate[f]
1056 1056 if state not in "nrm":
1057 1057 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1058 1058 errors += 1
1059 1059 if errors:
1060 1060 error = _(".hg/dirstate inconsistent with current parent's manifest")
1061 1061 raise util.Abort(error)
1062 1062
1063 1063 def showconfig(ui, repo, *values, **opts):
1064 1064 """show combined config settings from all hgrc files
1065 1065
1066 1066 With no arguments, print names and values of all config items.
1067 1067
1068 1068 With one argument of the form section.name, print just the value
1069 1069 of that config item.
1070 1070
1071 1071 With multiple arguments, print names and values of all config
1072 1072 items with matching section names.
1073 1073
1074 1074 With --debug, the source (filename and line number) is printed
1075 1075 for each config item.
1076 1076
1077 1077 Returns 0 on success.
1078 1078 """
1079 1079
1080 1080 for f in util.rcpath():
1081 1081 ui.debug(_('read config from: %s\n') % f)
1082 1082 untrusted = bool(opts.get('untrusted'))
1083 1083 if values:
1084 1084 sections = [v for v in values if '.' not in v]
1085 1085 items = [v for v in values if '.' in v]
1086 1086 if len(items) > 1 or items and sections:
1087 1087 raise util.Abort(_('only one config item permitted'))
1088 1088 for section, name, value in ui.walkconfig(untrusted=untrusted):
1089 1089 sectname = section + '.' + name
1090 1090 if values:
1091 1091 for v in values:
1092 1092 if v == section:
1093 1093 ui.debug('%s: ' %
1094 1094 ui.configsource(section, name, untrusted))
1095 1095 ui.write('%s=%s\n' % (sectname, value))
1096 1096 elif v == sectname:
1097 1097 ui.debug('%s: ' %
1098 1098 ui.configsource(section, name, untrusted))
1099 1099 ui.write(value, '\n')
1100 1100 else:
1101 1101 ui.debug('%s: ' %
1102 1102 ui.configsource(section, name, untrusted))
1103 1103 ui.write('%s=%s\n' % (sectname, value))
1104 1104
1105 1105 def debugpushkey(ui, repopath, namespace, *keyinfo):
1106 1106 '''access the pushkey key/value protocol
1107 1107
1108 1108 With two args, list the keys in the given namespace.
1109 1109
1110 1110 With five args, set a key to new if it currently is set to old.
1111 1111 Reports success or failure.
1112 1112 '''
1113 1113
1114 1114 target = hg.repository(ui, repopath)
1115 1115 if keyinfo:
1116 1116 key, old, new = keyinfo
1117 1117 r = target.pushkey(namespace, key, old, new)
1118 1118 ui.status(str(r) + '\n')
1119 1119 return not(r)
1120 1120 else:
1121 1121 for k, v in target.listkeys(namespace).iteritems():
1122 1122 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1123 1123 v.encode('string-escape')))
1124 1124
1125 1125 def debugrevspec(ui, repo, expr):
1126 1126 '''parse and apply a revision specification'''
1127 1127 if ui.verbose:
1128 1128 tree = revset.parse(expr)
1129 1129 ui.note(tree, "\n")
1130 1130 func = revset.match(expr)
1131 1131 for c in func(repo, range(len(repo))):
1132 1132 ui.write("%s\n" % c)
1133 1133
1134 1134 def debugsetparents(ui, repo, rev1, rev2=None):
1135 1135 """manually set the parents of the current working directory
1136 1136
1137 1137 This is useful for writing repository conversion tools, but should
1138 1138 be used with care.
1139 1139
1140 1140 Returns 0 on success.
1141 1141 """
1142 1142
1143 1143 if not rev2:
1144 1144 rev2 = hex(nullid)
1145 1145
1146 1146 wlock = repo.wlock()
1147 1147 try:
1148 1148 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1149 1149 finally:
1150 1150 wlock.release()
1151 1151
1152 1152 def debugstate(ui, repo, nodates=None):
1153 1153 """show the contents of the current dirstate"""
1154 1154 timestr = ""
1155 1155 showdate = not nodates
1156 1156 for file_, ent in sorted(repo.dirstate._map.iteritems()):
1157 1157 if showdate:
1158 1158 if ent[3] == -1:
1159 1159 # Pad or slice to locale representation
1160 1160 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
1161 1161 time.localtime(0)))
1162 1162 timestr = 'unset'
1163 1163 timestr = (timestr[:locale_len] +
1164 1164 ' ' * (locale_len - len(timestr)))
1165 1165 else:
1166 1166 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
1167 1167 time.localtime(ent[3]))
1168 1168 if ent[1] & 020000:
1169 1169 mode = 'lnk'
1170 1170 else:
1171 1171 mode = '%3o' % (ent[1] & 0777)
1172 1172 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
1173 1173 for f in repo.dirstate.copies():
1174 1174 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1175 1175
1176 1176 def debugsub(ui, repo, rev=None):
1177 1177 if rev == '':
1178 1178 rev = None
1179 1179 for k, v in sorted(repo[rev].substate.items()):
1180 1180 ui.write('path %s\n' % k)
1181 1181 ui.write(' source %s\n' % v[0])
1182 1182 ui.write(' revision %s\n' % v[1])
1183 1183
1184 1184 def debugdag(ui, repo, file_=None, *revs, **opts):
1185 1185 """format the changelog or an index DAG as a concise textual description
1186 1186
1187 1187 If you pass a revlog index, the revlog's DAG is emitted. If you list
1188 1188 revision numbers, they get labelled in the output as rN.
1189 1189
1190 1190 Otherwise, the changelog DAG of the current repo is emitted.
1191 1191 """
1192 1192 spaces = opts.get('spaces')
1193 1193 dots = opts.get('dots')
1194 1194 if file_:
1195 1195 rlog = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1196 1196 revs = set((int(r) for r in revs))
1197 1197 def events():
1198 1198 for r in rlog:
1199 1199 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1200 1200 if r in revs:
1201 1201 yield 'l', (r, "r%i" % r)
1202 1202 elif repo:
1203 1203 cl = repo.changelog
1204 1204 tags = opts.get('tags')
1205 1205 branches = opts.get('branches')
1206 1206 if tags:
1207 1207 labels = {}
1208 1208 for l, n in repo.tags().items():
1209 1209 labels.setdefault(cl.rev(n), []).append(l)
1210 1210 def events():
1211 1211 b = "default"
1212 1212 for r in cl:
1213 1213 if branches:
1214 1214 newb = cl.read(cl.node(r))[5]['branch']
1215 1215 if newb != b:
1216 1216 yield 'a', newb
1217 1217 b = newb
1218 1218 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1219 1219 if tags:
1220 1220 ls = labels.get(r)
1221 1221 if ls:
1222 1222 for l in ls:
1223 1223 yield 'l', (r, l)
1224 1224 else:
1225 1225 raise util.Abort(_('need repo for changelog dag'))
1226 1226
1227 1227 for line in dagparser.dagtextlines(events(),
1228 1228 addspaces=spaces,
1229 1229 wraplabels=True,
1230 1230 wrapannotations=True,
1231 1231 wrapnonlinear=dots,
1232 1232 usedots=dots,
1233 1233 maxlinewidth=70):
1234 1234 ui.write(line)
1235 1235 ui.write("\n")
1236 1236
1237 1237 def debugdata(ui, repo, file_, rev):
1238 1238 """dump the contents of a data file revision"""
1239 1239 r = None
1240 1240 if repo:
1241 1241 filelog = repo.file(file_)
1242 1242 if len(filelog):
1243 1243 r = filelog
1244 1244 if not r:
1245 1245 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
1246 1246 try:
1247 1247 ui.write(r.revision(r.lookup(rev)))
1248 1248 except KeyError:
1249 1249 raise util.Abort(_('invalid revision identifier %s') % rev)
1250 1250
1251 1251 def debugdate(ui, date, range=None, **opts):
1252 1252 """parse and display a date"""
1253 1253 if opts["extended"]:
1254 1254 d = util.parsedate(date, util.extendeddateformats)
1255 1255 else:
1256 1256 d = util.parsedate(date)
1257 1257 ui.write("internal: %s %s\n" % d)
1258 1258 ui.write("standard: %s\n" % util.datestr(d))
1259 1259 if range:
1260 1260 m = util.matchdate(range)
1261 1261 ui.write("match: %s\n" % m(d[0]))
1262 1262
1263 1263 def debugindex(ui, repo, file_):
1264 1264 """dump the contents of an index file"""
1265 1265 r = None
1266 1266 if repo:
1267 1267 filelog = repo.file(file_)
1268 1268 if len(filelog):
1269 1269 r = filelog
1270 1270 if not r:
1271 1271 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1272 1272 ui.write(" rev offset length base linkrev"
1273 1273 " nodeid p1 p2\n")
1274 1274 for i in r:
1275 1275 node = r.node(i)
1276 1276 try:
1277 1277 pp = r.parents(node)
1278 1278 except:
1279 1279 pp = [nullid, nullid]
1280 1280 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1281 1281 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1282 1282 short(node), short(pp[0]), short(pp[1])))
1283 1283
1284 1284 def debugindexdot(ui, repo, file_):
1285 1285 """dump an index DAG as a graphviz dot file"""
1286 1286 r = None
1287 1287 if repo:
1288 1288 filelog = repo.file(file_)
1289 1289 if len(filelog):
1290 1290 r = filelog
1291 1291 if not r:
1292 1292 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1293 1293 ui.write("digraph G {\n")
1294 1294 for i in r:
1295 1295 node = r.node(i)
1296 1296 pp = r.parents(node)
1297 1297 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1298 1298 if pp[1] != nullid:
1299 1299 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1300 1300 ui.write("}\n")
1301 1301
1302 1302 def debuginstall(ui):
1303 1303 '''test Mercurial installation
1304 1304
1305 1305 Returns 0 on success.
1306 1306 '''
1307 1307
1308 1308 def writetemp(contents):
1309 1309 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1310 1310 f = os.fdopen(fd, "wb")
1311 1311 f.write(contents)
1312 1312 f.close()
1313 1313 return name
1314 1314
1315 1315 problems = 0
1316 1316
1317 1317 # encoding
1318 1318 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1319 1319 try:
1320 1320 encoding.fromlocal("test")
1321 1321 except util.Abort, inst:
1322 1322 ui.write(" %s\n" % inst)
1323 1323 ui.write(_(" (check that your locale is properly set)\n"))
1324 1324 problems += 1
1325 1325
1326 1326 # compiled modules
1327 1327 ui.status(_("Checking installed modules (%s)...\n")
1328 1328 % os.path.dirname(__file__))
1329 1329 try:
1330 1330 import bdiff, mpatch, base85, osutil
1331 1331 except Exception, inst:
1332 1332 ui.write(" %s\n" % inst)
1333 1333 ui.write(_(" One or more extensions could not be found"))
1334 1334 ui.write(_(" (check that you compiled the extensions)\n"))
1335 1335 problems += 1
1336 1336
1337 1337 # templates
1338 1338 ui.status(_("Checking templates...\n"))
1339 1339 try:
1340 1340 import templater
1341 1341 templater.templater(templater.templatepath("map-cmdline.default"))
1342 1342 except Exception, inst:
1343 1343 ui.write(" %s\n" % inst)
1344 1344 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1345 1345 problems += 1
1346 1346
1347 1347 # patch
1348 1348 ui.status(_("Checking patch...\n"))
1349 1349 patchproblems = 0
1350 1350 a = "1\n2\n3\n4\n"
1351 1351 b = "1\n2\n3\ninsert\n4\n"
1352 1352 fa = writetemp(a)
1353 1353 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1354 1354 os.path.basename(fa))
1355 1355 fd = writetemp(d)
1356 1356
1357 1357 files = {}
1358 1358 try:
1359 1359 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1360 1360 except util.Abort, e:
1361 1361 ui.write(_(" patch call failed:\n"))
1362 1362 ui.write(" " + str(e) + "\n")
1363 1363 patchproblems += 1
1364 1364 else:
1365 1365 if list(files) != [os.path.basename(fa)]:
1366 1366 ui.write(_(" unexpected patch output!\n"))
1367 1367 patchproblems += 1
1368 1368 a = open(fa).read()
1369 1369 if a != b:
1370 1370 ui.write(_(" patch test failed!\n"))
1371 1371 patchproblems += 1
1372 1372
1373 1373 if patchproblems:
1374 1374 if ui.config('ui', 'patch'):
1375 1375 ui.write(_(" (Current patch tool may be incompatible with patch,"
1376 1376 " or misconfigured. Please check your configuration"
1377 1377 " file)\n"))
1378 1378 else:
1379 1379 ui.write(_(" Internal patcher failure, please report this error"
1380 1380 " to http://mercurial.selenic.com/bts/\n"))
1381 1381 problems += patchproblems
1382 1382
1383 1383 os.unlink(fa)
1384 1384 os.unlink(fd)
1385 1385
1386 1386 # editor
1387 1387 ui.status(_("Checking commit editor...\n"))
1388 1388 editor = ui.geteditor()
1389 1389 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1390 1390 if not cmdpath:
1391 1391 if editor == 'vi':
1392 1392 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1393 1393 ui.write(_(" (specify a commit editor in your configuration"
1394 1394 " file)\n"))
1395 1395 else:
1396 1396 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1397 1397 ui.write(_(" (specify a commit editor in your configuration"
1398 1398 " file)\n"))
1399 1399 problems += 1
1400 1400
1401 1401 # check username
1402 1402 ui.status(_("Checking username...\n"))
1403 1403 try:
1404 1404 ui.username()
1405 1405 except util.Abort, e:
1406 1406 ui.write(" %s\n" % e)
1407 1407 ui.write(_(" (specify a username in your configuration file)\n"))
1408 1408 problems += 1
1409 1409
1410 1410 if not problems:
1411 1411 ui.status(_("No problems detected\n"))
1412 1412 else:
1413 1413 ui.write(_("%s problems detected,"
1414 1414 " please check your install!\n") % problems)
1415 1415
1416 1416 return problems
1417 1417
1418 1418 def debugrename(ui, repo, file1, *pats, **opts):
1419 1419 """dump rename information"""
1420 1420
1421 1421 ctx = repo[opts.get('rev')]
1422 1422 m = cmdutil.match(repo, (file1,) + pats, opts)
1423 1423 for abs in ctx.walk(m):
1424 1424 fctx = ctx[abs]
1425 1425 o = fctx.filelog().renamed(fctx.filenode())
1426 1426 rel = m.rel(abs)
1427 1427 if o:
1428 1428 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1429 1429 else:
1430 1430 ui.write(_("%s not renamed\n") % rel)
1431 1431
1432 1432 def debugwalk(ui, repo, *pats, **opts):
1433 1433 """show how files match on given patterns"""
1434 1434 m = cmdutil.match(repo, pats, opts)
1435 1435 items = list(repo.walk(m))
1436 1436 if not items:
1437 1437 return
1438 1438 fmt = 'f %%-%ds %%-%ds %%s' % (
1439 1439 max([len(abs) for abs in items]),
1440 1440 max([len(m.rel(abs)) for abs in items]))
1441 1441 for abs in items:
1442 1442 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1443 1443 ui.write("%s\n" % line.rstrip())
1444 1444
1445 1445 def diff(ui, repo, *pats, **opts):
1446 1446 """diff repository (or selected files)
1447 1447
1448 1448 Show differences between revisions for the specified files.
1449 1449
1450 1450 Differences between files are shown using the unified diff format.
1451 1451
1452 1452 .. note::
1453 1453 diff may generate unexpected results for merges, as it will
1454 1454 default to comparing against the working directory's first
1455 1455 parent changeset if no revisions are specified.
1456 1456
1457 1457 When two revision arguments are given, then changes are shown
1458 1458 between those revisions. If only one revision is specified then
1459 1459 that revision is compared to the working directory, and, when no
1460 1460 revisions are specified, the working directory files are compared
1461 1461 to its parent.
1462 1462
1463 1463 Alternatively you can specify -c/--change with a revision to see
1464 1464 the changes in that changeset relative to its first parent.
1465 1465
1466 1466 Without the -a/--text option, diff will avoid generating diffs of
1467 1467 files it detects as binary. With -a, diff will generate a diff
1468 1468 anyway, probably with undesirable results.
1469 1469
1470 1470 Use the -g/--git option to generate diffs in the git extended diff
1471 1471 format. For more information, read :hg:`help diffs`.
1472 1472
1473 1473 Returns 0 on success.
1474 1474 """
1475 1475
1476 1476 revs = opts.get('rev')
1477 1477 change = opts.get('change')
1478 1478 stat = opts.get('stat')
1479 1479 reverse = opts.get('reverse')
1480 1480
1481 1481 if revs and change:
1482 1482 msg = _('cannot specify --rev and --change at the same time')
1483 1483 raise util.Abort(msg)
1484 1484 elif change:
1485 1485 node2 = repo.lookup(change)
1486 1486 node1 = repo[node2].parents()[0].node()
1487 1487 else:
1488 1488 node1, node2 = cmdutil.revpair(repo, revs)
1489 1489
1490 1490 if reverse:
1491 1491 node1, node2 = node2, node1
1492 1492
1493 1493 diffopts = patch.diffopts(ui, opts)
1494 1494 m = cmdutil.match(repo, pats, opts)
1495 1495 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1496 1496 listsubrepos=opts.get('subrepos'))
1497 1497
1498 1498 def export(ui, repo, *changesets, **opts):
1499 1499 """dump the header and diffs for one or more changesets
1500 1500
1501 1501 Print the changeset header and diffs for one or more revisions.
1502 1502
1503 1503 The information shown in the changeset header is: author, date,
1504 1504 branch name (if non-default), changeset hash, parent(s) and commit
1505 1505 comment.
1506 1506
1507 1507 .. note::
1508 1508 export may generate unexpected diff output for merge
1509 1509 changesets, as it will compare the merge changeset against its
1510 1510 first parent only.
1511 1511
1512 1512 Output may be to a file, in which case the name of the file is
1513 1513 given using a format string. The formatting rules are as follows:
1514 1514
1515 1515 :``%%``: literal "%" character
1516 1516 :``%H``: changeset hash (40 hexadecimal digits)
1517 1517 :``%N``: number of patches being generated
1518 1518 :``%R``: changeset revision number
1519 1519 :``%b``: basename of the exporting repository
1520 1520 :``%h``: short-form changeset hash (12 hexadecimal digits)
1521 1521 :``%n``: zero-padded sequence number, starting at 1
1522 1522 :``%r``: zero-padded changeset revision number
1523 1523
1524 1524 Without the -a/--text option, export will avoid generating diffs
1525 1525 of files it detects as binary. With -a, export will generate a
1526 1526 diff anyway, probably with undesirable results.
1527 1527
1528 1528 Use the -g/--git option to generate diffs in the git extended diff
1529 1529 format. See :hg:`help diffs` for more information.
1530 1530
1531 1531 With the --switch-parent option, the diff will be against the
1532 1532 second parent. It can be useful to review a merge.
1533 1533
1534 1534 Returns 0 on success.
1535 1535 """
1536 1536 changesets += tuple(opts.get('rev', []))
1537 1537 if not changesets:
1538 1538 raise util.Abort(_("export requires at least one changeset"))
1539 1539 revs = cmdutil.revrange(repo, changesets)
1540 1540 if len(revs) > 1:
1541 1541 ui.note(_('exporting patches:\n'))
1542 1542 else:
1543 1543 ui.note(_('exporting patch:\n'))
1544 1544 cmdutil.export(repo, revs, template=opts.get('output'),
1545 1545 switch_parent=opts.get('switch_parent'),
1546 1546 opts=patch.diffopts(ui, opts))
1547 1547
1548 1548 def forget(ui, repo, *pats, **opts):
1549 1549 """forget the specified files on the next commit
1550 1550
1551 1551 Mark the specified files so they will no longer be tracked
1552 1552 after the next commit.
1553 1553
1554 1554 This only removes files from the current branch, not from the
1555 1555 entire project history, and it does not delete them from the
1556 1556 working directory.
1557 1557
1558 1558 To undo a forget before the next commit, see :hg:`add`.
1559 1559
1560 1560 Returns 0 on success.
1561 1561 """
1562 1562
1563 1563 if not pats:
1564 1564 raise util.Abort(_('no files specified'))
1565 1565
1566 1566 m = cmdutil.match(repo, pats, opts)
1567 1567 s = repo.status(match=m, clean=True)
1568 1568 forget = sorted(s[0] + s[1] + s[3] + s[6])
1569 1569 errs = 0
1570 1570
1571 1571 for f in m.files():
1572 1572 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1573 1573 ui.warn(_('not removing %s: file is already untracked\n')
1574 1574 % m.rel(f))
1575 1575 errs = 1
1576 1576
1577 1577 for f in forget:
1578 1578 if ui.verbose or not m.exact(f):
1579 1579 ui.status(_('removing %s\n') % m.rel(f))
1580 1580
1581 1581 repo[None].remove(forget, unlink=False)
1582 1582 return errs
1583 1583
1584 1584 def grep(ui, repo, pattern, *pats, **opts):
1585 1585 """search for a pattern in specified files and revisions
1586 1586
1587 1587 Search revisions of files for a regular expression.
1588 1588
1589 1589 This command behaves differently than Unix grep. It only accepts
1590 1590 Python/Perl regexps. It searches repository history, not the
1591 1591 working directory. It always prints the revision number in which a
1592 1592 match appears.
1593 1593
1594 1594 By default, grep only prints output for the first revision of a
1595 1595 file in which it finds a match. To get it to print every revision
1596 1596 that contains a change in match status ("-" for a match that
1597 1597 becomes a non-match, or "+" for a non-match that becomes a match),
1598 1598 use the --all flag.
1599 1599
1600 1600 Returns 0 if a match is found, 1 otherwise.
1601 1601 """
1602 1602 reflags = 0
1603 1603 if opts.get('ignore_case'):
1604 1604 reflags |= re.I
1605 1605 try:
1606 1606 regexp = re.compile(pattern, reflags)
1607 1607 except re.error, inst:
1608 1608 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1609 1609 return 1
1610 1610 sep, eol = ':', '\n'
1611 1611 if opts.get('print0'):
1612 1612 sep = eol = '\0'
1613 1613
1614 1614 getfile = util.lrucachefunc(repo.file)
1615 1615
1616 1616 def matchlines(body):
1617 1617 begin = 0
1618 1618 linenum = 0
1619 1619 while True:
1620 1620 match = regexp.search(body, begin)
1621 1621 if not match:
1622 1622 break
1623 1623 mstart, mend = match.span()
1624 1624 linenum += body.count('\n', begin, mstart) + 1
1625 1625 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1626 1626 begin = body.find('\n', mend) + 1 or len(body)
1627 1627 lend = begin - 1
1628 1628 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1629 1629
1630 1630 class linestate(object):
1631 1631 def __init__(self, line, linenum, colstart, colend):
1632 1632 self.line = line
1633 1633 self.linenum = linenum
1634 1634 self.colstart = colstart
1635 1635 self.colend = colend
1636 1636
1637 1637 def __hash__(self):
1638 1638 return hash((self.linenum, self.line))
1639 1639
1640 1640 def __eq__(self, other):
1641 1641 return self.line == other.line
1642 1642
1643 1643 matches = {}
1644 1644 copies = {}
1645 1645 def grepbody(fn, rev, body):
1646 1646 matches[rev].setdefault(fn, [])
1647 1647 m = matches[rev][fn]
1648 1648 for lnum, cstart, cend, line in matchlines(body):
1649 1649 s = linestate(line, lnum, cstart, cend)
1650 1650 m.append(s)
1651 1651
1652 1652 def difflinestates(a, b):
1653 1653 sm = difflib.SequenceMatcher(None, a, b)
1654 1654 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1655 1655 if tag == 'insert':
1656 1656 for i in xrange(blo, bhi):
1657 1657 yield ('+', b[i])
1658 1658 elif tag == 'delete':
1659 1659 for i in xrange(alo, ahi):
1660 1660 yield ('-', a[i])
1661 1661 elif tag == 'replace':
1662 1662 for i in xrange(alo, ahi):
1663 1663 yield ('-', a[i])
1664 1664 for i in xrange(blo, bhi):
1665 1665 yield ('+', b[i])
1666 1666
1667 1667 def display(fn, ctx, pstates, states):
1668 1668 rev = ctx.rev()
1669 1669 datefunc = ui.quiet and util.shortdate or util.datestr
1670 1670 found = False
1671 1671 filerevmatches = {}
1672 1672 if opts.get('all'):
1673 1673 iter = difflinestates(pstates, states)
1674 1674 else:
1675 1675 iter = [('', l) for l in states]
1676 1676 for change, l in iter:
1677 1677 cols = [fn, str(rev)]
1678 1678 before, match, after = None, None, None
1679 1679 if opts.get('line_number'):
1680 1680 cols.append(str(l.linenum))
1681 1681 if opts.get('all'):
1682 1682 cols.append(change)
1683 1683 if opts.get('user'):
1684 1684 cols.append(ui.shortuser(ctx.user()))
1685 1685 if opts.get('date'):
1686 1686 cols.append(datefunc(ctx.date()))
1687 1687 if opts.get('files_with_matches'):
1688 1688 c = (fn, rev)
1689 1689 if c in filerevmatches:
1690 1690 continue
1691 1691 filerevmatches[c] = 1
1692 1692 else:
1693 1693 before = l.line[:l.colstart]
1694 1694 match = l.line[l.colstart:l.colend]
1695 1695 after = l.line[l.colend:]
1696 1696 ui.write(sep.join(cols))
1697 1697 if before is not None:
1698 1698 ui.write(sep + before)
1699 1699 ui.write(match, label='grep.match')
1700 1700 ui.write(after)
1701 1701 ui.write(eol)
1702 1702 found = True
1703 1703 return found
1704 1704
1705 1705 skip = {}
1706 1706 revfiles = {}
1707 1707 matchfn = cmdutil.match(repo, pats, opts)
1708 1708 found = False
1709 1709 follow = opts.get('follow')
1710 1710
1711 1711 def prep(ctx, fns):
1712 1712 rev = ctx.rev()
1713 1713 pctx = ctx.parents()[0]
1714 1714 parent = pctx.rev()
1715 1715 matches.setdefault(rev, {})
1716 1716 matches.setdefault(parent, {})
1717 1717 files = revfiles.setdefault(rev, [])
1718 1718 for fn in fns:
1719 1719 flog = getfile(fn)
1720 1720 try:
1721 1721 fnode = ctx.filenode(fn)
1722 1722 except error.LookupError:
1723 1723 continue
1724 1724
1725 1725 copied = flog.renamed(fnode)
1726 1726 copy = follow and copied and copied[0]
1727 1727 if copy:
1728 1728 copies.setdefault(rev, {})[fn] = copy
1729 1729 if fn in skip:
1730 1730 if copy:
1731 1731 skip[copy] = True
1732 1732 continue
1733 1733 files.append(fn)
1734 1734
1735 1735 if fn not in matches[rev]:
1736 1736 grepbody(fn, rev, flog.read(fnode))
1737 1737
1738 1738 pfn = copy or fn
1739 1739 if pfn not in matches[parent]:
1740 1740 try:
1741 1741 fnode = pctx.filenode(pfn)
1742 1742 grepbody(pfn, parent, flog.read(fnode))
1743 1743 except error.LookupError:
1744 1744 pass
1745 1745
1746 1746 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1747 1747 rev = ctx.rev()
1748 1748 parent = ctx.parents()[0].rev()
1749 1749 for fn in sorted(revfiles.get(rev, [])):
1750 1750 states = matches[rev][fn]
1751 1751 copy = copies.get(rev, {}).get(fn)
1752 1752 if fn in skip:
1753 1753 if copy:
1754 1754 skip[copy] = True
1755 1755 continue
1756 1756 pstates = matches.get(parent, {}).get(copy or fn, [])
1757 1757 if pstates or states:
1758 1758 r = display(fn, ctx, pstates, states)
1759 1759 found = found or r
1760 1760 if r and not opts.get('all'):
1761 1761 skip[fn] = True
1762 1762 if copy:
1763 1763 skip[copy] = True
1764 1764 del matches[rev]
1765 1765 del revfiles[rev]
1766 1766
1767 1767 return not found
1768 1768
1769 1769 def heads(ui, repo, *branchrevs, **opts):
1770 1770 """show current repository heads or show branch heads
1771 1771
1772 1772 With no arguments, show all repository branch heads.
1773 1773
1774 1774 Repository "heads" are changesets with no child changesets. They are
1775 1775 where development generally takes place and are the usual targets
1776 1776 for update and merge operations. Branch heads are changesets that have
1777 1777 no child changeset on the same branch.
1778 1778
1779 1779 If one or more REVs are given, only branch heads on the branches
1780 1780 associated with the specified changesets are shown.
1781 1781
1782 1782 If -c/--closed is specified, also show branch heads marked closed
1783 1783 (see :hg:`commit --close-branch`).
1784 1784
1785 1785 If STARTREV is specified, only those heads that are descendants of
1786 1786 STARTREV will be displayed.
1787 1787
1788 1788 If -t/--topo is specified, named branch mechanics will be ignored and only
1789 1789 changesets without children will be shown.
1790 1790
1791 1791 Returns 0 if matching heads are found, 1 if not.
1792 1792 """
1793 1793
1794 1794 if opts.get('rev'):
1795 1795 start = repo.lookup(opts['rev'])
1796 1796 else:
1797 1797 start = None
1798 1798
1799 1799 if opts.get('topo'):
1800 1800 heads = [repo[h] for h in repo.heads(start)]
1801 1801 else:
1802 1802 heads = []
1803 1803 for b, ls in repo.branchmap().iteritems():
1804 1804 if start is None:
1805 1805 heads += [repo[h] for h in ls]
1806 1806 continue
1807 1807 startrev = repo.changelog.rev(start)
1808 1808 descendants = set(repo.changelog.descendants(startrev))
1809 1809 descendants.add(startrev)
1810 1810 rev = repo.changelog.rev
1811 1811 heads += [repo[h] for h in ls if rev(h) in descendants]
1812 1812
1813 1813 if branchrevs:
1814 1814 decode, encode = encoding.fromlocal, encoding.tolocal
1815 1815 branches = set(repo[decode(br)].branch() for br in branchrevs)
1816 1816 heads = [h for h in heads if h.branch() in branches]
1817 1817
1818 1818 if not opts.get('closed'):
1819 1819 heads = [h for h in heads if not h.extra().get('close')]
1820 1820
1821 1821 if opts.get('active') and branchrevs:
1822 1822 dagheads = repo.heads(start)
1823 1823 heads = [h for h in heads if h.node() in dagheads]
1824 1824
1825 1825 if branchrevs:
1826 1826 haveheads = set(h.branch() for h in heads)
1827 1827 if branches - haveheads:
1828 1828 headless = ', '.join(encode(b) for b in branches - haveheads)
1829 1829 msg = _('no open branch heads found on branches %s')
1830 1830 if opts.get('rev'):
1831 1831 msg += _(' (started at %s)' % opts['rev'])
1832 1832 ui.warn((msg + '\n') % headless)
1833 1833
1834 1834 if not heads:
1835 1835 return 1
1836 1836
1837 1837 heads = sorted(heads, key=lambda x: -x.rev())
1838 1838 displayer = cmdutil.show_changeset(ui, repo, opts)
1839 1839 for ctx in heads:
1840 1840 displayer.show(ctx)
1841 1841 displayer.close()
1842 1842
1843 1843 def help_(ui, name=None, with_version=False, unknowncmd=False):
1844 1844 """show help for a given topic or a help overview
1845 1845
1846 1846 With no arguments, print a list of commands with short help messages.
1847 1847
1848 1848 Given a topic, extension, or command name, print help for that
1849 1849 topic.
1850 1850
1851 1851 Returns 0 if successful.
1852 1852 """
1853 1853 option_lists = []
1854 1854 textwidth = ui.termwidth() - 2
1855 1855
1856 1856 def addglobalopts(aliases):
1857 1857 if ui.verbose:
1858 1858 option_lists.append((_("global options:"), globalopts))
1859 1859 if name == 'shortlist':
1860 1860 option_lists.append((_('use "hg help" for the full list '
1861 1861 'of commands'), ()))
1862 1862 else:
1863 1863 if name == 'shortlist':
1864 1864 msg = _('use "hg help" for the full list of commands '
1865 1865 'or "hg -v" for details')
1866 1866 elif aliases:
1867 1867 msg = _('use "hg -v help%s" to show aliases and '
1868 1868 'global options') % (name and " " + name or "")
1869 1869 else:
1870 1870 msg = _('use "hg -v help %s" to show global options') % name
1871 1871 option_lists.append((msg, ()))
1872 1872
1873 1873 def helpcmd(name):
1874 1874 if with_version:
1875 1875 version_(ui)
1876 1876 ui.write('\n')
1877 1877
1878 1878 try:
1879 1879 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1880 1880 except error.AmbiguousCommand, inst:
1881 1881 # py3k fix: except vars can't be used outside the scope of the
1882 1882 # except block, nor can be used inside a lambda. python issue4617
1883 1883 prefix = inst.args[0]
1884 1884 select = lambda c: c.lstrip('^').startswith(prefix)
1885 1885 helplist(_('list of commands:\n\n'), select)
1886 1886 return
1887 1887
1888 1888 # check if it's an invalid alias and display its error if it is
1889 1889 if getattr(entry[0], 'badalias', False):
1890 1890 if not unknowncmd:
1891 1891 entry[0](ui)
1892 1892 return
1893 1893
1894 1894 # synopsis
1895 1895 if len(entry) > 2:
1896 1896 if entry[2].startswith('hg'):
1897 1897 ui.write("%s\n" % entry[2])
1898 1898 else:
1899 1899 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1900 1900 else:
1901 1901 ui.write('hg %s\n' % aliases[0])
1902 1902
1903 1903 # aliases
1904 1904 if not ui.quiet and len(aliases) > 1:
1905 1905 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1906 1906
1907 1907 # description
1908 1908 doc = gettext(entry[0].__doc__)
1909 1909 if not doc:
1910 1910 doc = _("(no help text available)")
1911 1911 if hasattr(entry[0], 'definition'): # aliased command
1912 1912 if entry[0].definition.startswith('!'): # shell alias
1913 1913 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
1914 1914 else:
1915 1915 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
1916 1916 if ui.quiet:
1917 1917 doc = doc.splitlines()[0]
1918 1918 keep = ui.verbose and ['verbose'] or []
1919 1919 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
1920 1920 ui.write("\n%s\n" % formatted)
1921 1921 if pruned:
1922 1922 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
1923 1923
1924 1924 if not ui.quiet:
1925 1925 # options
1926 1926 if entry[1]:
1927 1927 option_lists.append((_("options:\n"), entry[1]))
1928 1928
1929 1929 addglobalopts(False)
1930 1930
1931 1931 def helplist(header, select=None):
1932 1932 h = {}
1933 1933 cmds = {}
1934 1934 for c, e in table.iteritems():
1935 1935 f = c.split("|", 1)[0]
1936 1936 if select and not select(f):
1937 1937 continue
1938 1938 if (not select and name != 'shortlist' and
1939 1939 e[0].__module__ != __name__):
1940 1940 continue
1941 1941 if name == "shortlist" and not f.startswith("^"):
1942 1942 continue
1943 1943 f = f.lstrip("^")
1944 1944 if not ui.debugflag and f.startswith("debug"):
1945 1945 continue
1946 1946 doc = e[0].__doc__
1947 1947 if doc and 'DEPRECATED' in doc and not ui.verbose:
1948 1948 continue
1949 1949 doc = gettext(doc)
1950 1950 if not doc:
1951 1951 doc = _("(no help text available)")
1952 1952 h[f] = doc.splitlines()[0].rstrip()
1953 1953 cmds[f] = c.lstrip("^")
1954 1954
1955 1955 if not h:
1956 1956 ui.status(_('no commands defined\n'))
1957 1957 return
1958 1958
1959 1959 ui.status(header)
1960 1960 fns = sorted(h)
1961 1961 m = max(map(len, fns))
1962 1962 for f in fns:
1963 1963 if ui.verbose:
1964 1964 commands = cmds[f].replace("|",", ")
1965 1965 ui.write(" %s:\n %s\n"%(commands, h[f]))
1966 1966 else:
1967 1967 ui.write('%s\n' % (util.wrap(h[f], textwidth,
1968 1968 initindent=' %-*s ' % (m, f),
1969 1969 hangindent=' ' * (m + 4))))
1970 1970
1971 1971 if not ui.quiet:
1972 1972 addglobalopts(True)
1973 1973
1974 1974 def helptopic(name):
1975 1975 for names, header, doc in help.helptable:
1976 1976 if name in names:
1977 1977 break
1978 1978 else:
1979 1979 raise error.UnknownCommand(name)
1980 1980
1981 1981 # description
1982 1982 if not doc:
1983 1983 doc = _("(no help text available)")
1984 1984 if hasattr(doc, '__call__'):
1985 1985 doc = doc()
1986 1986
1987 1987 ui.write("%s\n\n" % header)
1988 1988 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1989 1989
1990 1990 def helpext(name):
1991 1991 try:
1992 1992 mod = extensions.find(name)
1993 1993 doc = gettext(mod.__doc__) or _('no help text available')
1994 1994 except KeyError:
1995 1995 mod = None
1996 1996 doc = extensions.disabledext(name)
1997 1997 if not doc:
1998 1998 raise error.UnknownCommand(name)
1999 1999
2000 2000 if '\n' not in doc:
2001 2001 head, tail = doc, ""
2002 2002 else:
2003 2003 head, tail = doc.split('\n', 1)
2004 2004 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
2005 2005 if tail:
2006 2006 ui.write(minirst.format(tail, textwidth))
2007 2007 ui.status('\n\n')
2008 2008
2009 2009 if mod:
2010 2010 try:
2011 2011 ct = mod.cmdtable
2012 2012 except AttributeError:
2013 2013 ct = {}
2014 2014 modcmds = set([c.split('|', 1)[0] for c in ct])
2015 2015 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2016 2016 else:
2017 2017 ui.write(_('use "hg help extensions" for information on enabling '
2018 2018 'extensions\n'))
2019 2019
2020 2020 def helpextcmd(name):
2021 2021 cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict'))
2022 2022 doc = gettext(mod.__doc__).splitlines()[0]
2023 2023
2024 2024 msg = help.listexts(_("'%s' is provided by the following "
2025 2025 "extension:") % cmd, {ext: doc}, len(ext),
2026 2026 indent=4)
2027 2027 ui.write(minirst.format(msg, textwidth))
2028 2028 ui.write('\n\n')
2029 2029 ui.write(_('use "hg help extensions" for information on enabling '
2030 2030 'extensions\n'))
2031 2031
2032 help.addtopichook('revsets', revset.makedoc)
2033
2032 2034 if name and name != 'shortlist':
2033 2035 i = None
2034 2036 if unknowncmd:
2035 2037 queries = (helpextcmd,)
2036 2038 else:
2037 2039 queries = (helptopic, helpcmd, helpext, helpextcmd)
2038 2040 for f in queries:
2039 2041 try:
2040 2042 f(name)
2041 2043 i = None
2042 2044 break
2043 2045 except error.UnknownCommand, inst:
2044 2046 i = inst
2045 2047 if i:
2046 2048 raise i
2047 2049
2048 2050 else:
2049 2051 # program name
2050 2052 if ui.verbose or with_version:
2051 2053 version_(ui)
2052 2054 else:
2053 2055 ui.status(_("Mercurial Distributed SCM\n"))
2054 2056 ui.status('\n')
2055 2057
2056 2058 # list of commands
2057 2059 if name == "shortlist":
2058 2060 header = _('basic commands:\n\n')
2059 2061 else:
2060 2062 header = _('list of commands:\n\n')
2061 2063
2062 2064 helplist(header)
2063 2065 if name != 'shortlist':
2064 2066 exts, maxlength = extensions.enabled()
2065 2067 text = help.listexts(_('enabled extensions:'), exts, maxlength)
2066 2068 if text:
2067 2069 ui.write("\n%s\n" % minirst.format(text, textwidth))
2068 2070
2069 2071 # list all option lists
2070 2072 opt_output = []
2071 2073 multioccur = False
2072 2074 for title, options in option_lists:
2073 2075 opt_output.append(("\n%s" % title, None))
2074 2076 for option in options:
2075 2077 if len(option) == 5:
2076 2078 shortopt, longopt, default, desc, optlabel = option
2077 2079 else:
2078 2080 shortopt, longopt, default, desc = option
2079 2081 optlabel = _("VALUE") # default label
2080 2082
2081 2083 if _("DEPRECATED") in desc and not ui.verbose:
2082 2084 continue
2083 2085 if isinstance(default, list):
2084 2086 numqualifier = " %s [+]" % optlabel
2085 2087 multioccur = True
2086 2088 elif (default is not None) and not isinstance(default, bool):
2087 2089 numqualifier = " %s" % optlabel
2088 2090 else:
2089 2091 numqualifier = ""
2090 2092 opt_output.append(("%2s%s" %
2091 2093 (shortopt and "-%s" % shortopt,
2092 2094 longopt and " --%s%s" %
2093 2095 (longopt, numqualifier)),
2094 2096 "%s%s" % (desc,
2095 2097 default
2096 2098 and _(" (default: %s)") % default
2097 2099 or "")))
2098 2100 if multioccur:
2099 2101 msg = _("\n[+] marked option can be specified multiple times")
2100 2102 if ui.verbose and name != 'shortlist':
2101 2103 opt_output.append((msg, None))
2102 2104 else:
2103 2105 opt_output.insert(-1, (msg, None))
2104 2106
2105 2107 if not name:
2106 2108 ui.write(_("\nadditional help topics:\n\n"))
2107 2109 topics = []
2108 2110 for names, header, doc in help.helptable:
2109 2111 topics.append((sorted(names, key=len, reverse=True)[0], header))
2110 2112 topics_len = max([len(s[0]) for s in topics])
2111 2113 for t, desc in topics:
2112 2114 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2113 2115
2114 2116 if opt_output:
2115 2117 colwidth = encoding.colwidth
2116 2118 # normalize: (opt or message, desc or None, width of opt)
2117 2119 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2118 2120 for opt, desc in opt_output]
2119 2121 hanging = max([e[2] for e in entries])
2120 2122 for opt, desc, width in entries:
2121 2123 if desc:
2122 2124 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2123 2125 hangindent = ' ' * (hanging + 3)
2124 2126 ui.write('%s\n' % (util.wrap(desc, textwidth,
2125 2127 initindent=initindent,
2126 2128 hangindent=hangindent)))
2127 2129 else:
2128 2130 ui.write("%s\n" % opt)
2129 2131
2130 2132 def identify(ui, repo, source=None,
2131 2133 rev=None, num=None, id=None, branch=None, tags=None):
2132 2134 """identify the working copy or specified revision
2133 2135
2134 2136 With no revision, print a summary of the current state of the
2135 2137 repository.
2136 2138
2137 2139 Specifying a path to a repository root or Mercurial bundle will
2138 2140 cause lookup to operate on that repository/bundle.
2139 2141
2140 2142 This summary identifies the repository state using one or two
2141 2143 parent hash identifiers, followed by a "+" if there are
2142 2144 uncommitted changes in the working directory, a list of tags for
2143 2145 this revision and a branch name for non-default branches.
2144 2146
2145 2147 Returns 0 if successful.
2146 2148 """
2147 2149
2148 2150 if not repo and not source:
2149 2151 raise util.Abort(_("there is no Mercurial repository here "
2150 2152 "(.hg not found)"))
2151 2153
2152 2154 hexfunc = ui.debugflag and hex or short
2153 2155 default = not (num or id or branch or tags)
2154 2156 output = []
2155 2157
2156 2158 revs = []
2157 2159 if source:
2158 2160 source, branches = hg.parseurl(ui.expandpath(source))
2159 2161 repo = hg.repository(ui, source)
2160 2162 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2161 2163
2162 2164 if not repo.local():
2163 2165 if not rev and revs:
2164 2166 rev = revs[0]
2165 2167 if not rev:
2166 2168 rev = "tip"
2167 2169 if num or branch or tags:
2168 2170 raise util.Abort(
2169 2171 "can't query remote revision number, branch, or tags")
2170 2172 output = [hexfunc(repo.lookup(rev))]
2171 2173 elif not rev:
2172 2174 ctx = repo[None]
2173 2175 parents = ctx.parents()
2174 2176 changed = False
2175 2177 if default or id or num:
2176 2178 changed = util.any(repo.status())
2177 2179 if default or id:
2178 2180 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
2179 2181 (changed) and "+" or "")]
2180 2182 if num:
2181 2183 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
2182 2184 (changed) and "+" or ""))
2183 2185 else:
2184 2186 ctx = repo[rev]
2185 2187 if default or id:
2186 2188 output = [hexfunc(ctx.node())]
2187 2189 if num:
2188 2190 output.append(str(ctx.rev()))
2189 2191
2190 2192 if repo.local() and default and not ui.quiet:
2191 2193 b = encoding.tolocal(ctx.branch())
2192 2194 if b != 'default':
2193 2195 output.append("(%s)" % b)
2194 2196
2195 2197 # multiple tags for a single parent separated by '/'
2196 2198 t = "/".join(ctx.tags())
2197 2199 if t:
2198 2200 output.append(t)
2199 2201
2200 2202 if branch:
2201 2203 output.append(encoding.tolocal(ctx.branch()))
2202 2204
2203 2205 if tags:
2204 2206 output.extend(ctx.tags())
2205 2207
2206 2208 ui.write("%s\n" % ' '.join(output))
2207 2209
2208 2210 def import_(ui, repo, patch1, *patches, **opts):
2209 2211 """import an ordered set of patches
2210 2212
2211 2213 Import a list of patches and commit them individually (unless
2212 2214 --no-commit is specified).
2213 2215
2214 2216 If there are outstanding changes in the working directory, import
2215 2217 will abort unless given the -f/--force flag.
2216 2218
2217 2219 You can import a patch straight from a mail message. Even patches
2218 2220 as attachments work (to use the body part, it must have type
2219 2221 text/plain or text/x-patch). From and Subject headers of email
2220 2222 message are used as default committer and commit message. All
2221 2223 text/plain body parts before first diff are added to commit
2222 2224 message.
2223 2225
2224 2226 If the imported patch was generated by :hg:`export`, user and
2225 2227 description from patch override values from message headers and
2226 2228 body. Values given on command line with -m/--message and -u/--user
2227 2229 override these.
2228 2230
2229 2231 If --exact is specified, import will set the working directory to
2230 2232 the parent of each patch before applying it, and will abort if the
2231 2233 resulting changeset has a different ID than the one recorded in
2232 2234 the patch. This may happen due to character set problems or other
2233 2235 deficiencies in the text patch format.
2234 2236
2235 2237 With -s/--similarity, hg will attempt to discover renames and
2236 2238 copies in the patch in the same way as 'addremove'.
2237 2239
2238 2240 To read a patch from standard input, use "-" as the patch name. If
2239 2241 a URL is specified, the patch will be downloaded from it.
2240 2242 See :hg:`help dates` for a list of formats valid for -d/--date.
2241 2243
2242 2244 Returns 0 on success.
2243 2245 """
2244 2246 patches = (patch1,) + patches
2245 2247
2246 2248 date = opts.get('date')
2247 2249 if date:
2248 2250 opts['date'] = util.parsedate(date)
2249 2251
2250 2252 try:
2251 2253 sim = float(opts.get('similarity') or 0)
2252 2254 except ValueError:
2253 2255 raise util.Abort(_('similarity must be a number'))
2254 2256 if sim < 0 or sim > 100:
2255 2257 raise util.Abort(_('similarity must be between 0 and 100'))
2256 2258
2257 2259 if opts.get('exact') or not opts.get('force'):
2258 2260 cmdutil.bail_if_changed(repo)
2259 2261
2260 2262 d = opts["base"]
2261 2263 strip = opts["strip"]
2262 2264 wlock = lock = None
2263 2265
2264 2266 def tryone(ui, hunk):
2265 2267 tmpname, message, user, date, branch, nodeid, p1, p2 = \
2266 2268 patch.extract(ui, hunk)
2267 2269
2268 2270 if not tmpname:
2269 2271 return None
2270 2272 commitid = _('to working directory')
2271 2273
2272 2274 try:
2273 2275 cmdline_message = cmdutil.logmessage(opts)
2274 2276 if cmdline_message:
2275 2277 # pickup the cmdline msg
2276 2278 message = cmdline_message
2277 2279 elif message:
2278 2280 # pickup the patch msg
2279 2281 message = message.strip()
2280 2282 else:
2281 2283 # launch the editor
2282 2284 message = None
2283 2285 ui.debug('message:\n%s\n' % message)
2284 2286
2285 2287 wp = repo.parents()
2286 2288 if opts.get('exact'):
2287 2289 if not nodeid or not p1:
2288 2290 raise util.Abort(_('not a Mercurial patch'))
2289 2291 p1 = repo.lookup(p1)
2290 2292 p2 = repo.lookup(p2 or hex(nullid))
2291 2293
2292 2294 if p1 != wp[0].node():
2293 2295 hg.clean(repo, p1)
2294 2296 repo.dirstate.setparents(p1, p2)
2295 2297 elif p2:
2296 2298 try:
2297 2299 p1 = repo.lookup(p1)
2298 2300 p2 = repo.lookup(p2)
2299 2301 if p1 == wp[0].node():
2300 2302 repo.dirstate.setparents(p1, p2)
2301 2303 except error.RepoError:
2302 2304 pass
2303 2305 if opts.get('exact') or opts.get('import_branch'):
2304 2306 repo.dirstate.setbranch(branch or 'default')
2305 2307
2306 2308 files = {}
2307 2309 try:
2308 2310 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2309 2311 files=files, eolmode=None)
2310 2312 finally:
2311 2313 files = cmdutil.updatedir(ui, repo, files,
2312 2314 similarity=sim / 100.0)
2313 2315 if not opts.get('no_commit'):
2314 2316 if opts.get('exact'):
2315 2317 m = None
2316 2318 else:
2317 2319 m = cmdutil.matchfiles(repo, files or [])
2318 2320 n = repo.commit(message, opts.get('user') or user,
2319 2321 opts.get('date') or date, match=m,
2320 2322 editor=cmdutil.commiteditor)
2321 2323 if opts.get('exact'):
2322 2324 if hex(n) != nodeid:
2323 2325 repo.rollback()
2324 2326 raise util.Abort(_('patch is damaged'
2325 2327 ' or loses information'))
2326 2328 # Force a dirstate write so that the next transaction
2327 2329 # backups an up-do-date file.
2328 2330 repo.dirstate.write()
2329 2331 if n:
2330 2332 commitid = short(n)
2331 2333
2332 2334 return commitid
2333 2335 finally:
2334 2336 os.unlink(tmpname)
2335 2337
2336 2338 try:
2337 2339 wlock = repo.wlock()
2338 2340 lock = repo.lock()
2339 2341 lastcommit = None
2340 2342 for p in patches:
2341 2343 pf = os.path.join(d, p)
2342 2344
2343 2345 if pf == '-':
2344 2346 ui.status(_("applying patch from stdin\n"))
2345 2347 pf = sys.stdin
2346 2348 else:
2347 2349 ui.status(_("applying %s\n") % p)
2348 2350 pf = url.open(ui, pf)
2349 2351
2350 2352 haspatch = False
2351 2353 for hunk in patch.split(pf):
2352 2354 commitid = tryone(ui, hunk)
2353 2355 if commitid:
2354 2356 haspatch = True
2355 2357 if lastcommit:
2356 2358 ui.status(_('applied %s\n') % lastcommit)
2357 2359 lastcommit = commitid
2358 2360
2359 2361 if not haspatch:
2360 2362 raise util.Abort(_('no diffs found'))
2361 2363
2362 2364 finally:
2363 2365 release(lock, wlock)
2364 2366
2365 2367 def incoming(ui, repo, source="default", **opts):
2366 2368 """show new changesets found in source
2367 2369
2368 2370 Show new changesets found in the specified path/URL or the default
2369 2371 pull location. These are the changesets that would have been pulled
2370 2372 if a pull at the time you issued this command.
2371 2373
2372 2374 For remote repository, using --bundle avoids downloading the
2373 2375 changesets twice if the incoming is followed by a pull.
2374 2376
2375 2377 See pull for valid source format details.
2376 2378
2377 2379 Returns 0 if there are incoming changes, 1 otherwise.
2378 2380 """
2379 2381 if opts.get('bundle') and opts.get('subrepos'):
2380 2382 raise util.Abort(_('cannot combine --bundle and --subrepos'))
2381 2383
2382 2384 ret = hg.incoming(ui, repo, source, opts)
2383 2385 return ret
2384 2386
2385 2387 def init(ui, dest=".", **opts):
2386 2388 """create a new repository in the given directory
2387 2389
2388 2390 Initialize a new repository in the given directory. If the given
2389 2391 directory does not exist, it will be created.
2390 2392
2391 2393 If no directory is given, the current directory is used.
2392 2394
2393 2395 It is possible to specify an ``ssh://`` URL as the destination.
2394 2396 See :hg:`help urls` for more information.
2395 2397
2396 2398 Returns 0 on success.
2397 2399 """
2398 2400 hg.repository(hg.remoteui(ui, opts), ui.expandpath(dest), create=1)
2399 2401
2400 2402 def locate(ui, repo, *pats, **opts):
2401 2403 """locate files matching specific patterns
2402 2404
2403 2405 Print files under Mercurial control in the working directory whose
2404 2406 names match the given patterns.
2405 2407
2406 2408 By default, this command searches all directories in the working
2407 2409 directory. To search just the current directory and its
2408 2410 subdirectories, use "--include .".
2409 2411
2410 2412 If no patterns are given to match, this command prints the names
2411 2413 of all files under Mercurial control in the working directory.
2412 2414
2413 2415 If you want to feed the output of this command into the "xargs"
2414 2416 command, use the -0 option to both this command and "xargs". This
2415 2417 will avoid the problem of "xargs" treating single filenames that
2416 2418 contain whitespace as multiple filenames.
2417 2419
2418 2420 Returns 0 if a match is found, 1 otherwise.
2419 2421 """
2420 2422 end = opts.get('print0') and '\0' or '\n'
2421 2423 rev = opts.get('rev') or None
2422 2424
2423 2425 ret = 1
2424 2426 m = cmdutil.match(repo, pats, opts, default='relglob')
2425 2427 m.bad = lambda x, y: False
2426 2428 for abs in repo[rev].walk(m):
2427 2429 if not rev and abs not in repo.dirstate:
2428 2430 continue
2429 2431 if opts.get('fullpath'):
2430 2432 ui.write(repo.wjoin(abs), end)
2431 2433 else:
2432 2434 ui.write(((pats and m.rel(abs)) or abs), end)
2433 2435 ret = 0
2434 2436
2435 2437 return ret
2436 2438
2437 2439 def log(ui, repo, *pats, **opts):
2438 2440 """show revision history of entire repository or files
2439 2441
2440 2442 Print the revision history of the specified files or the entire
2441 2443 project.
2442 2444
2443 2445 File history is shown without following rename or copy history of
2444 2446 files. Use -f/--follow with a filename to follow history across
2445 2447 renames and copies. --follow without a filename will only show
2446 2448 ancestors or descendants of the starting revision. --follow-first
2447 2449 only follows the first parent of merge revisions.
2448 2450
2449 2451 If no revision range is specified, the default is ``tip:0`` unless
2450 2452 --follow is set, in which case the working directory parent is
2451 2453 used as the starting revision. You can specify a revision set for
2452 2454 log, see :hg:`help revsets` for more information.
2453 2455
2454 2456 See :hg:`help dates` for a list of formats valid for -d/--date.
2455 2457
2456 2458 By default this command prints revision number and changeset id,
2457 2459 tags, non-trivial parents, user, date and time, and a summary for
2458 2460 each commit. When the -v/--verbose switch is used, the list of
2459 2461 changed files and full commit message are shown.
2460 2462
2461 2463 .. note::
2462 2464 log -p/--patch may generate unexpected diff output for merge
2463 2465 changesets, as it will only compare the merge changeset against
2464 2466 its first parent. Also, only files different from BOTH parents
2465 2467 will appear in files:.
2466 2468
2467 2469 Returns 0 on success.
2468 2470 """
2469 2471
2470 2472 matchfn = cmdutil.match(repo, pats, opts)
2471 2473 limit = cmdutil.loglimit(opts)
2472 2474 count = 0
2473 2475
2474 2476 endrev = None
2475 2477 if opts.get('copies') and opts.get('rev'):
2476 2478 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2477 2479
2478 2480 df = False
2479 2481 if opts["date"]:
2480 2482 df = util.matchdate(opts["date"])
2481 2483
2482 2484 branches = opts.get('branch', []) + opts.get('only_branch', [])
2483 2485 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2484 2486
2485 2487 displayer = cmdutil.show_changeset(ui, repo, opts, True)
2486 2488 def prep(ctx, fns):
2487 2489 rev = ctx.rev()
2488 2490 parents = [p for p in repo.changelog.parentrevs(rev)
2489 2491 if p != nullrev]
2490 2492 if opts.get('no_merges') and len(parents) == 2:
2491 2493 return
2492 2494 if opts.get('only_merges') and len(parents) != 2:
2493 2495 return
2494 2496 if opts.get('branch') and ctx.branch() not in opts['branch']:
2495 2497 return
2496 2498 if df and not df(ctx.date()[0]):
2497 2499 return
2498 2500 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2499 2501 return
2500 2502 if opts.get('keyword'):
2501 2503 for k in [kw.lower() for kw in opts['keyword']]:
2502 2504 if (k in ctx.user().lower() or
2503 2505 k in ctx.description().lower() or
2504 2506 k in " ".join(ctx.files()).lower()):
2505 2507 break
2506 2508 else:
2507 2509 return
2508 2510
2509 2511 copies = None
2510 2512 if opts.get('copies') and rev:
2511 2513 copies = []
2512 2514 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2513 2515 for fn in ctx.files():
2514 2516 rename = getrenamed(fn, rev)
2515 2517 if rename:
2516 2518 copies.append((fn, rename[0]))
2517 2519
2518 2520 revmatchfn = None
2519 2521 if opts.get('patch') or opts.get('stat'):
2520 2522 if opts.get('follow') or opts.get('follow_first'):
2521 2523 # note: this might be wrong when following through merges
2522 2524 revmatchfn = cmdutil.match(repo, fns, default='path')
2523 2525 else:
2524 2526 revmatchfn = matchfn
2525 2527
2526 2528 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2527 2529
2528 2530 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2529 2531 if count == limit:
2530 2532 break
2531 2533 if displayer.flush(ctx.rev()):
2532 2534 count += 1
2533 2535 displayer.close()
2534 2536
2535 2537 def manifest(ui, repo, node=None, rev=None):
2536 2538 """output the current or given revision of the project manifest
2537 2539
2538 2540 Print a list of version controlled files for the given revision.
2539 2541 If no revision is given, the first parent of the working directory
2540 2542 is used, or the null revision if no revision is checked out.
2541 2543
2542 2544 With -v, print file permissions, symlink and executable bits.
2543 2545 With --debug, print file revision hashes.
2544 2546
2545 2547 Returns 0 on success.
2546 2548 """
2547 2549
2548 2550 if rev and node:
2549 2551 raise util.Abort(_("please specify just one revision"))
2550 2552
2551 2553 if not node:
2552 2554 node = rev
2553 2555
2554 2556 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2555 2557 ctx = repo[node]
2556 2558 for f in ctx:
2557 2559 if ui.debugflag:
2558 2560 ui.write("%40s " % hex(ctx.manifest()[f]))
2559 2561 if ui.verbose:
2560 2562 ui.write(decor[ctx.flags(f)])
2561 2563 ui.write("%s\n" % f)
2562 2564
2563 2565 def merge(ui, repo, node=None, **opts):
2564 2566 """merge working directory with another revision
2565 2567
2566 2568 The current working directory is updated with all changes made in
2567 2569 the requested revision since the last common predecessor revision.
2568 2570
2569 2571 Files that changed between either parent are marked as changed for
2570 2572 the next commit and a commit must be performed before any further
2571 2573 updates to the repository are allowed. The next commit will have
2572 2574 two parents.
2573 2575
2574 2576 ``--tool`` can be used to specify the merge tool used for file
2575 2577 merges. It overrides the HGMERGE environment variable and your
2576 2578 configuration files.
2577 2579
2578 2580 If no revision is specified, the working directory's parent is a
2579 2581 head revision, and the current branch contains exactly one other
2580 2582 head, the other head is merged with by default. Otherwise, an
2581 2583 explicit revision with which to merge with must be provided.
2582 2584
2583 2585 :hg:`resolve` must be used to resolve unresolved files.
2584 2586
2585 2587 To undo an uncommitted merge, use :hg:`update --clean .` which
2586 2588 will check out a clean copy of the original merge parent, losing
2587 2589 all changes.
2588 2590
2589 2591 Returns 0 on success, 1 if there are unresolved files.
2590 2592 """
2591 2593
2592 2594 if opts.get('rev') and node:
2593 2595 raise util.Abort(_("please specify just one revision"))
2594 2596 if not node:
2595 2597 node = opts.get('rev')
2596 2598
2597 2599 if not node:
2598 2600 branch = repo.changectx(None).branch()
2599 2601 bheads = repo.branchheads(branch)
2600 2602 if len(bheads) > 2:
2601 2603 raise util.Abort(_(
2602 2604 'branch \'%s\' has %d heads - '
2603 2605 'please merge with an explicit rev\n'
2604 2606 '(run \'hg heads .\' to see heads)')
2605 2607 % (branch, len(bheads)))
2606 2608
2607 2609 parent = repo.dirstate.parents()[0]
2608 2610 if len(bheads) == 1:
2609 2611 if len(repo.heads()) > 1:
2610 2612 raise util.Abort(_(
2611 2613 'branch \'%s\' has one head - '
2612 2614 'please merge with an explicit rev\n'
2613 2615 '(run \'hg heads\' to see all heads)')
2614 2616 % branch)
2615 2617 msg = _('there is nothing to merge')
2616 2618 if parent != repo.lookup(repo[None].branch()):
2617 2619 msg = _('%s - use "hg update" instead') % msg
2618 2620 raise util.Abort(msg)
2619 2621
2620 2622 if parent not in bheads:
2621 2623 raise util.Abort(_('working dir not at a head rev - '
2622 2624 'use "hg update" or merge with an explicit rev'))
2623 2625 node = parent == bheads[0] and bheads[-1] or bheads[0]
2624 2626
2625 2627 if opts.get('preview'):
2626 2628 # find nodes that are ancestors of p2 but not of p1
2627 2629 p1 = repo.lookup('.')
2628 2630 p2 = repo.lookup(node)
2629 2631 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2630 2632
2631 2633 displayer = cmdutil.show_changeset(ui, repo, opts)
2632 2634 for node in nodes:
2633 2635 displayer.show(repo[node])
2634 2636 displayer.close()
2635 2637 return 0
2636 2638
2637 2639 try:
2638 2640 # ui.forcemerge is an internal variable, do not document
2639 2641 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2640 2642 return hg.merge(repo, node, force=opts.get('force'))
2641 2643 finally:
2642 2644 ui.setconfig('ui', 'forcemerge', '')
2643 2645
2644 2646 def outgoing(ui, repo, dest=None, **opts):
2645 2647 """show changesets not found in the destination
2646 2648
2647 2649 Show changesets not found in the specified destination repository
2648 2650 or the default push location. These are the changesets that would
2649 2651 be pushed if a push was requested.
2650 2652
2651 2653 See pull for details of valid destination formats.
2652 2654
2653 2655 Returns 0 if there are outgoing changes, 1 otherwise.
2654 2656 """
2655 2657 ret = hg.outgoing(ui, repo, dest, opts)
2656 2658 return ret
2657 2659
2658 2660 def parents(ui, repo, file_=None, **opts):
2659 2661 """show the parents of the working directory or revision
2660 2662
2661 2663 Print the working directory's parent revisions. If a revision is
2662 2664 given via -r/--rev, the parent of that revision will be printed.
2663 2665 If a file argument is given, the revision in which the file was
2664 2666 last changed (before the working directory revision or the
2665 2667 argument to --rev if given) is printed.
2666 2668
2667 2669 Returns 0 on success.
2668 2670 """
2669 2671 rev = opts.get('rev')
2670 2672 if rev:
2671 2673 ctx = repo[rev]
2672 2674 else:
2673 2675 ctx = repo[None]
2674 2676
2675 2677 if file_:
2676 2678 m = cmdutil.match(repo, (file_,), opts)
2677 2679 if m.anypats() or len(m.files()) != 1:
2678 2680 raise util.Abort(_('can only specify an explicit filename'))
2679 2681 file_ = m.files()[0]
2680 2682 filenodes = []
2681 2683 for cp in ctx.parents():
2682 2684 if not cp:
2683 2685 continue
2684 2686 try:
2685 2687 filenodes.append(cp.filenode(file_))
2686 2688 except error.LookupError:
2687 2689 pass
2688 2690 if not filenodes:
2689 2691 raise util.Abort(_("'%s' not found in manifest!") % file_)
2690 2692 fl = repo.file(file_)
2691 2693 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2692 2694 else:
2693 2695 p = [cp.node() for cp in ctx.parents()]
2694 2696
2695 2697 displayer = cmdutil.show_changeset(ui, repo, opts)
2696 2698 for n in p:
2697 2699 if n != nullid:
2698 2700 displayer.show(repo[n])
2699 2701 displayer.close()
2700 2702
2701 2703 def paths(ui, repo, search=None):
2702 2704 """show aliases for remote repositories
2703 2705
2704 2706 Show definition of symbolic path name NAME. If no name is given,
2705 2707 show definition of all available names.
2706 2708
2707 2709 Path names are defined in the [paths] section of your
2708 2710 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
2709 2711 repository, ``.hg/hgrc`` is used, too.
2710 2712
2711 2713 The path names ``default`` and ``default-push`` have a special
2712 2714 meaning. When performing a push or pull operation, they are used
2713 2715 as fallbacks if no location is specified on the command-line.
2714 2716 When ``default-push`` is set, it will be used for push and
2715 2717 ``default`` will be used for pull; otherwise ``default`` is used
2716 2718 as the fallback for both. When cloning a repository, the clone
2717 2719 source is written as ``default`` in ``.hg/hgrc``. Note that
2718 2720 ``default`` and ``default-push`` apply to all inbound (e.g.
2719 2721 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2720 2722 :hg:`bundle`) operations.
2721 2723
2722 2724 See :hg:`help urls` for more information.
2723 2725
2724 2726 Returns 0 on success.
2725 2727 """
2726 2728 if search:
2727 2729 for name, path in ui.configitems("paths"):
2728 2730 if name == search:
2729 2731 ui.write("%s\n" % url.hidepassword(path))
2730 2732 return
2731 2733 ui.warn(_("not found!\n"))
2732 2734 return 1
2733 2735 else:
2734 2736 for name, path in ui.configitems("paths"):
2735 2737 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2736 2738
2737 2739 def postincoming(ui, repo, modheads, optupdate, checkout):
2738 2740 if modheads == 0:
2739 2741 return
2740 2742 if optupdate:
2741 2743 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2742 2744 return hg.update(repo, checkout)
2743 2745 else:
2744 2746 ui.status(_("not updating, since new heads added\n"))
2745 2747 if modheads > 1:
2746 2748 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2747 2749 else:
2748 2750 ui.status(_("(run 'hg update' to get a working copy)\n"))
2749 2751
2750 2752 def pull(ui, repo, source="default", **opts):
2751 2753 """pull changes from the specified source
2752 2754
2753 2755 Pull changes from a remote repository to a local one.
2754 2756
2755 2757 This finds all changes from the repository at the specified path
2756 2758 or URL and adds them to a local repository (the current one unless
2757 2759 -R is specified). By default, this does not update the copy of the
2758 2760 project in the working directory.
2759 2761
2760 2762 Use :hg:`incoming` if you want to see what would have been added
2761 2763 by a pull at the time you issued this command. If you then decide
2762 2764 to add those changes to the repository, you should use :hg:`pull
2763 2765 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
2764 2766
2765 2767 If SOURCE is omitted, the 'default' path will be used.
2766 2768 See :hg:`help urls` for more information.
2767 2769
2768 2770 Returns 0 on success, 1 if an update had unresolved files.
2769 2771 """
2770 2772 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2771 2773 other = hg.repository(hg.remoteui(repo, opts), source)
2772 2774 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2773 2775 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2774 2776 if revs:
2775 2777 try:
2776 2778 revs = [other.lookup(rev) for rev in revs]
2777 2779 except error.CapabilityError:
2778 2780 err = _("other repository doesn't support revision lookup, "
2779 2781 "so a rev cannot be specified.")
2780 2782 raise util.Abort(err)
2781 2783
2782 2784 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2783 2785 if checkout:
2784 2786 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2785 2787 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2786 2788
2787 2789 def push(ui, repo, dest=None, **opts):
2788 2790 """push changes to the specified destination
2789 2791
2790 2792 Push changesets from the local repository to the specified
2791 2793 destination.
2792 2794
2793 2795 This operation is symmetrical to pull: it is identical to a pull
2794 2796 in the destination repository from the current one.
2795 2797
2796 2798 By default, push will not allow creation of new heads at the
2797 2799 destination, since multiple heads would make it unclear which head
2798 2800 to use. In this situation, it is recommended to pull and merge
2799 2801 before pushing.
2800 2802
2801 2803 Use --new-branch if you want to allow push to create a new named
2802 2804 branch that is not present at the destination. This allows you to
2803 2805 only create a new branch without forcing other changes.
2804 2806
2805 2807 Use -f/--force to override the default behavior and push all
2806 2808 changesets on all branches.
2807 2809
2808 2810 If -r/--rev is used, the specified revision and all its ancestors
2809 2811 will be pushed to the remote repository.
2810 2812
2811 2813 Please see :hg:`help urls` for important details about ``ssh://``
2812 2814 URLs. If DESTINATION is omitted, a default path will be used.
2813 2815
2814 2816 Returns 0 if push was successful, 1 if nothing to push.
2815 2817 """
2816 2818 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2817 2819 dest, branches = hg.parseurl(dest, opts.get('branch'))
2818 2820 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2819 2821 other = hg.repository(hg.remoteui(repo, opts), dest)
2820 2822 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2821 2823 if revs:
2822 2824 revs = [repo.lookup(rev) for rev in revs]
2823 2825
2824 2826 # push subrepos depth-first for coherent ordering
2825 2827 c = repo['']
2826 2828 subs = c.substate # only repos that are committed
2827 2829 for s in sorted(subs):
2828 2830 if not c.sub(s).push(opts.get('force')):
2829 2831 return False
2830 2832
2831 2833 r = repo.push(other, opts.get('force'), revs=revs,
2832 2834 newbranch=opts.get('new_branch'))
2833 2835 return r == 0
2834 2836
2835 2837 def recover(ui, repo):
2836 2838 """roll back an interrupted transaction
2837 2839
2838 2840 Recover from an interrupted commit or pull.
2839 2841
2840 2842 This command tries to fix the repository status after an
2841 2843 interrupted operation. It should only be necessary when Mercurial
2842 2844 suggests it.
2843 2845
2844 2846 Returns 0 if successful, 1 if nothing to recover or verify fails.
2845 2847 """
2846 2848 if repo.recover():
2847 2849 return hg.verify(repo)
2848 2850 return 1
2849 2851
2850 2852 def remove(ui, repo, *pats, **opts):
2851 2853 """remove the specified files on the next commit
2852 2854
2853 2855 Schedule the indicated files for removal from the repository.
2854 2856
2855 2857 This only removes files from the current branch, not from the
2856 2858 entire project history. -A/--after can be used to remove only
2857 2859 files that have already been deleted, -f/--force can be used to
2858 2860 force deletion, and -Af can be used to remove files from the next
2859 2861 revision without deleting them from the working directory.
2860 2862
2861 2863 The following table details the behavior of remove for different
2862 2864 file states (columns) and option combinations (rows). The file
2863 2865 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2864 2866 reported by :hg:`status`). The actions are Warn, Remove (from
2865 2867 branch) and Delete (from disk)::
2866 2868
2867 2869 A C M !
2868 2870 none W RD W R
2869 2871 -f R RD RD R
2870 2872 -A W W W R
2871 2873 -Af R R R R
2872 2874
2873 2875 This command schedules the files to be removed at the next commit.
2874 2876 To undo a remove before that, see :hg:`revert`.
2875 2877
2876 2878 Returns 0 on success, 1 if any warnings encountered.
2877 2879 """
2878 2880
2879 2881 ret = 0
2880 2882 after, force = opts.get('after'), opts.get('force')
2881 2883 if not pats and not after:
2882 2884 raise util.Abort(_('no files specified'))
2883 2885
2884 2886 m = cmdutil.match(repo, pats, opts)
2885 2887 s = repo.status(match=m, clean=True)
2886 2888 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2887 2889
2888 2890 for f in m.files():
2889 2891 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2890 2892 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2891 2893 ret = 1
2892 2894
2893 2895 if force:
2894 2896 remove, forget = modified + deleted + clean, added
2895 2897 elif after:
2896 2898 remove, forget = deleted, []
2897 2899 for f in modified + added + clean:
2898 2900 ui.warn(_('not removing %s: file still exists (use -f'
2899 2901 ' to force removal)\n') % m.rel(f))
2900 2902 ret = 1
2901 2903 else:
2902 2904 remove, forget = deleted + clean, []
2903 2905 for f in modified:
2904 2906 ui.warn(_('not removing %s: file is modified (use -f'
2905 2907 ' to force removal)\n') % m.rel(f))
2906 2908 ret = 1
2907 2909 for f in added:
2908 2910 ui.warn(_('not removing %s: file has been marked for add (use -f'
2909 2911 ' to force removal)\n') % m.rel(f))
2910 2912 ret = 1
2911 2913
2912 2914 for f in sorted(remove + forget):
2913 2915 if ui.verbose or not m.exact(f):
2914 2916 ui.status(_('removing %s\n') % m.rel(f))
2915 2917
2916 2918 repo[None].forget(forget)
2917 2919 repo[None].remove(remove, unlink=not after)
2918 2920 return ret
2919 2921
2920 2922 def rename(ui, repo, *pats, **opts):
2921 2923 """rename files; equivalent of copy + remove
2922 2924
2923 2925 Mark dest as copies of sources; mark sources for deletion. If dest
2924 2926 is a directory, copies are put in that directory. If dest is a
2925 2927 file, there can only be one source.
2926 2928
2927 2929 By default, this command copies the contents of files as they
2928 2930 exist in the working directory. If invoked with -A/--after, the
2929 2931 operation is recorded, but no copying is performed.
2930 2932
2931 2933 This command takes effect at the next commit. To undo a rename
2932 2934 before that, see :hg:`revert`.
2933 2935
2934 2936 Returns 0 on success, 1 if errors are encountered.
2935 2937 """
2936 2938 wlock = repo.wlock(False)
2937 2939 try:
2938 2940 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2939 2941 finally:
2940 2942 wlock.release()
2941 2943
2942 2944 def resolve(ui, repo, *pats, **opts):
2943 2945 """redo merges or set/view the merge status of files
2944 2946
2945 2947 Merges with unresolved conflicts are often the result of
2946 2948 non-interactive merging using the ``internal:merge`` configuration
2947 2949 setting, or a command-line merge tool like ``diff3``. The resolve
2948 2950 command is used to manage the files involved in a merge, after
2949 2951 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
2950 2952 working directory must have two parents).
2951 2953
2952 2954 The resolve command can be used in the following ways:
2953 2955
2954 2956 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
2955 2957 files, discarding any previous merge attempts. Re-merging is not
2956 2958 performed for files already marked as resolved. Use ``--all/-a``
2957 2959 to selects all unresolved files. ``--tool`` can be used to specify
2958 2960 the merge tool used for the given files. It overrides the HGMERGE
2959 2961 environment variable and your configuration files.
2960 2962
2961 2963 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
2962 2964 (e.g. after having manually fixed-up the files). The default is
2963 2965 to mark all unresolved files.
2964 2966
2965 2967 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
2966 2968 default is to mark all resolved files.
2967 2969
2968 2970 - :hg:`resolve -l`: list files which had or still have conflicts.
2969 2971 In the printed list, ``U`` = unresolved and ``R`` = resolved.
2970 2972
2971 2973 Note that Mercurial will not let you commit files with unresolved
2972 2974 merge conflicts. You must use :hg:`resolve -m ...` before you can
2973 2975 commit after a conflicting merge.
2974 2976
2975 2977 Returns 0 on success, 1 if any files fail a resolve attempt.
2976 2978 """
2977 2979
2978 2980 all, mark, unmark, show, nostatus = \
2979 2981 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2980 2982
2981 2983 if (show and (mark or unmark)) or (mark and unmark):
2982 2984 raise util.Abort(_("too many options specified"))
2983 2985 if pats and all:
2984 2986 raise util.Abort(_("can't specify --all and patterns"))
2985 2987 if not (all or pats or show or mark or unmark):
2986 2988 raise util.Abort(_('no files or directories specified; '
2987 2989 'use --all to remerge all files'))
2988 2990
2989 2991 ms = mergemod.mergestate(repo)
2990 2992 m = cmdutil.match(repo, pats, opts)
2991 2993 ret = 0
2992 2994
2993 2995 for f in ms:
2994 2996 if m(f):
2995 2997 if show:
2996 2998 if nostatus:
2997 2999 ui.write("%s\n" % f)
2998 3000 else:
2999 3001 ui.write("%s %s\n" % (ms[f].upper(), f),
3000 3002 label='resolve.' +
3001 3003 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3002 3004 elif mark:
3003 3005 ms.mark(f, "r")
3004 3006 elif unmark:
3005 3007 ms.mark(f, "u")
3006 3008 else:
3007 3009 wctx = repo[None]
3008 3010 mctx = wctx.parents()[-1]
3009 3011
3010 3012 # backup pre-resolve (merge uses .orig for its own purposes)
3011 3013 a = repo.wjoin(f)
3012 3014 util.copyfile(a, a + ".resolve")
3013 3015
3014 3016 try:
3015 3017 # resolve file
3016 3018 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3017 3019 if ms.resolve(f, wctx, mctx):
3018 3020 ret = 1
3019 3021 finally:
3020 3022 ui.setconfig('ui', 'forcemerge', '')
3021 3023
3022 3024 # replace filemerge's .orig file with our resolve file
3023 3025 util.rename(a + ".resolve", a + ".orig")
3024 3026
3025 3027 ms.commit()
3026 3028 return ret
3027 3029
3028 3030 def revert(ui, repo, *pats, **opts):
3029 3031 """restore individual files or directories to an earlier state
3030 3032
3031 3033 .. note::
3032 3034 This command is most likely not what you are looking for.
3033 3035 Revert will partially overwrite content in the working
3034 3036 directory without changing the working directory parents. Use
3035 3037 :hg:`update -r rev` to check out earlier revisions, or
3036 3038 :hg:`update --clean .` to undo a merge which has added another
3037 3039 parent.
3038 3040
3039 3041 With no revision specified, revert the named files or directories
3040 3042 to the contents they had in the parent of the working directory.
3041 3043 This restores the contents of the affected files to an unmodified
3042 3044 state and unschedules adds, removes, copies, and renames. If the
3043 3045 working directory has two parents, you must explicitly specify a
3044 3046 revision.
3045 3047
3046 3048 Using the -r/--rev option, revert the given files or directories
3047 3049 to their contents as of a specific revision. This can be helpful
3048 3050 to "roll back" some or all of an earlier change. See :hg:`help
3049 3051 dates` for a list of formats valid for -d/--date.
3050 3052
3051 3053 Revert modifies the working directory. It does not commit any
3052 3054 changes, or change the parent of the working directory. If you
3053 3055 revert to a revision other than the parent of the working
3054 3056 directory, the reverted files will thus appear modified
3055 3057 afterwards.
3056 3058
3057 3059 If a file has been deleted, it is restored. If the executable mode
3058 3060 of a file was changed, it is reset.
3059 3061
3060 3062 If names are given, all files matching the names are reverted.
3061 3063 If no arguments are given, no files are reverted.
3062 3064
3063 3065 Modified files are saved with a .orig suffix before reverting.
3064 3066 To disable these backups, use --no-backup.
3065 3067
3066 3068 Returns 0 on success.
3067 3069 """
3068 3070
3069 3071 if opts.get("date"):
3070 3072 if opts.get("rev"):
3071 3073 raise util.Abort(_("you can't specify a revision and a date"))
3072 3074 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
3073 3075
3074 3076 if not pats and not opts.get('all'):
3075 3077 raise util.Abort(_('no files or directories specified; '
3076 3078 'use --all to revert the whole repo'))
3077 3079
3078 3080 parent, p2 = repo.dirstate.parents()
3079 3081 if not opts.get('rev') and p2 != nullid:
3080 3082 raise util.Abort(_('uncommitted merge - please provide a '
3081 3083 'specific revision'))
3082 3084 ctx = repo[opts.get('rev')]
3083 3085 node = ctx.node()
3084 3086 mf = ctx.manifest()
3085 3087 if node == parent:
3086 3088 pmf = mf
3087 3089 else:
3088 3090 pmf = None
3089 3091
3090 3092 # need all matching names in dirstate and manifest of target rev,
3091 3093 # so have to walk both. do not print errors if files exist in one
3092 3094 # but not other.
3093 3095
3094 3096 names = {}
3095 3097
3096 3098 wlock = repo.wlock()
3097 3099 try:
3098 3100 # walk dirstate.
3099 3101
3100 3102 m = cmdutil.match(repo, pats, opts)
3101 3103 m.bad = lambda x, y: False
3102 3104 for abs in repo.walk(m):
3103 3105 names[abs] = m.rel(abs), m.exact(abs)
3104 3106
3105 3107 # walk target manifest.
3106 3108
3107 3109 def badfn(path, msg):
3108 3110 if path in names:
3109 3111 return
3110 3112 path_ = path + '/'
3111 3113 for f in names:
3112 3114 if f.startswith(path_):
3113 3115 return
3114 3116 ui.warn("%s: %s\n" % (m.rel(path), msg))
3115 3117
3116 3118 m = cmdutil.match(repo, pats, opts)
3117 3119 m.bad = badfn
3118 3120 for abs in repo[node].walk(m):
3119 3121 if abs not in names:
3120 3122 names[abs] = m.rel(abs), m.exact(abs)
3121 3123
3122 3124 m = cmdutil.matchfiles(repo, names)
3123 3125 changes = repo.status(match=m)[:4]
3124 3126 modified, added, removed, deleted = map(set, changes)
3125 3127
3126 3128 # if f is a rename, also revert the source
3127 3129 cwd = repo.getcwd()
3128 3130 for f in added:
3129 3131 src = repo.dirstate.copied(f)
3130 3132 if src and src not in names and repo.dirstate[src] == 'r':
3131 3133 removed.add(src)
3132 3134 names[src] = (repo.pathto(src, cwd), True)
3133 3135
3134 3136 def removeforget(abs):
3135 3137 if repo.dirstate[abs] == 'a':
3136 3138 return _('forgetting %s\n')
3137 3139 return _('removing %s\n')
3138 3140
3139 3141 revert = ([], _('reverting %s\n'))
3140 3142 add = ([], _('adding %s\n'))
3141 3143 remove = ([], removeforget)
3142 3144 undelete = ([], _('undeleting %s\n'))
3143 3145
3144 3146 disptable = (
3145 3147 # dispatch table:
3146 3148 # file state
3147 3149 # action if in target manifest
3148 3150 # action if not in target manifest
3149 3151 # make backup if in target manifest
3150 3152 # make backup if not in target manifest
3151 3153 (modified, revert, remove, True, True),
3152 3154 (added, revert, remove, True, False),
3153 3155 (removed, undelete, None, False, False),
3154 3156 (deleted, revert, remove, False, False),
3155 3157 )
3156 3158
3157 3159 for abs, (rel, exact) in sorted(names.items()):
3158 3160 mfentry = mf.get(abs)
3159 3161 target = repo.wjoin(abs)
3160 3162 def handle(xlist, dobackup):
3161 3163 xlist[0].append(abs)
3162 3164 if (dobackup and not opts.get('no_backup') and
3163 3165 os.path.lexists(target)):
3164 3166 bakname = "%s.orig" % rel
3165 3167 ui.note(_('saving current version of %s as %s\n') %
3166 3168 (rel, bakname))
3167 3169 if not opts.get('dry_run'):
3168 3170 util.rename(target, bakname)
3169 3171 if ui.verbose or not exact:
3170 3172 msg = xlist[1]
3171 3173 if not isinstance(msg, basestring):
3172 3174 msg = msg(abs)
3173 3175 ui.status(msg % rel)
3174 3176 for table, hitlist, misslist, backuphit, backupmiss in disptable:
3175 3177 if abs not in table:
3176 3178 continue
3177 3179 # file has changed in dirstate
3178 3180 if mfentry:
3179 3181 handle(hitlist, backuphit)
3180 3182 elif misslist is not None:
3181 3183 handle(misslist, backupmiss)
3182 3184 break
3183 3185 else:
3184 3186 if abs not in repo.dirstate:
3185 3187 if mfentry:
3186 3188 handle(add, True)
3187 3189 elif exact:
3188 3190 ui.warn(_('file not managed: %s\n') % rel)
3189 3191 continue
3190 3192 # file has not changed in dirstate
3191 3193 if node == parent:
3192 3194 if exact:
3193 3195 ui.warn(_('no changes needed to %s\n') % rel)
3194 3196 continue
3195 3197 if pmf is None:
3196 3198 # only need parent manifest in this unlikely case,
3197 3199 # so do not read by default
3198 3200 pmf = repo[parent].manifest()
3199 3201 if abs in pmf:
3200 3202 if mfentry:
3201 3203 # if version of file is same in parent and target
3202 3204 # manifests, do nothing
3203 3205 if (pmf[abs] != mfentry or
3204 3206 pmf.flags(abs) != mf.flags(abs)):
3205 3207 handle(revert, False)
3206 3208 else:
3207 3209 handle(remove, False)
3208 3210
3209 3211 if not opts.get('dry_run'):
3210 3212 def checkout(f):
3211 3213 fc = ctx[f]
3212 3214 repo.wwrite(f, fc.data(), fc.flags())
3213 3215
3214 3216 audit_path = util.path_auditor(repo.root)
3215 3217 for f in remove[0]:
3216 3218 if repo.dirstate[f] == 'a':
3217 3219 repo.dirstate.forget(f)
3218 3220 continue
3219 3221 audit_path(f)
3220 3222 try:
3221 3223 util.unlink(repo.wjoin(f))
3222 3224 except OSError:
3223 3225 pass
3224 3226 repo.dirstate.remove(f)
3225 3227
3226 3228 normal = None
3227 3229 if node == parent:
3228 3230 # We're reverting to our parent. If possible, we'd like status
3229 3231 # to report the file as clean. We have to use normallookup for
3230 3232 # merges to avoid losing information about merged/dirty files.
3231 3233 if p2 != nullid:
3232 3234 normal = repo.dirstate.normallookup
3233 3235 else:
3234 3236 normal = repo.dirstate.normal
3235 3237 for f in revert[0]:
3236 3238 checkout(f)
3237 3239 if normal:
3238 3240 normal(f)
3239 3241
3240 3242 for f in add[0]:
3241 3243 checkout(f)
3242 3244 repo.dirstate.add(f)
3243 3245
3244 3246 normal = repo.dirstate.normallookup
3245 3247 if node == parent and p2 == nullid:
3246 3248 normal = repo.dirstate.normal
3247 3249 for f in undelete[0]:
3248 3250 checkout(f)
3249 3251 normal(f)
3250 3252
3251 3253 finally:
3252 3254 wlock.release()
3253 3255
3254 3256 def rollback(ui, repo, **opts):
3255 3257 """roll back the last transaction (dangerous)
3256 3258
3257 3259 This command should be used with care. There is only one level of
3258 3260 rollback, and there is no way to undo a rollback. It will also
3259 3261 restore the dirstate at the time of the last transaction, losing
3260 3262 any dirstate changes since that time. This command does not alter
3261 3263 the working directory.
3262 3264
3263 3265 Transactions are used to encapsulate the effects of all commands
3264 3266 that create new changesets or propagate existing changesets into a
3265 3267 repository. For example, the following commands are transactional,
3266 3268 and their effects can be rolled back:
3267 3269
3268 3270 - commit
3269 3271 - import
3270 3272 - pull
3271 3273 - push (with this repository as the destination)
3272 3274 - unbundle
3273 3275
3274 3276 This command is not intended for use on public repositories. Once
3275 3277 changes are visible for pull by other users, rolling a transaction
3276 3278 back locally is ineffective (someone else may already have pulled
3277 3279 the changes). Furthermore, a race is possible with readers of the
3278 3280 repository; for example an in-progress pull from the repository
3279 3281 may fail if a rollback is performed.
3280 3282
3281 3283 Returns 0 on success, 1 if no rollback data is available.
3282 3284 """
3283 3285 return repo.rollback(opts.get('dry_run'))
3284 3286
3285 3287 def root(ui, repo):
3286 3288 """print the root (top) of the current working directory
3287 3289
3288 3290 Print the root directory of the current repository.
3289 3291
3290 3292 Returns 0 on success.
3291 3293 """
3292 3294 ui.write(repo.root + "\n")
3293 3295
3294 3296 def serve(ui, repo, **opts):
3295 3297 """start stand-alone webserver
3296 3298
3297 3299 Start a local HTTP repository browser and pull server. You can use
3298 3300 this for ad-hoc sharing and browing of repositories. It is
3299 3301 recommended to use a real web server to serve a repository for
3300 3302 longer periods of time.
3301 3303
3302 3304 Please note that the server does not implement access control.
3303 3305 This means that, by default, anybody can read from the server and
3304 3306 nobody can write to it by default. Set the ``web.allow_push``
3305 3307 option to ``*`` to allow everybody to push to the server. You
3306 3308 should use a real web server if you need to authenticate users.
3307 3309
3308 3310 By default, the server logs accesses to stdout and errors to
3309 3311 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3310 3312 files.
3311 3313
3312 3314 To have the server choose a free port number to listen on, specify
3313 3315 a port number of 0; in this case, the server will print the port
3314 3316 number it uses.
3315 3317
3316 3318 Returns 0 on success.
3317 3319 """
3318 3320
3319 3321 if opts["stdio"]:
3320 3322 if repo is None:
3321 3323 raise error.RepoError(_("There is no Mercurial repository here"
3322 3324 " (.hg not found)"))
3323 3325 s = sshserver.sshserver(ui, repo)
3324 3326 s.serve_forever()
3325 3327
3326 3328 # this way we can check if something was given in the command-line
3327 3329 if opts.get('port'):
3328 3330 opts['port'] = util.getport(opts.get('port'))
3329 3331
3330 3332 baseui = repo and repo.baseui or ui
3331 3333 optlist = ("name templates style address port prefix ipv6"
3332 3334 " accesslog errorlog certificate encoding")
3333 3335 for o in optlist.split():
3334 3336 val = opts.get(o, '')
3335 3337 if val in (None, ''): # should check against default options instead
3336 3338 continue
3337 3339 baseui.setconfig("web", o, val)
3338 3340 if repo and repo.ui != baseui:
3339 3341 repo.ui.setconfig("web", o, val)
3340 3342
3341 3343 o = opts.get('web_conf') or opts.get('webdir_conf')
3342 3344 if not o:
3343 3345 if not repo:
3344 3346 raise error.RepoError(_("There is no Mercurial repository"
3345 3347 " here (.hg not found)"))
3346 3348 o = repo.root
3347 3349
3348 3350 app = hgweb.hgweb(o, baseui=ui)
3349 3351
3350 3352 class service(object):
3351 3353 def init(self):
3352 3354 util.set_signal_handler()
3353 3355 self.httpd = hgweb.server.create_server(ui, app)
3354 3356
3355 3357 if opts['port'] and not ui.verbose:
3356 3358 return
3357 3359
3358 3360 if self.httpd.prefix:
3359 3361 prefix = self.httpd.prefix.strip('/') + '/'
3360 3362 else:
3361 3363 prefix = ''
3362 3364
3363 3365 port = ':%d' % self.httpd.port
3364 3366 if port == ':80':
3365 3367 port = ''
3366 3368
3367 3369 bindaddr = self.httpd.addr
3368 3370 if bindaddr == '0.0.0.0':
3369 3371 bindaddr = '*'
3370 3372 elif ':' in bindaddr: # IPv6
3371 3373 bindaddr = '[%s]' % bindaddr
3372 3374
3373 3375 fqaddr = self.httpd.fqaddr
3374 3376 if ':' in fqaddr:
3375 3377 fqaddr = '[%s]' % fqaddr
3376 3378 if opts['port']:
3377 3379 write = ui.status
3378 3380 else:
3379 3381 write = ui.write
3380 3382 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3381 3383 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3382 3384
3383 3385 def run(self):
3384 3386 self.httpd.serve_forever()
3385 3387
3386 3388 service = service()
3387 3389
3388 3390 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3389 3391
3390 3392 def status(ui, repo, *pats, **opts):
3391 3393 """show changed files in the working directory
3392 3394
3393 3395 Show status of files in the repository. If names are given, only
3394 3396 files that match are shown. Files that are clean or ignored or
3395 3397 the source of a copy/move operation, are not listed unless
3396 3398 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3397 3399 Unless options described with "show only ..." are given, the
3398 3400 options -mardu are used.
3399 3401
3400 3402 Option -q/--quiet hides untracked (unknown and ignored) files
3401 3403 unless explicitly requested with -u/--unknown or -i/--ignored.
3402 3404
3403 3405 .. note::
3404 3406 status may appear to disagree with diff if permissions have
3405 3407 changed or a merge has occurred. The standard diff format does
3406 3408 not report permission changes and diff only reports changes
3407 3409 relative to one merge parent.
3408 3410
3409 3411 If one revision is given, it is used as the base revision.
3410 3412 If two revisions are given, the differences between them are
3411 3413 shown. The --change option can also be used as a shortcut to list
3412 3414 the changed files of a revision from its first parent.
3413 3415
3414 3416 The codes used to show the status of files are::
3415 3417
3416 3418 M = modified
3417 3419 A = added
3418 3420 R = removed
3419 3421 C = clean
3420 3422 ! = missing (deleted by non-hg command, but still tracked)
3421 3423 ? = not tracked
3422 3424 I = ignored
3423 3425 = origin of the previous file listed as A (added)
3424 3426
3425 3427 Returns 0 on success.
3426 3428 """
3427 3429
3428 3430 revs = opts.get('rev')
3429 3431 change = opts.get('change')
3430 3432
3431 3433 if revs and change:
3432 3434 msg = _('cannot specify --rev and --change at the same time')
3433 3435 raise util.Abort(msg)
3434 3436 elif change:
3435 3437 node2 = repo.lookup(change)
3436 3438 node1 = repo[node2].parents()[0].node()
3437 3439 else:
3438 3440 node1, node2 = cmdutil.revpair(repo, revs)
3439 3441
3440 3442 cwd = (pats and repo.getcwd()) or ''
3441 3443 end = opts.get('print0') and '\0' or '\n'
3442 3444 copy = {}
3443 3445 states = 'modified added removed deleted unknown ignored clean'.split()
3444 3446 show = [k for k in states if opts.get(k)]
3445 3447 if opts.get('all'):
3446 3448 show += ui.quiet and (states[:4] + ['clean']) or states
3447 3449 if not show:
3448 3450 show = ui.quiet and states[:4] or states[:5]
3449 3451
3450 3452 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3451 3453 'ignored' in show, 'clean' in show, 'unknown' in show,
3452 3454 opts.get('subrepos'))
3453 3455 changestates = zip(states, 'MAR!?IC', stat)
3454 3456
3455 3457 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3456 3458 ctxn = repo[nullid]
3457 3459 ctx1 = repo[node1]
3458 3460 ctx2 = repo[node2]
3459 3461 added = stat[1]
3460 3462 if node2 is None:
3461 3463 added = stat[0] + stat[1] # merged?
3462 3464
3463 3465 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3464 3466 if k in added:
3465 3467 copy[k] = v
3466 3468 elif v in added:
3467 3469 copy[v] = k
3468 3470
3469 3471 for state, char, files in changestates:
3470 3472 if state in show:
3471 3473 format = "%s %%s%s" % (char, end)
3472 3474 if opts.get('no_status'):
3473 3475 format = "%%s%s" % end
3474 3476
3475 3477 for f in files:
3476 3478 ui.write(format % repo.pathto(f, cwd),
3477 3479 label='status.' + state)
3478 3480 if f in copy:
3479 3481 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3480 3482 label='status.copied')
3481 3483
3482 3484 def summary(ui, repo, **opts):
3483 3485 """summarize working directory state
3484 3486
3485 3487 This generates a brief summary of the working directory state,
3486 3488 including parents, branch, commit status, and available updates.
3487 3489
3488 3490 With the --remote option, this will check the default paths for
3489 3491 incoming and outgoing changes. This can be time-consuming.
3490 3492
3491 3493 Returns 0 on success.
3492 3494 """
3493 3495
3494 3496 ctx = repo[None]
3495 3497 parents = ctx.parents()
3496 3498 pnode = parents[0].node()
3497 3499
3498 3500 for p in parents:
3499 3501 # label with log.changeset (instead of log.parent) since this
3500 3502 # shows a working directory parent *changeset*:
3501 3503 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3502 3504 label='log.changeset')
3503 3505 ui.write(' '.join(p.tags()), label='log.tag')
3504 3506 if p.rev() == -1:
3505 3507 if not len(repo):
3506 3508 ui.write(_(' (empty repository)'))
3507 3509 else:
3508 3510 ui.write(_(' (no revision checked out)'))
3509 3511 ui.write('\n')
3510 3512 if p.description():
3511 3513 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3512 3514 label='log.summary')
3513 3515
3514 3516 branch = ctx.branch()
3515 3517 bheads = repo.branchheads(branch)
3516 3518 m = _('branch: %s\n') % branch
3517 3519 if branch != 'default':
3518 3520 ui.write(m, label='log.branch')
3519 3521 else:
3520 3522 ui.status(m, label='log.branch')
3521 3523
3522 3524 st = list(repo.status(unknown=True))[:6]
3523 3525
3524 3526 c = repo.dirstate.copies()
3525 3527 copied, renamed = [], []
3526 3528 for d, s in c.iteritems():
3527 3529 if s in st[2]:
3528 3530 st[2].remove(s)
3529 3531 renamed.append(d)
3530 3532 else:
3531 3533 copied.append(d)
3532 3534 if d in st[1]:
3533 3535 st[1].remove(d)
3534 3536 st.insert(3, renamed)
3535 3537 st.insert(4, copied)
3536 3538
3537 3539 ms = mergemod.mergestate(repo)
3538 3540 st.append([f for f in ms if ms[f] == 'u'])
3539 3541
3540 3542 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3541 3543 st.append(subs)
3542 3544
3543 3545 labels = [ui.label(_('%d modified'), 'status.modified'),
3544 3546 ui.label(_('%d added'), 'status.added'),
3545 3547 ui.label(_('%d removed'), 'status.removed'),
3546 3548 ui.label(_('%d renamed'), 'status.copied'),
3547 3549 ui.label(_('%d copied'), 'status.copied'),
3548 3550 ui.label(_('%d deleted'), 'status.deleted'),
3549 3551 ui.label(_('%d unknown'), 'status.unknown'),
3550 3552 ui.label(_('%d ignored'), 'status.ignored'),
3551 3553 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3552 3554 ui.label(_('%d subrepos'), 'status.modified')]
3553 3555 t = []
3554 3556 for s, l in zip(st, labels):
3555 3557 if s:
3556 3558 t.append(l % len(s))
3557 3559
3558 3560 t = ', '.join(t)
3559 3561 cleanworkdir = False
3560 3562
3561 3563 if len(parents) > 1:
3562 3564 t += _(' (merge)')
3563 3565 elif branch != parents[0].branch():
3564 3566 t += _(' (new branch)')
3565 3567 elif (parents[0].extra().get('close') and
3566 3568 pnode in repo.branchheads(branch, closed=True)):
3567 3569 t += _(' (head closed)')
3568 3570 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
3569 3571 t += _(' (clean)')
3570 3572 cleanworkdir = True
3571 3573 elif pnode not in bheads:
3572 3574 t += _(' (new branch head)')
3573 3575
3574 3576 if cleanworkdir:
3575 3577 ui.status(_('commit: %s\n') % t.strip())
3576 3578 else:
3577 3579 ui.write(_('commit: %s\n') % t.strip())
3578 3580
3579 3581 # all ancestors of branch heads - all ancestors of parent = new csets
3580 3582 new = [0] * len(repo)
3581 3583 cl = repo.changelog
3582 3584 for a in [cl.rev(n) for n in bheads]:
3583 3585 new[a] = 1
3584 3586 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3585 3587 new[a] = 1
3586 3588 for a in [p.rev() for p in parents]:
3587 3589 if a >= 0:
3588 3590 new[a] = 0
3589 3591 for a in cl.ancestors(*[p.rev() for p in parents]):
3590 3592 new[a] = 0
3591 3593 new = sum(new)
3592 3594
3593 3595 if new == 0:
3594 3596 ui.status(_('update: (current)\n'))
3595 3597 elif pnode not in bheads:
3596 3598 ui.write(_('update: %d new changesets (update)\n') % new)
3597 3599 else:
3598 3600 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3599 3601 (new, len(bheads)))
3600 3602
3601 3603 if opts.get('remote'):
3602 3604 t = []
3603 3605 source, branches = hg.parseurl(ui.expandpath('default'))
3604 3606 other = hg.repository(hg.remoteui(repo, {}), source)
3605 3607 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3606 3608 ui.debug('comparing with %s\n' % url.hidepassword(source))
3607 3609 repo.ui.pushbuffer()
3608 3610 common, incoming, rheads = discovery.findcommonincoming(repo, other)
3609 3611 repo.ui.popbuffer()
3610 3612 if incoming:
3611 3613 t.append(_('1 or more incoming'))
3612 3614
3613 3615 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3614 3616 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3615 3617 other = hg.repository(hg.remoteui(repo, {}), dest)
3616 3618 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3617 3619 repo.ui.pushbuffer()
3618 3620 o = discovery.findoutgoing(repo, other)
3619 3621 repo.ui.popbuffer()
3620 3622 o = repo.changelog.nodesbetween(o, None)[0]
3621 3623 if o:
3622 3624 t.append(_('%d outgoing') % len(o))
3623 3625
3624 3626 if t:
3625 3627 ui.write(_('remote: %s\n') % (', '.join(t)))
3626 3628 else:
3627 3629 ui.status(_('remote: (synced)\n'))
3628 3630
3629 3631 def tag(ui, repo, name1, *names, **opts):
3630 3632 """add one or more tags for the current or given revision
3631 3633
3632 3634 Name a particular revision using <name>.
3633 3635
3634 3636 Tags are used to name particular revisions of the repository and are
3635 3637 very useful to compare different revisions, to go back to significant
3636 3638 earlier versions or to mark branch points as releases, etc.
3637 3639
3638 3640 If no revision is given, the parent of the working directory is
3639 3641 used, or tip if no revision is checked out.
3640 3642
3641 3643 To facilitate version control, distribution, and merging of tags,
3642 3644 they are stored as a file named ".hgtags" which is managed
3643 3645 similarly to other project files and can be hand-edited if
3644 3646 necessary. The file '.hg/localtags' is used for local tags (not
3645 3647 shared among repositories).
3646 3648
3647 3649 See :hg:`help dates` for a list of formats valid for -d/--date.
3648 3650
3649 3651 Since tag names have priority over branch names during revision
3650 3652 lookup, using an existing branch name as a tag name is discouraged.
3651 3653
3652 3654 Returns 0 on success.
3653 3655 """
3654 3656
3655 3657 rev_ = "."
3656 3658 names = [t.strip() for t in (name1,) + names]
3657 3659 if len(names) != len(set(names)):
3658 3660 raise util.Abort(_('tag names must be unique'))
3659 3661 for n in names:
3660 3662 if n in ['tip', '.', 'null']:
3661 3663 raise util.Abort(_('the name \'%s\' is reserved') % n)
3662 3664 if not n:
3663 3665 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
3664 3666 if opts.get('rev') and opts.get('remove'):
3665 3667 raise util.Abort(_("--rev and --remove are incompatible"))
3666 3668 if opts.get('rev'):
3667 3669 rev_ = opts['rev']
3668 3670 message = opts.get('message')
3669 3671 if opts.get('remove'):
3670 3672 expectedtype = opts.get('local') and 'local' or 'global'
3671 3673 for n in names:
3672 3674 if not repo.tagtype(n):
3673 3675 raise util.Abort(_('tag \'%s\' does not exist') % n)
3674 3676 if repo.tagtype(n) != expectedtype:
3675 3677 if expectedtype == 'global':
3676 3678 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3677 3679 else:
3678 3680 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3679 3681 rev_ = nullid
3680 3682 if not message:
3681 3683 # we don't translate commit messages
3682 3684 message = 'Removed tag %s' % ', '.join(names)
3683 3685 elif not opts.get('force'):
3684 3686 for n in names:
3685 3687 if n in repo.tags():
3686 3688 raise util.Abort(_('tag \'%s\' already exists '
3687 3689 '(use -f to force)') % n)
3688 3690 if not rev_ and repo.dirstate.parents()[1] != nullid:
3689 3691 raise util.Abort(_('uncommitted merge - please provide a '
3690 3692 'specific revision'))
3691 3693 r = repo[rev_].node()
3692 3694
3693 3695 if not message:
3694 3696 # we don't translate commit messages
3695 3697 message = ('Added tag %s for changeset %s' %
3696 3698 (', '.join(names), short(r)))
3697 3699
3698 3700 date = opts.get('date')
3699 3701 if date:
3700 3702 date = util.parsedate(date)
3701 3703
3702 3704 if opts.get('edit'):
3703 3705 message = ui.edit(message, ui.username())
3704 3706
3705 3707 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3706 3708
3707 3709 def tags(ui, repo):
3708 3710 """list repository tags
3709 3711
3710 3712 This lists both regular and local tags. When the -v/--verbose
3711 3713 switch is used, a third column "local" is printed for local tags.
3712 3714
3713 3715 Returns 0 on success.
3714 3716 """
3715 3717
3716 3718 hexfunc = ui.debugflag and hex or short
3717 3719 tagtype = ""
3718 3720
3719 3721 for t, n in reversed(repo.tagslist()):
3720 3722 if ui.quiet:
3721 3723 ui.write("%s\n" % t)
3722 3724 continue
3723 3725
3724 3726 try:
3725 3727 hn = hexfunc(n)
3726 3728 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3727 3729 except error.LookupError:
3728 3730 r = " ?:%s" % hn
3729 3731 else:
3730 3732 spaces = " " * (30 - encoding.colwidth(t))
3731 3733 if ui.verbose:
3732 3734 if repo.tagtype(t) == 'local':
3733 3735 tagtype = " local"
3734 3736 else:
3735 3737 tagtype = ""
3736 3738 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3737 3739
3738 3740 def tip(ui, repo, **opts):
3739 3741 """show the tip revision
3740 3742
3741 3743 The tip revision (usually just called the tip) is the changeset
3742 3744 most recently added to the repository (and therefore the most
3743 3745 recently changed head).
3744 3746
3745 3747 If you have just made a commit, that commit will be the tip. If
3746 3748 you have just pulled changes from another repository, the tip of
3747 3749 that repository becomes the current tip. The "tip" tag is special
3748 3750 and cannot be renamed or assigned to a different changeset.
3749 3751
3750 3752 Returns 0 on success.
3751 3753 """
3752 3754 displayer = cmdutil.show_changeset(ui, repo, opts)
3753 3755 displayer.show(repo[len(repo) - 1])
3754 3756 displayer.close()
3755 3757
3756 3758 def unbundle(ui, repo, fname1, *fnames, **opts):
3757 3759 """apply one or more changegroup files
3758 3760
3759 3761 Apply one or more compressed changegroup files generated by the
3760 3762 bundle command.
3761 3763
3762 3764 Returns 0 on success, 1 if an update has unresolved files.
3763 3765 """
3764 3766 fnames = (fname1,) + fnames
3765 3767
3766 3768 lock = repo.lock()
3767 3769 try:
3768 3770 for fname in fnames:
3769 3771 f = url.open(ui, fname)
3770 3772 gen = changegroup.readbundle(f, fname)
3771 3773 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
3772 3774 lock=lock)
3773 3775 finally:
3774 3776 lock.release()
3775 3777
3776 3778 return postincoming(ui, repo, modheads, opts.get('update'), None)
3777 3779
3778 3780 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3779 3781 """update working directory (or switch revisions)
3780 3782
3781 3783 Update the repository's working directory to the specified
3782 3784 changeset. If no changeset is specified, update to the tip of the
3783 3785 current named branch.
3784 3786
3785 3787 If the changeset is not a descendant of the working directory's
3786 3788 parent, the update is aborted. With the -c/--check option, the
3787 3789 working directory is checked for uncommitted changes; if none are
3788 3790 found, the working directory is updated to the specified
3789 3791 changeset.
3790 3792
3791 3793 The following rules apply when the working directory contains
3792 3794 uncommitted changes:
3793 3795
3794 3796 1. If neither -c/--check nor -C/--clean is specified, and if
3795 3797 the requested changeset is an ancestor or descendant of
3796 3798 the working directory's parent, the uncommitted changes
3797 3799 are merged into the requested changeset and the merged
3798 3800 result is left uncommitted. If the requested changeset is
3799 3801 not an ancestor or descendant (that is, it is on another
3800 3802 branch), the update is aborted and the uncommitted changes
3801 3803 are preserved.
3802 3804
3803 3805 2. With the -c/--check option, the update is aborted and the
3804 3806 uncommitted changes are preserved.
3805 3807
3806 3808 3. With the -C/--clean option, uncommitted changes are discarded and
3807 3809 the working directory is updated to the requested changeset.
3808 3810
3809 3811 Use null as the changeset to remove the working directory (like
3810 3812 :hg:`clone -U`).
3811 3813
3812 3814 If you want to update just one file to an older changeset, use
3813 3815 :hg:`revert`.
3814 3816
3815 3817 See :hg:`help dates` for a list of formats valid for -d/--date.
3816 3818
3817 3819 Returns 0 on success, 1 if there are unresolved files.
3818 3820 """
3819 3821 if rev and node:
3820 3822 raise util.Abort(_("please specify just one revision"))
3821 3823
3822 3824 if not rev:
3823 3825 rev = node
3824 3826
3825 3827 rev = cmdutil.revsingle(repo, rev, rev).rev()
3826 3828
3827 3829 if check and clean:
3828 3830 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3829 3831
3830 3832 if check:
3831 3833 # we could use dirty() but we can ignore merge and branch trivia
3832 3834 c = repo[None]
3833 3835 if c.modified() or c.added() or c.removed():
3834 3836 raise util.Abort(_("uncommitted local changes"))
3835 3837
3836 3838 if date:
3837 3839 if rev:
3838 3840 raise util.Abort(_("you can't specify a revision and a date"))
3839 3841 rev = cmdutil.finddate(ui, repo, date)
3840 3842
3841 3843 if clean or check:
3842 3844 return hg.clean(repo, rev)
3843 3845 else:
3844 3846 return hg.update(repo, rev)
3845 3847
3846 3848 def verify(ui, repo):
3847 3849 """verify the integrity of the repository
3848 3850
3849 3851 Verify the integrity of the current repository.
3850 3852
3851 3853 This will perform an extensive check of the repository's
3852 3854 integrity, validating the hashes and checksums of each entry in
3853 3855 the changelog, manifest, and tracked files, as well as the
3854 3856 integrity of their crosslinks and indices.
3855 3857
3856 3858 Returns 0 on success, 1 if errors are encountered.
3857 3859 """
3858 3860 return hg.verify(repo)
3859 3861
3860 3862 def version_(ui):
3861 3863 """output version and copyright information"""
3862 3864 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3863 3865 % util.version())
3864 3866 ui.status(_(
3865 3867 "\nCopyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others\n"
3866 3868 "This is free software; see the source for copying conditions. "
3867 3869 "There is NO\nwarranty; "
3868 3870 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3869 3871 ))
3870 3872
3871 3873 # Command options and aliases are listed here, alphabetically
3872 3874
3873 3875 globalopts = [
3874 3876 ('R', 'repository', '',
3875 3877 _('repository root directory or name of overlay bundle file'),
3876 3878 _('REPO')),
3877 3879 ('', 'cwd', '',
3878 3880 _('change working directory'), _('DIR')),
3879 3881 ('y', 'noninteractive', None,
3880 3882 _('do not prompt, assume \'yes\' for any required answers')),
3881 3883 ('q', 'quiet', None, _('suppress output')),
3882 3884 ('v', 'verbose', None, _('enable additional output')),
3883 3885 ('', 'config', [],
3884 3886 _('set/override config option (use \'section.name=value\')'),
3885 3887 _('CONFIG')),
3886 3888 ('', 'debug', None, _('enable debugging output')),
3887 3889 ('', 'debugger', None, _('start debugger')),
3888 3890 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
3889 3891 _('ENCODE')),
3890 3892 ('', 'encodingmode', encoding.encodingmode,
3891 3893 _('set the charset encoding mode'), _('MODE')),
3892 3894 ('', 'traceback', None, _('always print a traceback on exception')),
3893 3895 ('', 'time', None, _('time how long the command takes')),
3894 3896 ('', 'profile', None, _('print command execution profile')),
3895 3897 ('', 'version', None, _('output version information and exit')),
3896 3898 ('h', 'help', None, _('display help and exit')),
3897 3899 ]
3898 3900
3899 3901 dryrunopts = [('n', 'dry-run', None,
3900 3902 _('do not perform actions, just print output'))]
3901 3903
3902 3904 remoteopts = [
3903 3905 ('e', 'ssh', '',
3904 3906 _('specify ssh command to use'), _('CMD')),
3905 3907 ('', 'remotecmd', '',
3906 3908 _('specify hg command to run on the remote side'), _('CMD')),
3907 3909 ]
3908 3910
3909 3911 walkopts = [
3910 3912 ('I', 'include', [],
3911 3913 _('include names matching the given patterns'), _('PATTERN')),
3912 3914 ('X', 'exclude', [],
3913 3915 _('exclude names matching the given patterns'), _('PATTERN')),
3914 3916 ]
3915 3917
3916 3918 commitopts = [
3917 3919 ('m', 'message', '',
3918 3920 _('use text as commit message'), _('TEXT')),
3919 3921 ('l', 'logfile', '',
3920 3922 _('read commit message from file'), _('FILE')),
3921 3923 ]
3922 3924
3923 3925 commitopts2 = [
3924 3926 ('d', 'date', '',
3925 3927 _('record datecode as commit date'), _('DATE')),
3926 3928 ('u', 'user', '',
3927 3929 _('record the specified user as committer'), _('USER')),
3928 3930 ]
3929 3931
3930 3932 templateopts = [
3931 3933 ('', 'style', '',
3932 3934 _('display using template map file'), _('STYLE')),
3933 3935 ('', 'template', '',
3934 3936 _('display with template'), _('TEMPLATE')),
3935 3937 ]
3936 3938
3937 3939 logopts = [
3938 3940 ('p', 'patch', None, _('show patch')),
3939 3941 ('g', 'git', None, _('use git extended diff format')),
3940 3942 ('l', 'limit', '',
3941 3943 _('limit number of changes displayed'), _('NUM')),
3942 3944 ('M', 'no-merges', None, _('do not show merges')),
3943 3945 ('', 'stat', None, _('output diffstat-style summary of changes')),
3944 3946 ] + templateopts
3945 3947
3946 3948 diffopts = [
3947 3949 ('a', 'text', None, _('treat all files as text')),
3948 3950 ('g', 'git', None, _('use git extended diff format')),
3949 3951 ('', 'nodates', None, _('omit dates from diff headers'))
3950 3952 ]
3951 3953
3952 3954 diffopts2 = [
3953 3955 ('p', 'show-function', None, _('show which function each change is in')),
3954 3956 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3955 3957 ('w', 'ignore-all-space', None,
3956 3958 _('ignore white space when comparing lines')),
3957 3959 ('b', 'ignore-space-change', None,
3958 3960 _('ignore changes in the amount of white space')),
3959 3961 ('B', 'ignore-blank-lines', None,
3960 3962 _('ignore changes whose lines are all blank')),
3961 3963 ('U', 'unified', '',
3962 3964 _('number of lines of context to show'), _('NUM')),
3963 3965 ('', 'stat', None, _('output diffstat-style summary of changes')),
3964 3966 ]
3965 3967
3966 3968 similarityopts = [
3967 3969 ('s', 'similarity', '',
3968 3970 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
3969 3971 ]
3970 3972
3971 3973 subrepoopts = [
3972 3974 ('S', 'subrepos', None,
3973 3975 _('recurse into subrepositories'))
3974 3976 ]
3975 3977
3976 3978 table = {
3977 3979 "^add": (add, walkopts + subrepoopts + dryrunopts,
3978 3980 _('[OPTION]... [FILE]...')),
3979 3981 "addremove":
3980 3982 (addremove, similarityopts + walkopts + dryrunopts,
3981 3983 _('[OPTION]... [FILE]...')),
3982 3984 "^annotate|blame":
3983 3985 (annotate,
3984 3986 [('r', 'rev', '',
3985 3987 _('annotate the specified revision'), _('REV')),
3986 3988 ('', 'follow', None,
3987 3989 _('follow copies/renames and list the filename (DEPRECATED)')),
3988 3990 ('', 'no-follow', None, _("don't follow copies and renames")),
3989 3991 ('a', 'text', None, _('treat all files as text')),
3990 3992 ('u', 'user', None, _('list the author (long with -v)')),
3991 3993 ('f', 'file', None, _('list the filename')),
3992 3994 ('d', 'date', None, _('list the date (short with -q)')),
3993 3995 ('n', 'number', None, _('list the revision number (default)')),
3994 3996 ('c', 'changeset', None, _('list the changeset')),
3995 3997 ('l', 'line-number', None,
3996 3998 _('show line number at the first appearance'))
3997 3999 ] + walkopts,
3998 4000 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3999 4001 "archive":
4000 4002 (archive,
4001 4003 [('', 'no-decode', None, _('do not pass files through decoders')),
4002 4004 ('p', 'prefix', '',
4003 4005 _('directory prefix for files in archive'), _('PREFIX')),
4004 4006 ('r', 'rev', '',
4005 4007 _('revision to distribute'), _('REV')),
4006 4008 ('t', 'type', '',
4007 4009 _('type of distribution to create'), _('TYPE')),
4008 4010 ] + subrepoopts + walkopts,
4009 4011 _('[OPTION]... DEST')),
4010 4012 "backout":
4011 4013 (backout,
4012 4014 [('', 'merge', None,
4013 4015 _('merge with old dirstate parent after backout')),
4014 4016 ('', 'parent', '',
4015 4017 _('parent to choose when backing out merge'), _('REV')),
4016 4018 ('t', 'tool', '',
4017 4019 _('specify merge tool')),
4018 4020 ('r', 'rev', '',
4019 4021 _('revision to backout'), _('REV')),
4020 4022 ] + walkopts + commitopts + commitopts2,
4021 4023 _('[OPTION]... [-r] REV')),
4022 4024 "bisect":
4023 4025 (bisect,
4024 4026 [('r', 'reset', False, _('reset bisect state')),
4025 4027 ('g', 'good', False, _('mark changeset good')),
4026 4028 ('b', 'bad', False, _('mark changeset bad')),
4027 4029 ('s', 'skip', False, _('skip testing changeset')),
4028 4030 ('c', 'command', '',
4029 4031 _('use command to check changeset state'), _('CMD')),
4030 4032 ('U', 'noupdate', False, _('do not update to target'))],
4031 4033 _("[-gbsr] [-U] [-c CMD] [REV]")),
4032 4034 "branch":
4033 4035 (branch,
4034 4036 [('f', 'force', None,
4035 4037 _('set branch name even if it shadows an existing branch')),
4036 4038 ('C', 'clean', None, _('reset branch name to parent branch name'))],
4037 4039 _('[-fC] [NAME]')),
4038 4040 "branches":
4039 4041 (branches,
4040 4042 [('a', 'active', False,
4041 4043 _('show only branches that have unmerged heads')),
4042 4044 ('c', 'closed', False,
4043 4045 _('show normal and closed branches'))],
4044 4046 _('[-ac]')),
4045 4047 "bundle":
4046 4048 (bundle,
4047 4049 [('f', 'force', None,
4048 4050 _('run even when the destination is unrelated')),
4049 4051 ('r', 'rev', [],
4050 4052 _('a changeset intended to be added to the destination'),
4051 4053 _('REV')),
4052 4054 ('b', 'branch', [],
4053 4055 _('a specific branch you would like to bundle'),
4054 4056 _('BRANCH')),
4055 4057 ('', 'base', [],
4056 4058 _('a base changeset assumed to be available at the destination'),
4057 4059 _('REV')),
4058 4060 ('a', 'all', None, _('bundle all changesets in the repository')),
4059 4061 ('t', 'type', 'bzip2',
4060 4062 _('bundle compression type to use'), _('TYPE')),
4061 4063 ] + remoteopts,
4062 4064 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
4063 4065 "cat":
4064 4066 (cat,
4065 4067 [('o', 'output', '',
4066 4068 _('print output to file with formatted name'), _('FORMAT')),
4067 4069 ('r', 'rev', '',
4068 4070 _('print the given revision'), _('REV')),
4069 4071 ('', 'decode', None, _('apply any matching decode filter')),
4070 4072 ] + walkopts,
4071 4073 _('[OPTION]... FILE...')),
4072 4074 "^clone":
4073 4075 (clone,
4074 4076 [('U', 'noupdate', None,
4075 4077 _('the clone will include an empty working copy (only a repository)')),
4076 4078 ('u', 'updaterev', '',
4077 4079 _('revision, tag or branch to check out'), _('REV')),
4078 4080 ('r', 'rev', [],
4079 4081 _('include the specified changeset'), _('REV')),
4080 4082 ('b', 'branch', [],
4081 4083 _('clone only the specified branch'), _('BRANCH')),
4082 4084 ('', 'pull', None, _('use pull protocol to copy metadata')),
4083 4085 ('', 'uncompressed', None,
4084 4086 _('use uncompressed transfer (fast over LAN)')),
4085 4087 ] + remoteopts,
4086 4088 _('[OPTION]... SOURCE [DEST]')),
4087 4089 "^commit|ci":
4088 4090 (commit,
4089 4091 [('A', 'addremove', None,
4090 4092 _('mark new/missing files as added/removed before committing')),
4091 4093 ('', 'close-branch', None,
4092 4094 _('mark a branch as closed, hiding it from the branch list')),
4093 4095 ] + walkopts + commitopts + commitopts2,
4094 4096 _('[OPTION]... [FILE]...')),
4095 4097 "copy|cp":
4096 4098 (copy,
4097 4099 [('A', 'after', None, _('record a copy that has already occurred')),
4098 4100 ('f', 'force', None,
4099 4101 _('forcibly copy over an existing managed file')),
4100 4102 ] + walkopts + dryrunopts,
4101 4103 _('[OPTION]... [SOURCE]... DEST')),
4102 4104 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
4103 4105 "debugbuilddag":
4104 4106 (debugbuilddag,
4105 4107 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
4106 4108 ('a', 'appended-file', None, _('add single file all revs append to')),
4107 4109 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
4108 4110 ('n', 'new-file', None, _('add new file at each rev')),
4109 4111 ],
4110 4112 _('[OPTION]... TEXT')),
4111 4113 "debugcheckstate": (debugcheckstate, [], ''),
4112 4114 "debugcommands": (debugcommands, [], _('[COMMAND]')),
4113 4115 "debugcomplete":
4114 4116 (debugcomplete,
4115 4117 [('o', 'options', None, _('show the command options'))],
4116 4118 _('[-o] CMD')),
4117 4119 "debugdag":
4118 4120 (debugdag,
4119 4121 [('t', 'tags', None, _('use tags as labels')),
4120 4122 ('b', 'branches', None, _('annotate with branch names')),
4121 4123 ('', 'dots', None, _('use dots for runs')),
4122 4124 ('s', 'spaces', None, _('separate elements by spaces')),
4123 4125 ],
4124 4126 _('[OPTION]... [FILE [REV]...]')),
4125 4127 "debugdate":
4126 4128 (debugdate,
4127 4129 [('e', 'extended', None, _('try extended date formats'))],
4128 4130 _('[-e] DATE [RANGE]')),
4129 4131 "debugdata": (debugdata, [], _('FILE REV')),
4130 4132 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
4131 4133 "debugindex": (debugindex, [], _('FILE')),
4132 4134 "debugindexdot": (debugindexdot, [], _('FILE')),
4133 4135 "debuginstall": (debuginstall, [], ''),
4134 4136 "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
4135 4137 "debugrebuildstate":
4136 4138 (debugrebuildstate,
4137 4139 [('r', 'rev', '',
4138 4140 _('revision to rebuild to'), _('REV'))],
4139 4141 _('[-r REV] [REV]')),
4140 4142 "debugrename":
4141 4143 (debugrename,
4142 4144 [('r', 'rev', '',
4143 4145 _('revision to debug'), _('REV'))],
4144 4146 _('[-r REV] FILE')),
4145 4147 "debugrevspec":
4146 4148 (debugrevspec, [], ('REVSPEC')),
4147 4149 "debugsetparents":
4148 4150 (debugsetparents, [], _('REV1 [REV2]')),
4149 4151 "debugstate":
4150 4152 (debugstate,
4151 4153 [('', 'nodates', None, _('do not display the saved mtime'))],
4152 4154 _('[OPTION]...')),
4153 4155 "debugsub":
4154 4156 (debugsub,
4155 4157 [('r', 'rev', '',
4156 4158 _('revision to check'), _('REV'))],
4157 4159 _('[-r REV] [REV]')),
4158 4160 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
4159 4161 "^diff":
4160 4162 (diff,
4161 4163 [('r', 'rev', [],
4162 4164 _('revision'), _('REV')),
4163 4165 ('c', 'change', '',
4164 4166 _('change made by revision'), _('REV'))
4165 4167 ] + diffopts + diffopts2 + walkopts + subrepoopts,
4166 4168 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
4167 4169 "^export":
4168 4170 (export,
4169 4171 [('o', 'output', '',
4170 4172 _('print output to file with formatted name'), _('FORMAT')),
4171 4173 ('', 'switch-parent', None, _('diff against the second parent')),
4172 4174 ('r', 'rev', [],
4173 4175 _('revisions to export'), _('REV')),
4174 4176 ] + diffopts,
4175 4177 _('[OPTION]... [-o OUTFILESPEC] REV...')),
4176 4178 "^forget":
4177 4179 (forget,
4178 4180 [] + walkopts,
4179 4181 _('[OPTION]... FILE...')),
4180 4182 "grep":
4181 4183 (grep,
4182 4184 [('0', 'print0', None, _('end fields with NUL')),
4183 4185 ('', 'all', None, _('print all revisions that match')),
4184 4186 ('f', 'follow', None,
4185 4187 _('follow changeset history,'
4186 4188 ' or file history across copies and renames')),
4187 4189 ('i', 'ignore-case', None, _('ignore case when matching')),
4188 4190 ('l', 'files-with-matches', None,
4189 4191 _('print only filenames and revisions that match')),
4190 4192 ('n', 'line-number', None, _('print matching line numbers')),
4191 4193 ('r', 'rev', [],
4192 4194 _('only search files changed within revision range'), _('REV')),
4193 4195 ('u', 'user', None, _('list the author (long with -v)')),
4194 4196 ('d', 'date', None, _('list the date (short with -q)')),
4195 4197 ] + walkopts,
4196 4198 _('[OPTION]... PATTERN [FILE]...')),
4197 4199 "heads":
4198 4200 (heads,
4199 4201 [('r', 'rev', '',
4200 4202 _('show only heads which are descendants of STARTREV'),
4201 4203 _('STARTREV')),
4202 4204 ('t', 'topo', False, _('show topological heads only')),
4203 4205 ('a', 'active', False,
4204 4206 _('show active branchheads only (DEPRECATED)')),
4205 4207 ('c', 'closed', False,
4206 4208 _('show normal and closed branch heads')),
4207 4209 ] + templateopts,
4208 4210 _('[-ac] [-r STARTREV] [REV]...')),
4209 4211 "help": (help_, [], _('[TOPIC]')),
4210 4212 "identify|id":
4211 4213 (identify,
4212 4214 [('r', 'rev', '',
4213 4215 _('identify the specified revision'), _('REV')),
4214 4216 ('n', 'num', None, _('show local revision number')),
4215 4217 ('i', 'id', None, _('show global revision id')),
4216 4218 ('b', 'branch', None, _('show branch')),
4217 4219 ('t', 'tags', None, _('show tags'))],
4218 4220 _('[-nibt] [-r REV] [SOURCE]')),
4219 4221 "import|patch":
4220 4222 (import_,
4221 4223 [('p', 'strip', 1,
4222 4224 _('directory strip option for patch. This has the same '
4223 4225 'meaning as the corresponding patch option'),
4224 4226 _('NUM')),
4225 4227 ('b', 'base', '',
4226 4228 _('base path'), _('PATH')),
4227 4229 ('f', 'force', None,
4228 4230 _('skip check for outstanding uncommitted changes')),
4229 4231 ('', 'no-commit', None,
4230 4232 _("don't commit, just update the working directory")),
4231 4233 ('', 'exact', None,
4232 4234 _('apply patch to the nodes from which it was generated')),
4233 4235 ('', 'import-branch', None,
4234 4236 _('use any branch information in patch (implied by --exact)'))] +
4235 4237 commitopts + commitopts2 + similarityopts,
4236 4238 _('[OPTION]... PATCH...')),
4237 4239 "incoming|in":
4238 4240 (incoming,
4239 4241 [('f', 'force', None,
4240 4242 _('run even if remote repository is unrelated')),
4241 4243 ('n', 'newest-first', None, _('show newest record first')),
4242 4244 ('', 'bundle', '',
4243 4245 _('file to store the bundles into'), _('FILE')),
4244 4246 ('r', 'rev', [],
4245 4247 _('a remote changeset intended to be added'), _('REV')),
4246 4248 ('b', 'branch', [],
4247 4249 _('a specific branch you would like to pull'), _('BRANCH')),
4248 4250 ] + logopts + remoteopts + subrepoopts,
4249 4251 _('[-p] [-n] [-M] [-f] [-r REV]...'
4250 4252 ' [--bundle FILENAME] [SOURCE]')),
4251 4253 "^init":
4252 4254 (init,
4253 4255 remoteopts,
4254 4256 _('[-e CMD] [--remotecmd CMD] [DEST]')),
4255 4257 "locate":
4256 4258 (locate,
4257 4259 [('r', 'rev', '',
4258 4260 _('search the repository as it is in REV'), _('REV')),
4259 4261 ('0', 'print0', None,
4260 4262 _('end filenames with NUL, for use with xargs')),
4261 4263 ('f', 'fullpath', None,
4262 4264 _('print complete paths from the filesystem root')),
4263 4265 ] + walkopts,
4264 4266 _('[OPTION]... [PATTERN]...')),
4265 4267 "^log|history":
4266 4268 (log,
4267 4269 [('f', 'follow', None,
4268 4270 _('follow changeset history,'
4269 4271 ' or file history across copies and renames')),
4270 4272 ('', 'follow-first', None,
4271 4273 _('only follow the first parent of merge changesets')),
4272 4274 ('d', 'date', '',
4273 4275 _('show revisions matching date spec'), _('DATE')),
4274 4276 ('C', 'copies', None, _('show copied files')),
4275 4277 ('k', 'keyword', [],
4276 4278 _('do case-insensitive search for a given text'), _('TEXT')),
4277 4279 ('r', 'rev', [],
4278 4280 _('show the specified revision or range'), _('REV')),
4279 4281 ('', 'removed', None, _('include revisions where files were removed')),
4280 4282 ('m', 'only-merges', None, _('show only merges')),
4281 4283 ('u', 'user', [],
4282 4284 _('revisions committed by user'), _('USER')),
4283 4285 ('', 'only-branch', [],
4284 4286 _('show only changesets within the given named branch (DEPRECATED)'),
4285 4287 _('BRANCH')),
4286 4288 ('b', 'branch', [],
4287 4289 _('show changesets within the given named branch'), _('BRANCH')),
4288 4290 ('P', 'prune', [],
4289 4291 _('do not display revision or any of its ancestors'), _('REV')),
4290 4292 ] + logopts + walkopts,
4291 4293 _('[OPTION]... [FILE]')),
4292 4294 "manifest":
4293 4295 (manifest,
4294 4296 [('r', 'rev', '',
4295 4297 _('revision to display'), _('REV'))],
4296 4298 _('[-r REV]')),
4297 4299 "^merge":
4298 4300 (merge,
4299 4301 [('f', 'force', None, _('force a merge with outstanding changes')),
4300 4302 ('t', 'tool', '', _('specify merge tool')),
4301 4303 ('r', 'rev', '',
4302 4304 _('revision to merge'), _('REV')),
4303 4305 ('P', 'preview', None,
4304 4306 _('review revisions to merge (no merge is performed)'))],
4305 4307 _('[-P] [-f] [[-r] REV]')),
4306 4308 "outgoing|out":
4307 4309 (outgoing,
4308 4310 [('f', 'force', None,
4309 4311 _('run even when the destination is unrelated')),
4310 4312 ('r', 'rev', [],
4311 4313 _('a changeset intended to be included in the destination'),
4312 4314 _('REV')),
4313 4315 ('n', 'newest-first', None, _('show newest record first')),
4314 4316 ('b', 'branch', [],
4315 4317 _('a specific branch you would like to push'), _('BRANCH')),
4316 4318 ] + logopts + remoteopts + subrepoopts,
4317 4319 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
4318 4320 "parents":
4319 4321 (parents,
4320 4322 [('r', 'rev', '',
4321 4323 _('show parents of the specified revision'), _('REV')),
4322 4324 ] + templateopts,
4323 4325 _('[-r REV] [FILE]')),
4324 4326 "paths": (paths, [], _('[NAME]')),
4325 4327 "^pull":
4326 4328 (pull,
4327 4329 [('u', 'update', None,
4328 4330 _('update to new branch head if changesets were pulled')),
4329 4331 ('f', 'force', None,
4330 4332 _('run even when remote repository is unrelated')),
4331 4333 ('r', 'rev', [],
4332 4334 _('a remote changeset intended to be added'), _('REV')),
4333 4335 ('b', 'branch', [],
4334 4336 _('a specific branch you would like to pull'), _('BRANCH')),
4335 4337 ] + remoteopts,
4336 4338 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
4337 4339 "^push":
4338 4340 (push,
4339 4341 [('f', 'force', None, _('force push')),
4340 4342 ('r', 'rev', [],
4341 4343 _('a changeset intended to be included in the destination'),
4342 4344 _('REV')),
4343 4345 ('b', 'branch', [],
4344 4346 _('a specific branch you would like to push'), _('BRANCH')),
4345 4347 ('', 'new-branch', False, _('allow pushing a new branch')),
4346 4348 ] + remoteopts,
4347 4349 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
4348 4350 "recover": (recover, []),
4349 4351 "^remove|rm":
4350 4352 (remove,
4351 4353 [('A', 'after', None, _('record delete for missing files')),
4352 4354 ('f', 'force', None,
4353 4355 _('remove (and delete) file even if added or modified')),
4354 4356 ] + walkopts,
4355 4357 _('[OPTION]... FILE...')),
4356 4358 "rename|move|mv":
4357 4359 (rename,
4358 4360 [('A', 'after', None, _('record a rename that has already occurred')),
4359 4361 ('f', 'force', None,
4360 4362 _('forcibly copy over an existing managed file')),
4361 4363 ] + walkopts + dryrunopts,
4362 4364 _('[OPTION]... SOURCE... DEST')),
4363 4365 "resolve":
4364 4366 (resolve,
4365 4367 [('a', 'all', None, _('select all unresolved files')),
4366 4368 ('l', 'list', None, _('list state of files needing merge')),
4367 4369 ('m', 'mark', None, _('mark files as resolved')),
4368 4370 ('u', 'unmark', None, _('mark files as unresolved')),
4369 4371 ('t', 'tool', '', _('specify merge tool')),
4370 4372 ('n', 'no-status', None, _('hide status prefix'))]
4371 4373 + walkopts,
4372 4374 _('[OPTION]... [FILE]...')),
4373 4375 "revert":
4374 4376 (revert,
4375 4377 [('a', 'all', None, _('revert all changes when no arguments given')),
4376 4378 ('d', 'date', '',
4377 4379 _('tipmost revision matching date'), _('DATE')),
4378 4380 ('r', 'rev', '',
4379 4381 _('revert to the specified revision'), _('REV')),
4380 4382 ('', 'no-backup', None, _('do not save backup copies of files')),
4381 4383 ] + walkopts + dryrunopts,
4382 4384 _('[OPTION]... [-r REV] [NAME]...')),
4383 4385 "rollback": (rollback, dryrunopts),
4384 4386 "root": (root, []),
4385 4387 "^serve":
4386 4388 (serve,
4387 4389 [('A', 'accesslog', '',
4388 4390 _('name of access log file to write to'), _('FILE')),
4389 4391 ('d', 'daemon', None, _('run server in background')),
4390 4392 ('', 'daemon-pipefds', '',
4391 4393 _('used internally by daemon mode'), _('NUM')),
4392 4394 ('E', 'errorlog', '',
4393 4395 _('name of error log file to write to'), _('FILE')),
4394 4396 # use string type, then we can check if something was passed
4395 4397 ('p', 'port', '',
4396 4398 _('port to listen on (default: 8000)'), _('PORT')),
4397 4399 ('a', 'address', '',
4398 4400 _('address to listen on (default: all interfaces)'), _('ADDR')),
4399 4401 ('', 'prefix', '',
4400 4402 _('prefix path to serve from (default: server root)'), _('PREFIX')),
4401 4403 ('n', 'name', '',
4402 4404 _('name to show in web pages (default: working directory)'),
4403 4405 _('NAME')),
4404 4406 ('', 'web-conf', '',
4405 4407 _('name of the hgweb config file (see "hg help hgweb")'),
4406 4408 _('FILE')),
4407 4409 ('', 'webdir-conf', '',
4408 4410 _('name of the hgweb config file (DEPRECATED)'), _('FILE')),
4409 4411 ('', 'pid-file', '',
4410 4412 _('name of file to write process ID to'), _('FILE')),
4411 4413 ('', 'stdio', None, _('for remote clients')),
4412 4414 ('t', 'templates', '',
4413 4415 _('web templates to use'), _('TEMPLATE')),
4414 4416 ('', 'style', '',
4415 4417 _('template style to use'), _('STYLE')),
4416 4418 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4417 4419 ('', 'certificate', '',
4418 4420 _('SSL certificate file'), _('FILE'))],
4419 4421 _('[OPTION]...')),
4420 4422 "showconfig|debugconfig":
4421 4423 (showconfig,
4422 4424 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4423 4425 _('[-u] [NAME]...')),
4424 4426 "^summary|sum":
4425 4427 (summary,
4426 4428 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4427 4429 "^status|st":
4428 4430 (status,
4429 4431 [('A', 'all', None, _('show status of all files')),
4430 4432 ('m', 'modified', None, _('show only modified files')),
4431 4433 ('a', 'added', None, _('show only added files')),
4432 4434 ('r', 'removed', None, _('show only removed files')),
4433 4435 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4434 4436 ('c', 'clean', None, _('show only files without changes')),
4435 4437 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4436 4438 ('i', 'ignored', None, _('show only ignored files')),
4437 4439 ('n', 'no-status', None, _('hide status prefix')),
4438 4440 ('C', 'copies', None, _('show source of copied files')),
4439 4441 ('0', 'print0', None,
4440 4442 _('end filenames with NUL, for use with xargs')),
4441 4443 ('', 'rev', [],
4442 4444 _('show difference from revision'), _('REV')),
4443 4445 ('', 'change', '',
4444 4446 _('list the changed files of a revision'), _('REV')),
4445 4447 ] + walkopts + subrepoopts,
4446 4448 _('[OPTION]... [FILE]...')),
4447 4449 "tag":
4448 4450 (tag,
4449 4451 [('f', 'force', None, _('replace existing tag')),
4450 4452 ('l', 'local', None, _('make the tag local')),
4451 4453 ('r', 'rev', '',
4452 4454 _('revision to tag'), _('REV')),
4453 4455 ('', 'remove', None, _('remove a tag')),
4454 4456 # -l/--local is already there, commitopts cannot be used
4455 4457 ('e', 'edit', None, _('edit commit message')),
4456 4458 ('m', 'message', '',
4457 4459 _('use <text> as commit message'), _('TEXT')),
4458 4460 ] + commitopts2,
4459 4461 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4460 4462 "tags": (tags, [], ''),
4461 4463 "tip":
4462 4464 (tip,
4463 4465 [('p', 'patch', None, _('show patch')),
4464 4466 ('g', 'git', None, _('use git extended diff format')),
4465 4467 ] + templateopts,
4466 4468 _('[-p] [-g]')),
4467 4469 "unbundle":
4468 4470 (unbundle,
4469 4471 [('u', 'update', None,
4470 4472 _('update to new branch head if changesets were unbundled'))],
4471 4473 _('[-u] FILE...')),
4472 4474 "^update|up|checkout|co":
4473 4475 (update,
4474 4476 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4475 4477 ('c', 'check', None,
4476 4478 _('update across branches if no uncommitted changes')),
4477 4479 ('d', 'date', '',
4478 4480 _('tipmost revision matching date'), _('DATE')),
4479 4481 ('r', 'rev', '',
4480 4482 _('revision'), _('REV'))],
4481 4483 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4482 4484 "verify": (verify, []),
4483 4485 "version": (version_, []),
4484 4486 }
4485 4487
4486 4488 norepo = ("clone init version help debugcommands debugcomplete"
4487 4489 " debugdate debuginstall debugfsinfo debugpushkey")
4488 4490 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
4489 4491 " debugdata debugindex debugindexdot")
@@ -1,203 +1,87 b''
1 1 Mercurial supports a functional language for selecting a set of
2 2 revisions.
3 3
4 4 The language supports a number of predicates which are joined by infix
5 5 operators. Parenthesis can be used for grouping.
6 6
7 7 Identifiers such as branch names must be quoted with single or double
8 8 quotes if they contain characters outside of
9 9 ``[._a-zA-Z0-9\x80-\xff]`` or if they match one of the predefined
10 10 predicates.
11 11
12 12 Special characters can be used in quoted identifiers by escaping them,
13 13 e.g., ``\n`` is interpreted as a newline. To prevent them from being
14 14 interpreted, strings can be prefixed with ``r``, e.g. ``r'...'``.
15 15
16 16 There is a single prefix operator:
17 17
18 18 ``not x``
19 19 Changesets not in x. Short form is ``! x``.
20 20
21 21 These are the supported infix operators:
22 22
23 23 ``x::y``
24 24 A DAG range, meaning all changesets that are descendants of x and
25 25 ancestors of y, including x and y themselves. If the first endpoint
26 26 is left out, this is equivalent to ``ancestors(y)``, if the second
27 27 is left out it is equivalent to ``descendants(x)``.
28 28
29 29 An alternative syntax is ``x..y``.
30 30
31 31 ``x:y``
32 32 All changesets with revision numbers between x and y, both
33 33 inclusive. Either endpoint can be left out, they default to 0 and
34 34 tip.
35 35
36 36 ``x and y``
37 37 The intersection of changesets in x and y. Short form is ``x & y``.
38 38
39 39 ``x or y``
40 40 The union of changesets in x and y. There are two alternative short
41 41 forms: ``x | y`` and ``x + y``.
42 42
43 43 ``x - y``
44 44 Changesets in x but not in y.
45 45
46 46 The following predicates are supported:
47 47
48 ``adds(pattern)``
49 Changesets that add a file matching pattern.
50
51 ``all()``
52 All changesets, the same as ``0:tip``.
53
54 ``ancestor(single, single)``
55 Greatest common ancestor of the two changesets.
56
57 ``ancestors(set)``
58 Changesets that are ancestors of a changeset in set.
59
60 ``author(string)``
61 Alias for ``user(string)``.
62
63 ``branch(set)``
64 All changesets belonging to the branches of changesets in set.
65
66 ``children(set)``
67 Child changesets of changesets in set.
68
69 ``closed()``
70 Changeset is closed.
71
72 ``contains(pattern)``
73 Revision contains pattern.
74
75 ``date(interval)``
76 Changesets within the interval, see :hg:`help dates`.
77
78 ``descendants(set)``
79 Changesets which are descendants of changesets in set.
80
81 ``file(pattern)``
82 Changesets affecting files matched by pattern.
83
84 ``follow()``
85 An alias for ``::.`` (ancestors of the working copy's first parent).
86
87 ``grep(regex)``
88 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
89 to ensure special escape characters are handled correctly.
90
91 ``head()``
92 Changeset is a named branch head.
93
94 ``heads(set)``
95 Members of set with no children in set.
96
97 ``id(string)``
98 Revision non-ambiguously specified by the given hex string prefix
99
100 ``keyword(string)``
101 Search commit message, user name, and names of changed files for
102 string.
103
104 ``limit(set, n)``
105 First n members of set.
106
107 ``max(set)``
108 Changeset with highest revision number in set.
109
110 ``min(set)``
111 Changeset with lowest revision number in set.
112
113 ``merge()``
114 Changeset is a merge changeset.
115
116 ``modifies(pattern)``
117 Changesets modifying files matched by pattern.
118
119 ``outgoing([path])``
120 Changesets not found in the specified destination repository, or the
121 default push location.
122
123 ``p1(set)``
124 First parent of changesets in set.
125
126 ``p2(set)``
127 Second parent of changesets in set.
128
129 ``parents(set)``
130 The set of all parents for all changesets in set.
131
132 ``present(set)``
133 An empty set, if any revision in set isn't found; otherwise,
134 all revisions in set.
135
136 ``removes(pattern)``
137 Changesets which remove files matching pattern.
138
139 ``rev(number)``
140 Revision with the given numeric identifier.
141
142 ``reverse(set)``
143 Reverse order of set.
144
145 ``roots(set)``
146 Changesets with no parent changeset in set.
147
148 ``sort(set[, [-]key...])``
149 Sort set by keys. The default sort order is ascending, specify a key
150 as ``-key`` to sort in descending order.
151
152 The keys can be:
153
154 - ``rev`` for the revision number,
155 - ``branch`` for the branch name,
156 - ``desc`` for the commit message (description),
157 - ``user`` for user name (``author`` can be used as an alias),
158 - ``date`` for the commit date
159
160 ``tag(name)``
161 The specified tag by name, or all tagged revisions if no name is given.
162
163 ``user(string)``
164 User name is string.
48 .. predicatesmarker
165 49
166 50 Command line equivalents for :hg:`log`::
167 51
168 52 -f -> ::.
169 53 -d x -> date(x)
170 54 -k x -> keyword(x)
171 55 -m -> merge()
172 56 -u x -> user(x)
173 57 -b x -> branch(x)
174 58 -P x -> !::x
175 59 -l x -> limit(expr, x)
176 60
177 61 Some sample queries:
178 62
179 63 - Changesets on the default branch::
180 64
181 65 hg log -r "branch(default)"
182 66
183 67 - Changesets on the default branch since tag 1.5 (excluding merges)::
184 68
185 69 hg log -r "branch(default) and 1.5:: and not merge()"
186 70
187 71 - Open branch heads::
188 72
189 73 hg log -r "head() and not closed()"
190 74
191 75 - Changesets between tags 1.3 and 1.5 mentioning "bug" that affect
192 76 ``hgext/*``::
193 77
194 78 hg log -r "1.3::1.5 and keyword(bug) and file('hgext/*')"
195 79
196 80 - Changesets in committed May 2008, sorted by user::
197 81
198 82 hg log -r "sort(date('May 2008'), user)"
199 83
200 84 - Changesets mentioning "bug" or "issue" that are not in a tagged
201 85 release::
202 86
203 87 hg log -r "(keyword(bug) or keyword(issue)) and not ancestors(tagged())"
@@ -1,655 +1,793 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
10 10 import match as matchmod
11 11 from i18n import _
12 12
13 13 elements = {
14 14 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
15 15 "-": (5, ("negate", 19), ("minus", 5)),
16 16 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
17 17 ("dagrangepost", 17)),
18 18 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
19 19 ("dagrangepost", 17)),
20 20 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
21 21 "not": (10, ("not", 10)),
22 22 "!": (10, ("not", 10)),
23 23 "and": (5, None, ("and", 5)),
24 24 "&": (5, None, ("and", 5)),
25 25 "or": (4, None, ("or", 4)),
26 26 "|": (4, None, ("or", 4)),
27 27 "+": (4, None, ("or", 4)),
28 28 ",": (2, None, ("list", 2)),
29 29 ")": (0, None, None),
30 30 "symbol": (0, ("symbol",), None),
31 31 "string": (0, ("string",), None),
32 32 "end": (0, None, None),
33 33 }
34 34
35 35 keywords = set(['and', 'or', 'not'])
36 36
37 37 def tokenize(program):
38 38 pos, l = 0, len(program)
39 39 while pos < l:
40 40 c = program[pos]
41 41 if c.isspace(): # skip inter-token whitespace
42 42 pass
43 43 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
44 44 yield ('::', None, pos)
45 45 pos += 1 # skip ahead
46 46 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
47 47 yield ('..', None, pos)
48 48 pos += 1 # skip ahead
49 49 elif c in "():,-|&+!": # handle simple operators
50 50 yield (c, None, pos)
51 51 elif (c in '"\'' or c == 'r' and
52 52 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
53 53 if c == 'r':
54 54 pos += 1
55 55 c = program[pos]
56 56 decode = lambda x: x
57 57 else:
58 58 decode = lambda x: x.decode('string-escape')
59 59 pos += 1
60 60 s = pos
61 61 while pos < l: # find closing quote
62 62 d = program[pos]
63 63 if d == '\\': # skip over escaped characters
64 64 pos += 2
65 65 continue
66 66 if d == c:
67 67 yield ('string', decode(program[s:pos]), s)
68 68 break
69 69 pos += 1
70 70 else:
71 71 raise error.ParseError(_("unterminated string"), s)
72 72 elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
73 73 s = pos
74 74 pos += 1
75 75 while pos < l: # find end of symbol
76 76 d = program[pos]
77 77 if not (d.isalnum() or d in "._" or ord(d) > 127):
78 78 break
79 79 if d == '.' and program[pos - 1] == '.': # special case for ..
80 80 pos -= 1
81 81 break
82 82 pos += 1
83 83 sym = program[s:pos]
84 84 if sym in keywords: # operator keywords
85 85 yield (sym, None, s)
86 86 else:
87 87 yield ('symbol', sym, s)
88 88 pos -= 1
89 89 else:
90 90 raise error.ParseError(_("syntax error"), pos)
91 91 pos += 1
92 92 yield ('end', None, pos)
93 93
94 94 # helpers
95 95
96 96 def getstring(x, err):
97 97 if x and (x[0] == 'string' or x[0] == 'symbol'):
98 98 return x[1]
99 99 raise error.ParseError(err)
100 100
101 101 def getlist(x):
102 102 if not x:
103 103 return []
104 104 if x[0] == 'list':
105 105 return getlist(x[1]) + [x[2]]
106 106 return [x]
107 107
108 108 def getargs(x, min, max, err):
109 109 l = getlist(x)
110 110 if len(l) < min or len(l) > max:
111 111 raise error.ParseError(err)
112 112 return l
113 113
114 114 def getset(repo, subset, x):
115 115 if not x:
116 116 raise error.ParseError(_("missing argument"))
117 117 return methods[x[0]](repo, subset, *x[1:])
118 118
119 119 # operator methods
120 120
121 121 def stringset(repo, subset, x):
122 122 x = repo[x].rev()
123 123 if x == -1 and len(subset) == len(repo):
124 124 return [-1]
125 125 if x in subset:
126 126 return [x]
127 127 return []
128 128
129 129 def symbolset(repo, subset, x):
130 130 if x in symbols:
131 131 raise error.ParseError(_("can't use %s here") % x)
132 132 return stringset(repo, subset, x)
133 133
134 134 def rangeset(repo, subset, x, y):
135 135 m = getset(repo, subset, x)
136 136 if not m:
137 137 m = getset(repo, range(len(repo)), x)
138 138
139 139 n = getset(repo, subset, y)
140 140 if not n:
141 141 n = getset(repo, range(len(repo)), y)
142 142
143 143 if not m or not n:
144 144 return []
145 145 m, n = m[0], n[-1]
146 146
147 147 if m < n:
148 148 r = range(m, n + 1)
149 149 else:
150 150 r = range(m, n - 1, -1)
151 151 s = set(subset)
152 152 return [x for x in r if x in s]
153 153
154 154 def andset(repo, subset, x, y):
155 155 return getset(repo, getset(repo, subset, x), y)
156 156
157 157 def orset(repo, subset, x, y):
158 158 s = set(getset(repo, subset, x))
159 159 s |= set(getset(repo, [r for r in subset if r not in s], y))
160 160 return [r for r in subset if r in s]
161 161
162 162 def notset(repo, subset, x):
163 163 s = set(getset(repo, subset, x))
164 164 return [r for r in subset if r not in s]
165 165
166 166 def listset(repo, subset, a, b):
167 167 raise error.ParseError(_("can't use a list in this context"))
168 168
169 169 def func(repo, subset, a, b):
170 170 if a[0] == 'symbol' and a[1] in symbols:
171 171 return symbols[a[1]](repo, subset, b)
172 172 raise error.ParseError(_("not a function: %s") % a[1])
173 173
174 174 # functions
175 175
176 176 def node(repo, subset, x):
177 """``id(string)``
178 Revision non-ambiguously specified by the given hex string prefix
179 """
177 180 # i18n: "id" is a keyword
178 181 l = getargs(x, 1, 1, _("id requires one argument"))
179 182 # i18n: "id" is a keyword
180 183 n = getstring(l[0], _("id requires a string"))
181 184 if len(n) == 40:
182 185 rn = repo[n].rev()
183 186 else:
184 187 rn = repo.changelog.rev(repo.changelog._partialmatch(n))
185 188 return [r for r in subset if r == rn]
186 189
187 190 def rev(repo, subset, x):
191 """``rev(number)``
192 Revision with the given numeric identifier.
193 """
188 194 # i18n: "rev" is a keyword
189 195 l = getargs(x, 1, 1, _("rev requires one argument"))
190 196 try:
191 197 # i18n: "rev" is a keyword
192 198 l = int(getstring(l[0], _("rev requires a number")))
193 199 except ValueError:
194 200 # i18n: "rev" is a keyword
195 201 raise error.ParseError(_("rev expects a number"))
196 202 return [r for r in subset if r == l]
197 203
198 204 def p1(repo, subset, x):
205 """``p1(set)``
206 First parent of changesets in set.
207 """
199 208 ps = set()
200 209 cl = repo.changelog
201 210 for r in getset(repo, range(len(repo)), x):
202 211 ps.add(cl.parentrevs(r)[0])
203 212 return [r for r in subset if r in ps]
204 213
205 214 def p2(repo, subset, x):
215 """``p2(set)``
216 Second parent of changesets in set.
217 """
206 218 ps = set()
207 219 cl = repo.changelog
208 220 for r in getset(repo, range(len(repo)), x):
209 221 ps.add(cl.parentrevs(r)[1])
210 222 return [r for r in subset if r in ps]
211 223
212 224 def parents(repo, subset, x):
225 """``parents(set)``
226 The set of all parents for all changesets in set.
227 """
213 228 ps = set()
214 229 cl = repo.changelog
215 230 for r in getset(repo, range(len(repo)), x):
216 231 ps.update(cl.parentrevs(r))
217 232 return [r for r in subset if r in ps]
218 233
219 234 def maxrev(repo, subset, x):
235 """``max(set)``
236 Changeset with highest revision number in set.
237 """
220 238 s = getset(repo, subset, x)
221 239 if s:
222 240 m = max(s)
223 241 if m in subset:
224 242 return [m]
225 243 return []
226 244
227 245 def minrev(repo, subset, x):
246 """``min(set)``
247 Changeset with lowest revision number in set.
248 """
228 249 s = getset(repo, subset, x)
229 250 if s:
230 251 m = min(s)
231 252 if m in subset:
232 253 return [m]
233 254 return []
234 255
235 256 def limit(repo, subset, x):
257 """``limit(set, n)``
258 First n members of set.
259 """
236 260 # i18n: "limit" is a keyword
237 261 l = getargs(x, 2, 2, _("limit requires two arguments"))
238 262 try:
239 263 # i18n: "limit" is a keyword
240 264 lim = int(getstring(l[1], _("limit requires a number")))
241 265 except ValueError:
242 266 # i18n: "limit" is a keyword
243 267 raise error.ParseError(_("limit expects a number"))
244 268 return getset(repo, subset, l[0])[:lim]
245 269
246 270 def children(repo, subset, x):
271 """``children(set)``
272 Child changesets of changesets in set.
273 """
247 274 cs = set()
248 275 cl = repo.changelog
249 276 s = set(getset(repo, range(len(repo)), x))
250 277 for r in xrange(0, len(repo)):
251 278 for p in cl.parentrevs(r):
252 279 if p in s:
253 280 cs.add(r)
254 281 return [r for r in subset if r in cs]
255 282
256 283 def branch(repo, subset, x):
284 """``branch(set)``
285 All changesets belonging to the branches of changesets in set.
286 """
257 287 s = getset(repo, range(len(repo)), x)
258 288 b = set()
259 289 for r in s:
260 290 b.add(repo[r].branch())
261 291 s = set(s)
262 292 return [r for r in subset if r in s or repo[r].branch() in b]
263 293
264 294 def ancestor(repo, subset, x):
295 """``ancestor(single, single)``
296 Greatest common ancestor of the two changesets.
297 """
265 298 # i18n: "ancestor" is a keyword
266 299 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
267 300 r = range(len(repo))
268 301 a = getset(repo, r, l[0])
269 302 b = getset(repo, r, l[1])
270 303 if len(a) != 1 or len(b) != 1:
271 304 # i18n: "ancestor" is a keyword
272 305 raise error.ParseError(_("ancestor arguments must be single revisions"))
273 306 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
274 307
275 308 return [r for r in an if r in subset]
276 309
277 310 def ancestors(repo, subset, x):
311 """``ancestors(set)``
312 Changesets that are ancestors of a changeset in set.
313 """
278 314 args = getset(repo, range(len(repo)), x)
279 315 if not args:
280 316 return []
281 317 s = set(repo.changelog.ancestors(*args)) | set(args)
282 318 return [r for r in subset if r in s]
283 319
284 320 def descendants(repo, subset, x):
321 """``descendants(set)``
322 Changesets which are descendants of changesets in set.
323 """
285 324 args = getset(repo, range(len(repo)), x)
286 325 if not args:
287 326 return []
288 327 s = set(repo.changelog.descendants(*args)) | set(args)
289 328 return [r for r in subset if r in s]
290 329
291 330 def follow(repo, subset, x):
331 """``follow()``
332 An alias for ``::.`` (ancestors of the working copy's first parent).
333 """
292 334 # i18n: "follow" is a keyword
293 335 getargs(x, 0, 0, _("follow takes no arguments"))
294 336 p = repo['.'].rev()
295 337 s = set(repo.changelog.ancestors(p)) | set([p])
296 338 return [r for r in subset if r in s]
297 339
298 340 def date(repo, subset, x):
341 """``date(interval)``
342 Changesets within the interval, see :hg:`help dates`.
343 """
299 344 # i18n: "date" is a keyword
300 345 ds = getstring(x, _("date requires a string"))
301 346 dm = util.matchdate(ds)
302 347 return [r for r in subset if dm(repo[r].date()[0])]
303 348
304 349 def keyword(repo, subset, x):
350 """``keyword(string)``
351 Search commit message, user name, and names of changed files for
352 string.
353 """
305 354 # i18n: "keyword" is a keyword
306 355 kw = getstring(x, _("keyword requires a string")).lower()
307 356 l = []
308 357 for r in subset:
309 358 c = repo[r]
310 359 t = " ".join(c.files() + [c.user(), c.description()])
311 360 if kw in t.lower():
312 361 l.append(r)
313 362 return l
314 363
315 364 def grep(repo, subset, x):
365 """``grep(regex)``
366 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
367 to ensure special escape characters are handled correctly.
368 """
316 369 try:
317 370 # i18n: "grep" is a keyword
318 371 gr = re.compile(getstring(x, _("grep requires a string")))
319 372 except re.error, e:
320 373 raise error.ParseError(_('invalid match pattern: %s') % e)
321 374 l = []
322 375 for r in subset:
323 376 c = repo[r]
324 377 for e in c.files() + [c.user(), c.description()]:
325 378 if gr.search(e):
326 379 l.append(r)
327 380 continue
328 381 return l
329 382
330 383 def author(repo, subset, x):
384 """``author(string)``
385 Alias for ``user(string)``.
386 """
331 387 # i18n: "author" is a keyword
332 388 n = getstring(x, _("author requires a string")).lower()
333 389 return [r for r in subset if n in repo[r].user().lower()]
334 390
391 def user(repo, subset, x):
392 """``user(string)``
393 User name is string.
394 """
395 return author(repo, subset, x)
396
335 397 def hasfile(repo, subset, x):
398 """``file(pattern)``
399 Changesets affecting files matched by pattern.
400 """
336 401 # i18n: "file" is a keyword
337 402 pat = getstring(x, _("file requires a pattern"))
338 403 m = matchmod.match(repo.root, repo.getcwd(), [pat])
339 404 s = []
340 405 for r in subset:
341 406 for f in repo[r].files():
342 407 if m(f):
343 408 s.append(r)
344 409 continue
345 410 return s
346 411
347 412 def contains(repo, subset, x):
413 """``contains(pattern)``
414 Revision contains pattern.
415 """
348 416 # i18n: "contains" is a keyword
349 417 pat = getstring(x, _("contains requires a pattern"))
350 418 m = matchmod.match(repo.root, repo.getcwd(), [pat])
351 419 s = []
352 420 if m.files() == [pat]:
353 421 for r in subset:
354 422 if pat in repo[r]:
355 423 s.append(r)
356 424 continue
357 425 else:
358 426 for r in subset:
359 427 for f in repo[r].manifest():
360 428 if m(f):
361 429 s.append(r)
362 430 continue
363 431 return s
364 432
365 433 def checkstatus(repo, subset, pat, field):
366 434 m = matchmod.match(repo.root, repo.getcwd(), [pat])
367 435 s = []
368 436 fast = (m.files() == [pat])
369 437 for r in subset:
370 438 c = repo[r]
371 439 if fast:
372 440 if pat not in c.files():
373 441 continue
374 442 else:
375 443 for f in c.files():
376 444 if m(f):
377 445 break
378 446 else:
379 447 continue
380 448 files = repo.status(c.p1().node(), c.node())[field]
381 449 if fast:
382 450 if pat in files:
383 451 s.append(r)
384 452 continue
385 453 else:
386 454 for f in files:
387 455 if m(f):
388 456 s.append(r)
389 457 continue
390 458 return s
391 459
392 460 def modifies(repo, subset, x):
461 """``modifies(pattern)``
462 Changesets modifying files matched by pattern.
463 """
393 464 # i18n: "modifies" is a keyword
394 465 pat = getstring(x, _("modifies requires a pattern"))
395 466 return checkstatus(repo, subset, pat, 0)
396 467
397 468 def adds(repo, subset, x):
469 """``adds(pattern)``
470 Changesets that add a file matching pattern.
471 """
398 472 # i18n: "adds" is a keyword
399 473 pat = getstring(x, _("adds requires a pattern"))
400 474 return checkstatus(repo, subset, pat, 1)
401 475
402 476 def removes(repo, subset, x):
477 """``removes(pattern)``
478 Changesets which remove files matching pattern.
479 """
403 480 # i18n: "removes" is a keyword
404 481 pat = getstring(x, _("removes requires a pattern"))
405 482 return checkstatus(repo, subset, pat, 2)
406 483
407 484 def merge(repo, subset, x):
485 """``merge()``
486 Changeset is a merge changeset.
487 """
408 488 # i18n: "merge" is a keyword
409 489 getargs(x, 0, 0, _("merge takes no arguments"))
410 490 cl = repo.changelog
411 491 return [r for r in subset if cl.parentrevs(r)[1] != -1]
412 492
413 493 def closed(repo, subset, x):
494 """``closed()``
495 Changeset is closed.
496 """
414 497 # i18n: "closed" is a keyword
415 498 getargs(x, 0, 0, _("closed takes no arguments"))
416 499 return [r for r in subset if repo[r].extra().get('close')]
417 500
418 501 def head(repo, subset, x):
502 """``head()``
503 Changeset is a named branch head.
504 """
419 505 # i18n: "head" is a keyword
420 506 getargs(x, 0, 0, _("head takes no arguments"))
421 507 hs = set()
422 508 for b, ls in repo.branchmap().iteritems():
423 509 hs.update(repo[h].rev() for h in ls)
424 510 return [r for r in subset if r in hs]
425 511
426 512 def reverse(repo, subset, x):
513 """``reverse(set)``
514 Reverse order of set.
515 """
427 516 l = getset(repo, subset, x)
428 517 l.reverse()
429 518 return l
430 519
431 520 def present(repo, subset, x):
521 """``present(set)``
522 An empty set, if any revision in set isn't found; otherwise,
523 all revisions in set.
524 """
432 525 try:
433 526 return getset(repo, subset, x)
434 527 except error.RepoLookupError:
435 528 return []
436 529
437 530 def sort(repo, subset, x):
531 """``sort(set[, [-]key...])``
532 Sort set by keys. The default sort order is ascending, specify a key
533 as ``-key`` to sort in descending order.
534
535 The keys can be:
536
537 - ``rev`` for the revision number,
538 - ``branch`` for the branch name,
539 - ``desc`` for the commit message (description),
540 - ``user`` for user name (``author`` can be used as an alias),
541 - ``date`` for the commit date
542 """
438 543 # i18n: "sort" is a keyword
439 544 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
440 545 keys = "rev"
441 546 if len(l) == 2:
442 547 keys = getstring(l[1], _("sort spec must be a string"))
443 548
444 549 s = l[0]
445 550 keys = keys.split()
446 551 l = []
447 552 def invert(s):
448 553 return "".join(chr(255 - ord(c)) for c in s)
449 554 for r in getset(repo, subset, s):
450 555 c = repo[r]
451 556 e = []
452 557 for k in keys:
453 558 if k == 'rev':
454 559 e.append(r)
455 560 elif k == '-rev':
456 561 e.append(-r)
457 562 elif k == 'branch':
458 563 e.append(c.branch())
459 564 elif k == '-branch':
460 565 e.append(invert(c.branch()))
461 566 elif k == 'desc':
462 567 e.append(c.description())
463 568 elif k == '-desc':
464 569 e.append(invert(c.description()))
465 570 elif k in 'user author':
466 571 e.append(c.user())
467 572 elif k in '-user -author':
468 573 e.append(invert(c.user()))
469 574 elif k == 'date':
470 575 e.append(c.date()[0])
471 576 elif k == '-date':
472 577 e.append(-c.date()[0])
473 578 else:
474 579 raise error.ParseError(_("unknown sort key %r") % k)
475 580 e.append(r)
476 581 l.append(e)
477 582 l.sort()
478 583 return [e[-1] for e in l]
479 584
480 585 def getall(repo, subset, x):
586 """``all()``
587 All changesets, the same as ``0:tip``.
588 """
481 589 # i18n: "all" is a keyword
482 590 getargs(x, 0, 0, _("all takes no arguments"))
483 591 return subset
484 592
485 593 def heads(repo, subset, x):
594 """``heads(set)``
595 Members of set with no children in set.
596 """
486 597 s = getset(repo, subset, x)
487 598 ps = set(parents(repo, subset, x))
488 599 return [r for r in s if r not in ps]
489 600
490 601 def roots(repo, subset, x):
602 """``roots(set)``
603 Changesets with no parent changeset in set.
604 """
491 605 s = getset(repo, subset, x)
492 606 cs = set(children(repo, subset, x))
493 607 return [r for r in s if r not in cs]
494 608
495 609 def outgoing(repo, subset, x):
610 """``outgoing([path])``
611 Changesets not found in the specified destination repository, or the
612 default push location.
613 """
496 614 import hg # avoid start-up nasties
497 615 # i18n: "outgoing" is a keyword
498 616 l = getargs(x, 0, 1, _("outgoing requires a repository path"))
499 617 # i18n: "outgoing" is a keyword
500 618 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
501 619 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
502 620 dest, branches = hg.parseurl(dest)
503 621 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
504 622 if revs:
505 623 revs = [repo.lookup(rev) for rev in revs]
506 624 other = hg.repository(hg.remoteui(repo, {}), dest)
507 625 repo.ui.pushbuffer()
508 626 o = discovery.findoutgoing(repo, other)
509 627 repo.ui.popbuffer()
510 628 cl = repo.changelog
511 629 o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, revs)[0]])
512 630 return [r for r in subset if r in o]
513 631
514 632 def tag(repo, subset, x):
633 """``tag(name)``
634 The specified tag by name, or all tagged revisions if no name is given.
635 """
515 636 # i18n: "tag" is a keyword
516 637 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
517 638 cl = repo.changelog
518 639 if args:
519 640 tn = getstring(args[0],
520 641 # i18n: "tag" is a keyword
521 642 _('the argument to tag must be a string'))
522 643 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
523 644 else:
524 645 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
525 646 return [r for r in subset if r in s]
526 647
648 def tagged(repo, subset, x):
649 return tag(repo, subset, x)
650
527 651 symbols = {
528 652 "adds": adds,
529 653 "all": getall,
530 654 "ancestor": ancestor,
531 655 "ancestors": ancestors,
532 656 "author": author,
533 657 "branch": branch,
534 658 "children": children,
535 659 "closed": closed,
536 660 "contains": contains,
537 661 "date": date,
538 662 "descendants": descendants,
539 663 "file": hasfile,
540 664 "follow": follow,
541 665 "grep": grep,
542 666 "head": head,
543 667 "heads": heads,
544 668 "keyword": keyword,
545 669 "limit": limit,
546 670 "max": maxrev,
547 671 "min": minrev,
548 672 "merge": merge,
549 673 "modifies": modifies,
550 674 "id": node,
551 675 "outgoing": outgoing,
552 676 "p1": p1,
553 677 "p2": p2,
554 678 "parents": parents,
555 679 "present": present,
556 680 "removes": removes,
557 681 "reverse": reverse,
558 682 "rev": rev,
559 683 "roots": roots,
560 684 "sort": sort,
561 685 "tag": tag,
562 "tagged": tag,
563 "user": author,
686 "tagged": tagged,
687 "user": user,
564 688 }
565 689
566 690 methods = {
567 691 "range": rangeset,
568 692 "string": stringset,
569 693 "symbol": symbolset,
570 694 "and": andset,
571 695 "or": orset,
572 696 "not": notset,
573 697 "list": listset,
574 698 "func": func,
575 699 }
576 700
577 701 def optimize(x, small):
578 702 if x == None:
579 703 return 0, x
580 704
581 705 smallbonus = 1
582 706 if small:
583 707 smallbonus = .5
584 708
585 709 op = x[0]
586 710 if op == 'minus':
587 711 return optimize(('and', x[1], ('not', x[2])), small)
588 712 elif op == 'dagrange':
589 713 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
590 714 ('func', ('symbol', 'ancestors'), x[2])), small)
591 715 elif op == 'dagrangepre':
592 716 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
593 717 elif op == 'dagrangepost':
594 718 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
595 719 elif op == 'rangepre':
596 720 return optimize(('range', ('string', '0'), x[1]), small)
597 721 elif op == 'rangepost':
598 722 return optimize(('range', x[1], ('string', 'tip')), small)
599 723 elif op == 'negate':
600 724 return optimize(('string',
601 725 '-' + getstring(x[1], _("can't negate that"))), small)
602 726 elif op in 'string symbol negate':
603 727 return smallbonus, x # single revisions are small
604 728 elif op == 'and' or op == 'dagrange':
605 729 wa, ta = optimize(x[1], True)
606 730 wb, tb = optimize(x[2], True)
607 731 w = min(wa, wb)
608 732 if wa > wb:
609 733 return w, (op, tb, ta)
610 734 return w, (op, ta, tb)
611 735 elif op == 'or':
612 736 wa, ta = optimize(x[1], False)
613 737 wb, tb = optimize(x[2], False)
614 738 if wb < wa:
615 739 wb, wa = wa, wb
616 740 return max(wa, wb), (op, ta, tb)
617 741 elif op == 'not':
618 742 o = optimize(x[1], not small)
619 743 return o[0], (op, o[1])
620 744 elif op == 'group':
621 745 return optimize(x[1], small)
622 746 elif op in 'range list':
623 747 wa, ta = optimize(x[1], small)
624 748 wb, tb = optimize(x[2], small)
625 749 return wa + wb, (op, ta, tb)
626 750 elif op == 'func':
627 751 f = getstring(x[1], _("not a symbol"))
628 752 wa, ta = optimize(x[2], small)
629 753 if f in "grep date user author keyword branch file outgoing":
630 754 w = 10 # slow
631 755 elif f in "modifies adds removes":
632 756 w = 30 # slower
633 757 elif f == "contains":
634 758 w = 100 # very slow
635 759 elif f == "ancestor":
636 760 w = 1 * smallbonus
637 761 elif f == "reverse limit":
638 762 w = 0
639 763 elif f in "sort":
640 764 w = 10 # assume most sorts look at changelog
641 765 else:
642 766 w = 1
643 767 return w + wa, (op, x[1], ta)
644 768 return 1, x
645 769
646 770 parse = parser.parser(tokenize, elements).parse
647 771
648 772 def match(spec):
649 773 if not spec:
650 774 raise error.ParseError(_("empty query"))
651 775 tree = parse(spec)
652 776 weight, tree = optimize(tree, True)
653 777 def mfunc(repo, subset):
654 778 return getset(repo, subset, tree)
655 779 return mfunc
780
781 def makedoc(topic, doc):
782 """Generate and include predicates help in revsets topic."""
783 predicates = []
784 for name in sorted(symbols):
785 text = symbols[name].__doc__
786 if not text:
787 continue
788 lines = text.splitlines()
789 lines[1:] = [(' ' + l.strip()) for l in lines[1:]]
790 predicates.append('\n'.join(lines))
791 predicates = '\n'.join(predicates)
792 doc = doc.replace('.. predicatesmarker', predicates)
793 return doc
General Comments 0
You need to be logged in to leave comments. Login now