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