##// END OF EJS Templates
Move finding/checking the log limit to cmdutil
Thomas Arendsen Hein -
r6190:a79d9408 default
parent child Browse files
Show More
@@ -1,1163 +1,1176 b''
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
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from node import *
9 9 from i18n import _
10 10 import os, sys, bisect, stat
11 11 import mdiff, bdiff, util, templater, templatefilters, patch, errno
12 12
13 13 revrangesep = ':'
14 14
15 15 class UnknownCommand(Exception):
16 16 """Exception raised if command is not in the command table."""
17 17 class AmbiguousCommand(Exception):
18 18 """Exception raised if command shortcut matches more than one command."""
19 19
20 20 def findpossible(ui, cmd, table):
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 = e.lstrip("^").split("|")
30 30 found = None
31 31 if cmd in aliases:
32 32 found = cmd
33 33 elif not ui.config("ui", "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(ui, cmd, table):
50 50 """Return (aliases, command table entry) for command string."""
51 51 choice = findpossible(ui, cmd, table)
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 AmbiguousCommand(cmd, clist)
60 60
61 61 if choice:
62 62 return choice.values()[0]
63 63
64 64 raise UnknownCommand(cmd)
65 65
66 66 def bail_if_changed(repo):
67 67 if repo.dirstate.parents()[1] != nullid:
68 68 raise util.Abort(_('outstanding uncommitted merge'))
69 69 modified, added, removed, deleted = repo.status()[:4]
70 70 if modified or added or removed or deleted:
71 71 raise util.Abort(_("outstanding uncommitted changes"))
72 72
73 73 def logmessage(opts):
74 74 """ get the log message according to -m and -l option """
75 75 message = opts['message']
76 76 logfile = opts['logfile']
77 77
78 78 if message and logfile:
79 79 raise util.Abort(_('options --message and --logfile are mutually '
80 80 'exclusive'))
81 81 if not message and logfile:
82 82 try:
83 83 if logfile == '-':
84 84 message = sys.stdin.read()
85 85 else:
86 86 message = open(logfile).read()
87 87 except IOError, inst:
88 88 raise util.Abort(_("can't read commit message '%s': %s") %
89 89 (logfile, inst.strerror))
90 90 return message
91 91
92 def loglimit(opts):
93 """get the log limit according to option -l/--limit"""
94 limit = opts.get('limit')
95 if limit:
96 try:
97 limit = int(limit)
98 except ValueError:
99 raise util.Abort(_('limit must be a positive integer'))
100 if limit <= 0: raise util.Abort(_('limit must be positive'))
101 else:
102 limit = sys.maxint
103 return limit
104
92 105 def setremoteconfig(ui, opts):
93 106 "copy remote options to ui tree"
94 107 if opts.get('ssh'):
95 108 ui.setconfig("ui", "ssh", opts['ssh'])
96 109 if opts.get('remotecmd'):
97 110 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
98 111
99 112 def revpair(repo, revs):
100 113 '''return pair of nodes, given list of revisions. second item can
101 114 be None, meaning use working dir.'''
102 115
103 116 def revfix(repo, val, defval):
104 117 if not val and val != 0 and defval is not None:
105 118 val = defval
106 119 return repo.lookup(val)
107 120
108 121 if not revs:
109 122 return repo.dirstate.parents()[0], None
110 123 end = None
111 124 if len(revs) == 1:
112 125 if revrangesep in revs[0]:
113 126 start, end = revs[0].split(revrangesep, 1)
114 127 start = revfix(repo, start, 0)
115 128 end = revfix(repo, end, repo.changelog.count() - 1)
116 129 else:
117 130 start = revfix(repo, revs[0], None)
118 131 elif len(revs) == 2:
119 132 if revrangesep in revs[0] or revrangesep in revs[1]:
120 133 raise util.Abort(_('too many revisions specified'))
121 134 start = revfix(repo, revs[0], None)
122 135 end = revfix(repo, revs[1], None)
123 136 else:
124 137 raise util.Abort(_('too many revisions specified'))
125 138 return start, end
126 139
127 140 def revrange(repo, revs):
128 141 """Yield revision as strings from a list of revision specifications."""
129 142
130 143 def revfix(repo, val, defval):
131 144 if not val and val != 0 and defval is not None:
132 145 return defval
133 146 return repo.changelog.rev(repo.lookup(val))
134 147
135 148 seen, l = {}, []
136 149 for spec in revs:
137 150 if revrangesep in spec:
138 151 start, end = spec.split(revrangesep, 1)
139 152 start = revfix(repo, start, 0)
140 153 end = revfix(repo, end, repo.changelog.count() - 1)
141 154 step = start > end and -1 or 1
142 155 for rev in xrange(start, end+step, step):
143 156 if rev in seen:
144 157 continue
145 158 seen[rev] = 1
146 159 l.append(rev)
147 160 else:
148 161 rev = revfix(repo, spec, None)
149 162 if rev in seen:
150 163 continue
151 164 seen[rev] = 1
152 165 l.append(rev)
153 166
154 167 return l
155 168
156 169 def make_filename(repo, pat, node,
157 170 total=None, seqno=None, revwidth=None, pathname=None):
158 171 node_expander = {
159 172 'H': lambda: hex(node),
160 173 'R': lambda: str(repo.changelog.rev(node)),
161 174 'h': lambda: short(node),
162 175 }
163 176 expander = {
164 177 '%': lambda: '%',
165 178 'b': lambda: os.path.basename(repo.root),
166 179 }
167 180
168 181 try:
169 182 if node:
170 183 expander.update(node_expander)
171 184 if node:
172 185 expander['r'] = (lambda:
173 186 str(repo.changelog.rev(node)).zfill(revwidth or 0))
174 187 if total is not None:
175 188 expander['N'] = lambda: str(total)
176 189 if seqno is not None:
177 190 expander['n'] = lambda: str(seqno)
178 191 if total is not None and seqno is not None:
179 192 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
180 193 if pathname is not None:
181 194 expander['s'] = lambda: os.path.basename(pathname)
182 195 expander['d'] = lambda: os.path.dirname(pathname) or '.'
183 196 expander['p'] = lambda: pathname
184 197
185 198 newname = []
186 199 patlen = len(pat)
187 200 i = 0
188 201 while i < patlen:
189 202 c = pat[i]
190 203 if c == '%':
191 204 i += 1
192 205 c = pat[i]
193 206 c = expander[c]()
194 207 newname.append(c)
195 208 i += 1
196 209 return ''.join(newname)
197 210 except KeyError, inst:
198 211 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
199 212 inst.args[0])
200 213
201 214 def make_file(repo, pat, node=None,
202 215 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
203 216 if not pat or pat == '-':
204 217 return 'w' in mode and sys.stdout or sys.stdin
205 218 if hasattr(pat, 'write') and 'w' in mode:
206 219 return pat
207 220 if hasattr(pat, 'read') and 'r' in mode:
208 221 return pat
209 222 return open(make_filename(repo, pat, node, total, seqno, revwidth,
210 223 pathname),
211 224 mode)
212 225
213 226 def matchpats(repo, pats=[], opts={}, globbed=False, default=None):
214 227 cwd = repo.getcwd()
215 228 return util.cmdmatcher(repo.root, cwd, pats or [], opts.get('include'),
216 229 opts.get('exclude'), globbed=globbed,
217 230 default=default)
218 231
219 232 def walk(repo, pats=[], opts={}, node=None, badmatch=None, globbed=False,
220 233 default=None):
221 234 files, matchfn, anypats = matchpats(repo, pats, opts, globbed=globbed,
222 235 default=default)
223 236 exact = dict.fromkeys(files)
224 237 cwd = repo.getcwd()
225 238 for src, fn in repo.walk(node=node, files=files, match=matchfn,
226 239 badmatch=badmatch):
227 240 yield src, fn, repo.pathto(fn, cwd), fn in exact
228 241
229 242 def findrenames(repo, added=None, removed=None, threshold=0.5):
230 243 '''find renamed files -- yields (before, after, score) tuples'''
231 244 if added is None or removed is None:
232 245 added, removed = repo.status()[1:3]
233 246 ctx = repo.changectx()
234 247 for a in added:
235 248 aa = repo.wread(a)
236 249 bestname, bestscore = None, threshold
237 250 for r in removed:
238 251 rr = ctx.filectx(r).data()
239 252
240 253 # bdiff.blocks() returns blocks of matching lines
241 254 # count the number of bytes in each
242 255 equal = 0
243 256 alines = mdiff.splitnewlines(aa)
244 257 matches = bdiff.blocks(aa, rr)
245 258 for x1,x2,y1,y2 in matches:
246 259 for line in alines[x1:x2]:
247 260 equal += len(line)
248 261
249 262 lengths = len(aa) + len(rr)
250 263 if lengths:
251 264 myscore = equal*2.0 / lengths
252 265 if myscore >= bestscore:
253 266 bestname, bestscore = r, myscore
254 267 if bestname:
255 268 yield bestname, a, bestscore
256 269
257 270 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
258 271 if dry_run is None:
259 272 dry_run = opts.get('dry_run')
260 273 if similarity is None:
261 274 similarity = float(opts.get('similarity') or 0)
262 275 add, remove = [], []
263 276 mapping = {}
264 277 for src, abs, rel, exact in walk(repo, pats, opts):
265 278 target = repo.wjoin(abs)
266 279 if src == 'f' and abs not in repo.dirstate:
267 280 add.append(abs)
268 281 mapping[abs] = rel, exact
269 282 if repo.ui.verbose or not exact:
270 283 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
271 284 if repo.dirstate[abs] != 'r' and (not util.lexists(target)
272 285 or (os.path.isdir(target) and not os.path.islink(target))):
273 286 remove.append(abs)
274 287 mapping[abs] = rel, exact
275 288 if repo.ui.verbose or not exact:
276 289 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
277 290 if not dry_run:
278 291 repo.remove(remove)
279 292 repo.add(add)
280 293 if similarity > 0:
281 294 for old, new, score in findrenames(repo, add, remove, similarity):
282 295 oldrel, oldexact = mapping[old]
283 296 newrel, newexact = mapping[new]
284 297 if repo.ui.verbose or not oldexact or not newexact:
285 298 repo.ui.status(_('recording removal of %s as rename to %s '
286 299 '(%d%% similar)\n') %
287 300 (oldrel, newrel, score * 100))
288 301 if not dry_run:
289 302 repo.copy(old, new)
290 303
291 304 def copy(ui, repo, pats, opts, rename=False):
292 305 # called with the repo lock held
293 306 #
294 307 # hgsep => pathname that uses "/" to separate directories
295 308 # ossep => pathname that uses os.sep to separate directories
296 309 cwd = repo.getcwd()
297 310 targets = {}
298 311 after = opts.get("after")
299 312 dryrun = opts.get("dry_run")
300 313
301 314 def walkpat(pat):
302 315 srcs = []
303 316 for tag, abs, rel, exact in walk(repo, [pat], opts, globbed=True):
304 317 state = repo.dirstate[abs]
305 318 if state in '?r':
306 319 if exact and state == '?':
307 320 ui.warn(_('%s: not copying - file is not managed\n') % rel)
308 321 if exact and state == 'r':
309 322 ui.warn(_('%s: not copying - file has been marked for'
310 323 ' remove\n') % rel)
311 324 continue
312 325 # abs: hgsep
313 326 # rel: ossep
314 327 srcs.append((abs, rel, exact))
315 328 return srcs
316 329
317 330 # abssrc: hgsep
318 331 # relsrc: ossep
319 332 # otarget: ossep
320 333 def copyfile(abssrc, relsrc, otarget, exact):
321 334 abstarget = util.canonpath(repo.root, cwd, otarget)
322 335 reltarget = repo.pathto(abstarget, cwd)
323 336 target = repo.wjoin(abstarget)
324 337 src = repo.wjoin(abssrc)
325 338 state = repo.dirstate[abstarget]
326 339
327 340 # check for collisions
328 341 prevsrc = targets.get(abstarget)
329 342 if prevsrc is not None:
330 343 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
331 344 (reltarget, repo.pathto(abssrc, cwd),
332 345 repo.pathto(prevsrc, cwd)))
333 346 return
334 347
335 348 # check for overwrites
336 349 exists = os.path.exists(target)
337 350 if (not after and exists or after and state in 'mn'):
338 351 if not opts['force']:
339 352 ui.warn(_('%s: not overwriting - file exists\n') %
340 353 reltarget)
341 354 return
342 355
343 356 if after:
344 357 if not exists:
345 358 return
346 359 elif not dryrun:
347 360 try:
348 361 if exists:
349 362 os.unlink(target)
350 363 targetdir = os.path.dirname(target) or '.'
351 364 if not os.path.isdir(targetdir):
352 365 os.makedirs(targetdir)
353 366 util.copyfile(src, target)
354 367 except IOError, inst:
355 368 if inst.errno == errno.ENOENT:
356 369 ui.warn(_('%s: deleted in working copy\n') % relsrc)
357 370 else:
358 371 ui.warn(_('%s: cannot copy - %s\n') %
359 372 (relsrc, inst.strerror))
360 373 return True # report a failure
361 374
362 375 if ui.verbose or not exact:
363 376 action = rename and "moving" or "copying"
364 377 ui.status(_('%s %s to %s\n') % (action, relsrc, reltarget))
365 378
366 379 targets[abstarget] = abssrc
367 380
368 381 # fix up dirstate
369 382 origsrc = repo.dirstate.copied(abssrc) or abssrc
370 383 if abstarget == origsrc: # copying back a copy?
371 384 if state not in 'mn' and not dryrun:
372 385 repo.dirstate.normallookup(abstarget)
373 386 else:
374 387 if repo.dirstate[origsrc] == 'a':
375 388 if not ui.quiet:
376 389 ui.warn(_("%s has not been committed yet, so no copy "
377 390 "data will be stored for %s.\n")
378 391 % (repo.pathto(origsrc, cwd), reltarget))
379 392 if abstarget not in repo.dirstate and not dryrun:
380 393 repo.add([abstarget])
381 394 elif not dryrun:
382 395 repo.copy(origsrc, abstarget)
383 396
384 397 if rename and not dryrun:
385 398 repo.remove([abssrc], True)
386 399
387 400 # pat: ossep
388 401 # dest ossep
389 402 # srcs: list of (hgsep, hgsep, ossep, bool)
390 403 # return: function that takes hgsep and returns ossep
391 404 def targetpathfn(pat, dest, srcs):
392 405 if os.path.isdir(pat):
393 406 abspfx = util.canonpath(repo.root, cwd, pat)
394 407 abspfx = util.localpath(abspfx)
395 408 if destdirexists:
396 409 striplen = len(os.path.split(abspfx)[0])
397 410 else:
398 411 striplen = len(abspfx)
399 412 if striplen:
400 413 striplen += len(os.sep)
401 414 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
402 415 elif destdirexists:
403 416 res = lambda p: os.path.join(dest,
404 417 os.path.basename(util.localpath(p)))
405 418 else:
406 419 res = lambda p: dest
407 420 return res
408 421
409 422 # pat: ossep
410 423 # dest ossep
411 424 # srcs: list of (hgsep, hgsep, ossep, bool)
412 425 # return: function that takes hgsep and returns ossep
413 426 def targetpathafterfn(pat, dest, srcs):
414 427 if util.patkind(pat, None)[0]:
415 428 # a mercurial pattern
416 429 res = lambda p: os.path.join(dest,
417 430 os.path.basename(util.localpath(p)))
418 431 else:
419 432 abspfx = util.canonpath(repo.root, cwd, pat)
420 433 if len(abspfx) < len(srcs[0][0]):
421 434 # A directory. Either the target path contains the last
422 435 # component of the source path or it does not.
423 436 def evalpath(striplen):
424 437 score = 0
425 438 for s in srcs:
426 439 t = os.path.join(dest, util.localpath(s[0])[striplen:])
427 440 if os.path.exists(t):
428 441 score += 1
429 442 return score
430 443
431 444 abspfx = util.localpath(abspfx)
432 445 striplen = len(abspfx)
433 446 if striplen:
434 447 striplen += len(os.sep)
435 448 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
436 449 score = evalpath(striplen)
437 450 striplen1 = len(os.path.split(abspfx)[0])
438 451 if striplen1:
439 452 striplen1 += len(os.sep)
440 453 if evalpath(striplen1) > score:
441 454 striplen = striplen1
442 455 res = lambda p: os.path.join(dest,
443 456 util.localpath(p)[striplen:])
444 457 else:
445 458 # a file
446 459 if destdirexists:
447 460 res = lambda p: os.path.join(dest,
448 461 os.path.basename(util.localpath(p)))
449 462 else:
450 463 res = lambda p: dest
451 464 return res
452 465
453 466
454 467 pats = util.expand_glob(pats)
455 468 if not pats:
456 469 raise util.Abort(_('no source or destination specified'))
457 470 if len(pats) == 1:
458 471 raise util.Abort(_('no destination specified'))
459 472 dest = pats.pop()
460 473 destdirexists = os.path.isdir(dest)
461 474 if not destdirexists:
462 475 if len(pats) > 1 or util.patkind(pats[0], None)[0]:
463 476 raise util.Abort(_('with multiple sources, destination must be an '
464 477 'existing directory'))
465 478 if util.endswithsep(dest):
466 479 raise util.Abort(_('destination %s is not a directory') % dest)
467 480
468 481 tfn = targetpathfn
469 482 if after:
470 483 tfn = targetpathafterfn
471 484 copylist = []
472 485 for pat in pats:
473 486 srcs = walkpat(pat)
474 487 if not srcs:
475 488 continue
476 489 copylist.append((tfn(pat, dest, srcs), srcs))
477 490 if not copylist:
478 491 raise util.Abort(_('no files to copy'))
479 492
480 493 errors = 0
481 494 for targetpath, srcs in copylist:
482 495 for abssrc, relsrc, exact in srcs:
483 496 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
484 497 errors += 1
485 498
486 499 if errors:
487 500 ui.warn(_('(consider using --after)\n'))
488 501
489 502 return errors
490 503
491 504 def service(opts, parentfn=None, initfn=None, runfn=None):
492 505 '''Run a command as a service.'''
493 506
494 507 if opts['daemon'] and not opts['daemon_pipefds']:
495 508 rfd, wfd = os.pipe()
496 509 args = sys.argv[:]
497 510 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
498 511 # Don't pass --cwd to the child process, because we've already
499 512 # changed directory.
500 513 for i in xrange(1,len(args)):
501 514 if args[i].startswith('--cwd='):
502 515 del args[i]
503 516 break
504 517 elif args[i].startswith('--cwd'):
505 518 del args[i:i+2]
506 519 break
507 520 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
508 521 args[0], args)
509 522 os.close(wfd)
510 523 os.read(rfd, 1)
511 524 if parentfn:
512 525 return parentfn(pid)
513 526 else:
514 527 os._exit(0)
515 528
516 529 if initfn:
517 530 initfn()
518 531
519 532 if opts['pid_file']:
520 533 fp = open(opts['pid_file'], 'w')
521 534 fp.write(str(os.getpid()) + '\n')
522 535 fp.close()
523 536
524 537 if opts['daemon_pipefds']:
525 538 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
526 539 os.close(rfd)
527 540 try:
528 541 os.setsid()
529 542 except AttributeError:
530 543 pass
531 544 os.write(wfd, 'y')
532 545 os.close(wfd)
533 546 sys.stdout.flush()
534 547 sys.stderr.flush()
535 548 fd = os.open(util.nulldev, os.O_RDWR)
536 549 if fd != 0: os.dup2(fd, 0)
537 550 if fd != 1: os.dup2(fd, 1)
538 551 if fd != 2: os.dup2(fd, 2)
539 552 if fd not in (0, 1, 2): os.close(fd)
540 553
541 554 if runfn:
542 555 return runfn()
543 556
544 557 class changeset_printer(object):
545 558 '''show changeset information when templating not requested.'''
546 559
547 560 def __init__(self, ui, repo, patch, buffered):
548 561 self.ui = ui
549 562 self.repo = repo
550 563 self.buffered = buffered
551 564 self.patch = patch
552 565 self.header = {}
553 566 self.hunk = {}
554 567 self.lastheader = None
555 568
556 569 def flush(self, rev):
557 570 if rev in self.header:
558 571 h = self.header[rev]
559 572 if h != self.lastheader:
560 573 self.lastheader = h
561 574 self.ui.write(h)
562 575 del self.header[rev]
563 576 if rev in self.hunk:
564 577 self.ui.write(self.hunk[rev])
565 578 del self.hunk[rev]
566 579 return 1
567 580 return 0
568 581
569 582 def show(self, rev=0, changenode=None, copies=(), **props):
570 583 if self.buffered:
571 584 self.ui.pushbuffer()
572 585 self._show(rev, changenode, copies, props)
573 586 self.hunk[rev] = self.ui.popbuffer()
574 587 else:
575 588 self._show(rev, changenode, copies, props)
576 589
577 590 def _show(self, rev, changenode, copies, props):
578 591 '''show a single changeset or file revision'''
579 592 log = self.repo.changelog
580 593 if changenode is None:
581 594 changenode = log.node(rev)
582 595 elif not rev:
583 596 rev = log.rev(changenode)
584 597
585 598 if self.ui.quiet:
586 599 self.ui.write("%d:%s\n" % (rev, short(changenode)))
587 600 return
588 601
589 602 changes = log.read(changenode)
590 603 date = util.datestr(changes[2])
591 604 extra = changes[5]
592 605 branch = extra.get("branch")
593 606
594 607 hexfunc = self.ui.debugflag and hex or short
595 608
596 609 parents = [(p, hexfunc(log.node(p)))
597 610 for p in self._meaningful_parentrevs(log, rev)]
598 611
599 612 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
600 613
601 614 # don't show the default branch name
602 615 if branch != 'default':
603 616 branch = util.tolocal(branch)
604 617 self.ui.write(_("branch: %s\n") % branch)
605 618 for tag in self.repo.nodetags(changenode):
606 619 self.ui.write(_("tag: %s\n") % tag)
607 620 for parent in parents:
608 621 self.ui.write(_("parent: %d:%s\n") % parent)
609 622
610 623 if self.ui.debugflag:
611 624 self.ui.write(_("manifest: %d:%s\n") %
612 625 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
613 626 self.ui.write(_("user: %s\n") % changes[1])
614 627 self.ui.write(_("date: %s\n") % date)
615 628
616 629 if self.ui.debugflag:
617 630 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
618 631 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
619 632 files):
620 633 if value:
621 634 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
622 635 elif changes[3] and self.ui.verbose:
623 636 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
624 637 if copies and self.ui.verbose:
625 638 copies = ['%s (%s)' % c for c in copies]
626 639 self.ui.write(_("copies: %s\n") % ' '.join(copies))
627 640
628 641 if extra and self.ui.debugflag:
629 642 extraitems = extra.items()
630 643 extraitems.sort()
631 644 for key, value in extraitems:
632 645 self.ui.write(_("extra: %s=%s\n")
633 646 % (key, value.encode('string_escape')))
634 647
635 648 description = changes[4].strip()
636 649 if description:
637 650 if self.ui.verbose:
638 651 self.ui.write(_("description:\n"))
639 652 self.ui.write(description)
640 653 self.ui.write("\n\n")
641 654 else:
642 655 self.ui.write(_("summary: %s\n") %
643 656 description.splitlines()[0])
644 657 self.ui.write("\n")
645 658
646 659 self.showpatch(changenode)
647 660
648 661 def showpatch(self, node):
649 662 if self.patch:
650 663 prev = self.repo.changelog.parents(node)[0]
651 664 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui,
652 665 opts=patch.diffopts(self.ui))
653 666 self.ui.write("\n")
654 667
655 668 def _meaningful_parentrevs(self, log, rev):
656 669 """Return list of meaningful (or all if debug) parentrevs for rev.
657 670
658 671 For merges (two non-nullrev revisions) both parents are meaningful.
659 672 Otherwise the first parent revision is considered meaningful if it
660 673 is not the preceding revision.
661 674 """
662 675 parents = log.parentrevs(rev)
663 676 if not self.ui.debugflag and parents[1] == nullrev:
664 677 if parents[0] >= rev - 1:
665 678 parents = []
666 679 else:
667 680 parents = [parents[0]]
668 681 return parents
669 682
670 683
671 684 class changeset_templater(changeset_printer):
672 685 '''format changeset information.'''
673 686
674 687 def __init__(self, ui, repo, patch, mapfile, buffered):
675 688 changeset_printer.__init__(self, ui, repo, patch, buffered)
676 689 filters = templatefilters.filters.copy()
677 690 filters['formatnode'] = (ui.debugflag and (lambda x: x)
678 691 or (lambda x: x[:12]))
679 692 self.t = templater.templater(mapfile, filters,
680 693 cache={
681 694 'parent': '{rev}:{node|formatnode} ',
682 695 'manifest': '{rev}:{node|formatnode}',
683 696 'filecopy': '{name} ({source})'})
684 697
685 698 def use_template(self, t):
686 699 '''set template string to use'''
687 700 self.t.cache['changeset'] = t
688 701
689 702 def _show(self, rev, changenode, copies, props):
690 703 '''show a single changeset or file revision'''
691 704 log = self.repo.changelog
692 705 if changenode is None:
693 706 changenode = log.node(rev)
694 707 elif not rev:
695 708 rev = log.rev(changenode)
696 709
697 710 changes = log.read(changenode)
698 711
699 712 def showlist(name, values, plural=None, **args):
700 713 '''expand set of values.
701 714 name is name of key in template map.
702 715 values is list of strings or dicts.
703 716 plural is plural of name, if not simply name + 's'.
704 717
705 718 expansion works like this, given name 'foo'.
706 719
707 720 if values is empty, expand 'no_foos'.
708 721
709 722 if 'foo' not in template map, return values as a string,
710 723 joined by space.
711 724
712 725 expand 'start_foos'.
713 726
714 727 for each value, expand 'foo'. if 'last_foo' in template
715 728 map, expand it instead of 'foo' for last key.
716 729
717 730 expand 'end_foos'.
718 731 '''
719 732 if plural: names = plural
720 733 else: names = name + 's'
721 734 if not values:
722 735 noname = 'no_' + names
723 736 if noname in self.t:
724 737 yield self.t(noname, **args)
725 738 return
726 739 if name not in self.t:
727 740 if isinstance(values[0], str):
728 741 yield ' '.join(values)
729 742 else:
730 743 for v in values:
731 744 yield dict(v, **args)
732 745 return
733 746 startname = 'start_' + names
734 747 if startname in self.t:
735 748 yield self.t(startname, **args)
736 749 vargs = args.copy()
737 750 def one(v, tag=name):
738 751 try:
739 752 vargs.update(v)
740 753 except (AttributeError, ValueError):
741 754 try:
742 755 for a, b in v:
743 756 vargs[a] = b
744 757 except ValueError:
745 758 vargs[name] = v
746 759 return self.t(tag, **vargs)
747 760 lastname = 'last_' + name
748 761 if lastname in self.t:
749 762 last = values.pop()
750 763 else:
751 764 last = None
752 765 for v in values:
753 766 yield one(v)
754 767 if last is not None:
755 768 yield one(last, tag=lastname)
756 769 endname = 'end_' + names
757 770 if endname in self.t:
758 771 yield self.t(endname, **args)
759 772
760 773 def showbranches(**args):
761 774 branch = changes[5].get("branch")
762 775 if branch != 'default':
763 776 branch = util.tolocal(branch)
764 777 return showlist('branch', [branch], plural='branches', **args)
765 778
766 779 def showparents(**args):
767 780 parents = [[('rev', p), ('node', hex(log.node(p)))]
768 781 for p in self._meaningful_parentrevs(log, rev)]
769 782 return showlist('parent', parents, **args)
770 783
771 784 def showtags(**args):
772 785 return showlist('tag', self.repo.nodetags(changenode), **args)
773 786
774 787 def showextras(**args):
775 788 extras = changes[5].items()
776 789 extras.sort()
777 790 for key, value in extras:
778 791 args = args.copy()
779 792 args.update(dict(key=key, value=value))
780 793 yield self.t('extra', **args)
781 794
782 795 def showcopies(**args):
783 796 c = [{'name': x[0], 'source': x[1]} for x in copies]
784 797 return showlist('file_copy', c, plural='file_copies', **args)
785 798
786 799 files = []
787 800 def getfiles():
788 801 if not files:
789 802 files[:] = self.repo.status(
790 803 log.parents(changenode)[0], changenode)[:3]
791 804 return files
792 805 def showfiles(**args):
793 806 return showlist('file', changes[3], **args)
794 807 def showmods(**args):
795 808 return showlist('file_mod', getfiles()[0], **args)
796 809 def showadds(**args):
797 810 return showlist('file_add', getfiles()[1], **args)
798 811 def showdels(**args):
799 812 return showlist('file_del', getfiles()[2], **args)
800 813 def showmanifest(**args):
801 814 args = args.copy()
802 815 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
803 816 node=hex(changes[0])))
804 817 return self.t('manifest', **args)
805 818
806 819 defprops = {
807 820 'author': changes[1],
808 821 'branches': showbranches,
809 822 'date': changes[2],
810 823 'desc': changes[4].strip(),
811 824 'file_adds': showadds,
812 825 'file_dels': showdels,
813 826 'file_mods': showmods,
814 827 'files': showfiles,
815 828 'file_copies': showcopies,
816 829 'manifest': showmanifest,
817 830 'node': hex(changenode),
818 831 'parents': showparents,
819 832 'rev': rev,
820 833 'tags': showtags,
821 834 'extras': showextras,
822 835 }
823 836 props = props.copy()
824 837 props.update(defprops)
825 838
826 839 try:
827 840 if self.ui.debugflag and 'header_debug' in self.t:
828 841 key = 'header_debug'
829 842 elif self.ui.quiet and 'header_quiet' in self.t:
830 843 key = 'header_quiet'
831 844 elif self.ui.verbose and 'header_verbose' in self.t:
832 845 key = 'header_verbose'
833 846 elif 'header' in self.t:
834 847 key = 'header'
835 848 else:
836 849 key = ''
837 850 if key:
838 851 h = templater.stringify(self.t(key, **props))
839 852 if self.buffered:
840 853 self.header[rev] = h
841 854 else:
842 855 self.ui.write(h)
843 856 if self.ui.debugflag and 'changeset_debug' in self.t:
844 857 key = 'changeset_debug'
845 858 elif self.ui.quiet and 'changeset_quiet' in self.t:
846 859 key = 'changeset_quiet'
847 860 elif self.ui.verbose and 'changeset_verbose' in self.t:
848 861 key = 'changeset_verbose'
849 862 else:
850 863 key = 'changeset'
851 864 self.ui.write(templater.stringify(self.t(key, **props)))
852 865 self.showpatch(changenode)
853 866 except KeyError, inst:
854 867 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
855 868 inst.args[0]))
856 869 except SyntaxError, inst:
857 870 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
858 871
859 872 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
860 873 """show one changeset using template or regular display.
861 874
862 875 Display format will be the first non-empty hit of:
863 876 1. option 'template'
864 877 2. option 'style'
865 878 3. [ui] setting 'logtemplate'
866 879 4. [ui] setting 'style'
867 880 If all of these values are either the unset or the empty string,
868 881 regular display via changeset_printer() is done.
869 882 """
870 883 # options
871 884 patch = False
872 885 if opts.get('patch'):
873 886 patch = matchfn or util.always
874 887
875 888 tmpl = opts.get('template')
876 889 mapfile = None
877 890 if tmpl:
878 891 tmpl = templater.parsestring(tmpl, quoted=False)
879 892 else:
880 893 mapfile = opts.get('style')
881 894 # ui settings
882 895 if not mapfile:
883 896 tmpl = ui.config('ui', 'logtemplate')
884 897 if tmpl:
885 898 tmpl = templater.parsestring(tmpl)
886 899 else:
887 900 mapfile = ui.config('ui', 'style')
888 901
889 902 if tmpl or mapfile:
890 903 if mapfile:
891 904 if not os.path.split(mapfile)[0]:
892 905 mapname = (templater.templatepath('map-cmdline.' + mapfile)
893 906 or templater.templatepath(mapfile))
894 907 if mapname: mapfile = mapname
895 908 try:
896 909 t = changeset_templater(ui, repo, patch, mapfile, buffered)
897 910 except SyntaxError, inst:
898 911 raise util.Abort(inst.args[0])
899 912 if tmpl: t.use_template(tmpl)
900 913 return t
901 914 return changeset_printer(ui, repo, patch, buffered)
902 915
903 916 def finddate(ui, repo, date):
904 917 """Find the tipmost changeset that matches the given date spec"""
905 918 df = util.matchdate(date)
906 919 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
907 920 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
908 921 results = {}
909 922 for st, rev, fns in changeiter:
910 923 if st == 'add':
911 924 d = get(rev)[2]
912 925 if df(d[0]):
913 926 results[rev] = d
914 927 elif st == 'iter':
915 928 if rev in results:
916 929 ui.status("Found revision %s from %s\n" %
917 930 (rev, util.datestr(results[rev])))
918 931 return str(rev)
919 932
920 933 raise util.Abort(_("revision matching date not found"))
921 934
922 935 def walkchangerevs(ui, repo, pats, change, opts):
923 936 '''Iterate over files and the revs they changed in.
924 937
925 938 Callers most commonly need to iterate backwards over the history
926 939 it is interested in. Doing so has awful (quadratic-looking)
927 940 performance, so we use iterators in a "windowed" way.
928 941
929 942 We walk a window of revisions in the desired order. Within the
930 943 window, we first walk forwards to gather data, then in the desired
931 944 order (usually backwards) to display it.
932 945
933 946 This function returns an (iterator, matchfn) tuple. The iterator
934 947 yields 3-tuples. They will be of one of the following forms:
935 948
936 949 "window", incrementing, lastrev: stepping through a window,
937 950 positive if walking forwards through revs, last rev in the
938 951 sequence iterated over - use to reset state for the current window
939 952
940 953 "add", rev, fns: out-of-order traversal of the given file names
941 954 fns, which changed during revision rev - use to gather data for
942 955 possible display
943 956
944 957 "iter", rev, None: in-order traversal of the revs earlier iterated
945 958 over with "add" - use to display data'''
946 959
947 960 def increasing_windows(start, end, windowsize=8, sizelimit=512):
948 961 if start < end:
949 962 while start < end:
950 963 yield start, min(windowsize, end-start)
951 964 start += windowsize
952 965 if windowsize < sizelimit:
953 966 windowsize *= 2
954 967 else:
955 968 while start > end:
956 969 yield start, min(windowsize, start-end-1)
957 970 start -= windowsize
958 971 if windowsize < sizelimit:
959 972 windowsize *= 2
960 973
961 974 files, matchfn, anypats = matchpats(repo, pats, opts)
962 975 follow = opts.get('follow') or opts.get('follow_first')
963 976
964 977 if repo.changelog.count() == 0:
965 978 return [], matchfn
966 979
967 980 if follow:
968 981 defrange = '%s:0' % repo.changectx().rev()
969 982 else:
970 983 defrange = '-1:0'
971 984 revs = revrange(repo, opts['rev'] or [defrange])
972 985 wanted = {}
973 986 slowpath = anypats or opts.get('removed')
974 987 fncache = {}
975 988
976 989 if not slowpath and not files:
977 990 # No files, no patterns. Display all revs.
978 991 wanted = dict.fromkeys(revs)
979 992 copies = []
980 993 if not slowpath:
981 994 # Only files, no patterns. Check the history of each file.
982 995 def filerevgen(filelog, node):
983 996 cl_count = repo.changelog.count()
984 997 if node is None:
985 998 last = filelog.count() - 1
986 999 else:
987 1000 last = filelog.rev(node)
988 1001 for i, window in increasing_windows(last, nullrev):
989 1002 revs = []
990 1003 for j in xrange(i - window, i + 1):
991 1004 n = filelog.node(j)
992 1005 revs.append((filelog.linkrev(n),
993 1006 follow and filelog.renamed(n)))
994 1007 revs.reverse()
995 1008 for rev in revs:
996 1009 # only yield rev for which we have the changelog, it can
997 1010 # happen while doing "hg log" during a pull or commit
998 1011 if rev[0] < cl_count:
999 1012 yield rev
1000 1013 def iterfiles():
1001 1014 for filename in files:
1002 1015 yield filename, None
1003 1016 for filename_node in copies:
1004 1017 yield filename_node
1005 1018 minrev, maxrev = min(revs), max(revs)
1006 1019 for file_, node in iterfiles():
1007 1020 filelog = repo.file(file_)
1008 1021 # A zero count may be a directory or deleted file, so
1009 1022 # try to find matching entries on the slow path.
1010 1023 if filelog.count() == 0:
1011 1024 slowpath = True
1012 1025 break
1013 1026 for rev, copied in filerevgen(filelog, node):
1014 1027 if rev <= maxrev:
1015 1028 if rev < minrev:
1016 1029 break
1017 1030 fncache.setdefault(rev, [])
1018 1031 fncache[rev].append(file_)
1019 1032 wanted[rev] = 1
1020 1033 if follow and copied:
1021 1034 copies.append(copied)
1022 1035 if slowpath:
1023 1036 if follow:
1024 1037 raise util.Abort(_('can only follow copies/renames for explicit '
1025 1038 'file names'))
1026 1039
1027 1040 # The slow path checks files modified in every changeset.
1028 1041 def changerevgen():
1029 1042 for i, window in increasing_windows(repo.changelog.count()-1,
1030 1043 nullrev):
1031 1044 for j in xrange(i - window, i + 1):
1032 1045 yield j, change(j)[3]
1033 1046
1034 1047 for rev, changefiles in changerevgen():
1035 1048 matches = filter(matchfn, changefiles)
1036 1049 if matches:
1037 1050 fncache[rev] = matches
1038 1051 wanted[rev] = 1
1039 1052
1040 1053 class followfilter:
1041 1054 def __init__(self, onlyfirst=False):
1042 1055 self.startrev = nullrev
1043 1056 self.roots = []
1044 1057 self.onlyfirst = onlyfirst
1045 1058
1046 1059 def match(self, rev):
1047 1060 def realparents(rev):
1048 1061 if self.onlyfirst:
1049 1062 return repo.changelog.parentrevs(rev)[0:1]
1050 1063 else:
1051 1064 return filter(lambda x: x != nullrev,
1052 1065 repo.changelog.parentrevs(rev))
1053 1066
1054 1067 if self.startrev == nullrev:
1055 1068 self.startrev = rev
1056 1069 return True
1057 1070
1058 1071 if rev > self.startrev:
1059 1072 # forward: all descendants
1060 1073 if not self.roots:
1061 1074 self.roots.append(self.startrev)
1062 1075 for parent in realparents(rev):
1063 1076 if parent in self.roots:
1064 1077 self.roots.append(rev)
1065 1078 return True
1066 1079 else:
1067 1080 # backwards: all parents
1068 1081 if not self.roots:
1069 1082 self.roots.extend(realparents(self.startrev))
1070 1083 if rev in self.roots:
1071 1084 self.roots.remove(rev)
1072 1085 self.roots.extend(realparents(rev))
1073 1086 return True
1074 1087
1075 1088 return False
1076 1089
1077 1090 # it might be worthwhile to do this in the iterator if the rev range
1078 1091 # is descending and the prune args are all within that range
1079 1092 for rev in opts.get('prune', ()):
1080 1093 rev = repo.changelog.rev(repo.lookup(rev))
1081 1094 ff = followfilter()
1082 1095 stop = min(revs[0], revs[-1])
1083 1096 for x in xrange(rev, stop-1, -1):
1084 1097 if ff.match(x) and x in wanted:
1085 1098 del wanted[x]
1086 1099
1087 1100 def iterate():
1088 1101 if follow and not files:
1089 1102 ff = followfilter(onlyfirst=opts.get('follow_first'))
1090 1103 def want(rev):
1091 1104 if ff.match(rev) and rev in wanted:
1092 1105 return True
1093 1106 return False
1094 1107 else:
1095 1108 def want(rev):
1096 1109 return rev in wanted
1097 1110
1098 1111 for i, window in increasing_windows(0, len(revs)):
1099 1112 yield 'window', revs[0] < revs[-1], revs[-1]
1100 1113 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1101 1114 srevs = list(nrevs)
1102 1115 srevs.sort()
1103 1116 for rev in srevs:
1104 1117 fns = fncache.get(rev)
1105 1118 if not fns:
1106 1119 def fns_generator():
1107 1120 for f in change(rev)[3]:
1108 1121 if matchfn(f):
1109 1122 yield f
1110 1123 fns = fns_generator()
1111 1124 yield 'add', rev, fns
1112 1125 for rev in nrevs:
1113 1126 yield 'iter', rev, None
1114 1127 return iterate(), matchfn
1115 1128
1116 1129 def commit(ui, repo, commitfunc, pats, opts):
1117 1130 '''commit the specified files or all outstanding changes'''
1118 1131 date = opts.get('date')
1119 1132 if date:
1120 1133 opts['date'] = util.parsedate(date)
1121 1134 message = logmessage(opts)
1122 1135
1123 1136 # extract addremove carefully -- this function can be called from a command
1124 1137 # that doesn't support addremove
1125 1138 if opts.get('addremove'):
1126 1139 addremove(repo, pats, opts)
1127 1140
1128 1141 fns, match, anypats = matchpats(repo, pats, opts)
1129 1142 if pats:
1130 1143 status = repo.status(files=fns, match=match)
1131 1144 modified, added, removed, deleted, unknown = status[:5]
1132 1145 files = modified + added + removed
1133 1146 slist = None
1134 1147 for f in fns:
1135 1148 if f == '.':
1136 1149 continue
1137 1150 if f not in files:
1138 1151 rf = repo.wjoin(f)
1139 1152 rel = repo.pathto(f)
1140 1153 try:
1141 1154 mode = os.lstat(rf)[stat.ST_MODE]
1142 1155 except OSError:
1143 1156 raise util.Abort(_("file %s not found!") % rel)
1144 1157 if stat.S_ISDIR(mode):
1145 1158 name = f + '/'
1146 1159 if slist is None:
1147 1160 slist = list(files)
1148 1161 slist.sort()
1149 1162 i = bisect.bisect(slist, name)
1150 1163 if i >= len(slist) or not slist[i].startswith(name):
1151 1164 raise util.Abort(_("no match under directory %s!")
1152 1165 % rel)
1153 1166 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
1154 1167 raise util.Abort(_("can't commit %s: "
1155 1168 "unsupported file type!") % rel)
1156 1169 elif f not in repo.dirstate:
1157 1170 raise util.Abort(_("file %s not tracked!") % rel)
1158 1171 else:
1159 1172 files = []
1160 1173 try:
1161 1174 return commitfunc(ui, repo, files, message, match, opts)
1162 1175 except ValueError, inst:
1163 1176 raise util.Abort(str(inst))
@@ -1,3169 +1,3162 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from node import *
9 9 from i18n import _
10 10 import os, re, sys, urllib
11 11 import hg, util, revlog, bundlerepo, extensions
12 12 import difflib, patch, time, help, mdiff, tempfile
13 13 import errno, version, socket
14 14 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
15 15
16 16 # Commands start here, listed alphabetically
17 17
18 18 def add(ui, repo, *pats, **opts):
19 19 """add the specified files on the next commit
20 20
21 21 Schedule files to be version controlled and added to the repository.
22 22
23 23 The files will be added to the repository at the next commit. To
24 24 undo an add before that, see hg revert.
25 25
26 26 If no names are given, add all files in the repository.
27 27 """
28 28
29 29 rejected = None
30 30 exacts = {}
31 31 names = []
32 32 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
33 33 badmatch=util.always):
34 34 if exact:
35 35 if ui.verbose:
36 36 ui.status(_('adding %s\n') % rel)
37 37 names.append(abs)
38 38 exacts[abs] = 1
39 39 elif abs not in repo.dirstate:
40 40 ui.status(_('adding %s\n') % rel)
41 41 names.append(abs)
42 42 if not opts.get('dry_run'):
43 43 rejected = repo.add(names)
44 44 rejected = [p for p in rejected if p in exacts]
45 45 return rejected and 1 or 0
46 46
47 47 def addremove(ui, repo, *pats, **opts):
48 48 """add all new files, delete all missing files
49 49
50 50 Add all new files and remove all missing files from the repository.
51 51
52 52 New files are ignored if they match any of the patterns in .hgignore. As
53 53 with add, these changes take effect at the next commit.
54 54
55 55 Use the -s option to detect renamed files. With a parameter > 0,
56 56 this compares every removed file with every added file and records
57 57 those similar enough as renames. This option takes a percentage
58 58 between 0 (disabled) and 100 (files must be identical) as its
59 59 parameter. Detecting renamed files this way can be expensive.
60 60 """
61 61 try:
62 62 sim = float(opts.get('similarity') or 0)
63 63 except ValueError:
64 64 raise util.Abort(_('similarity must be a number'))
65 65 if sim < 0 or sim > 100:
66 66 raise util.Abort(_('similarity must be between 0 and 100'))
67 67 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
68 68
69 69 def annotate(ui, repo, *pats, **opts):
70 70 """show changeset information per file line
71 71
72 72 List changes in files, showing the revision id responsible for each line
73 73
74 74 This command is useful to discover who did a change or when a change took
75 75 place.
76 76
77 77 Without the -a option, annotate will avoid processing files it
78 78 detects as binary. With -a, annotate will generate an annotation
79 79 anyway, probably with undesirable results.
80 80 """
81 81 datefunc = ui.quiet and util.shortdate or util.datestr
82 82 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
83 83
84 84 if not pats:
85 85 raise util.Abort(_('at least one file name or pattern required'))
86 86
87 87 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
88 88 ('number', lambda x: str(x[0].rev())),
89 89 ('changeset', lambda x: short(x[0].node())),
90 90 ('date', getdate),
91 91 ('follow', lambda x: x[0].path()),
92 92 ]
93 93
94 94 if (not opts['user'] and not opts['changeset'] and not opts['date']
95 95 and not opts['follow']):
96 96 opts['number'] = 1
97 97
98 98 linenumber = opts.get('line_number') is not None
99 99 if (linenumber and (not opts['changeset']) and (not opts['number'])):
100 100 raise util.Abort(_('at least one of -n/-c is required for -l'))
101 101
102 102 funcmap = [func for op, func in opmap if opts.get(op)]
103 103 if linenumber:
104 104 lastfunc = funcmap[-1]
105 105 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
106 106
107 107 ctx = repo.changectx(opts['rev'])
108 108
109 109 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
110 110 node=ctx.node()):
111 111 fctx = ctx.filectx(abs)
112 112 if not opts['text'] and util.binary(fctx.data()):
113 113 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
114 114 continue
115 115
116 116 lines = fctx.annotate(follow=opts.get('follow'),
117 117 linenumber=linenumber)
118 118 pieces = []
119 119
120 120 for f in funcmap:
121 121 l = [f(n) for n, dummy in lines]
122 122 if l:
123 123 m = max(map(len, l))
124 124 pieces.append(["%*s" % (m, x) for x in l])
125 125
126 126 if pieces:
127 127 for p, l in zip(zip(*pieces), lines):
128 128 ui.write("%s: %s" % (" ".join(p), l[1]))
129 129
130 130 def archive(ui, repo, dest, **opts):
131 131 '''create unversioned archive of a repository revision
132 132
133 133 By default, the revision used is the parent of the working
134 134 directory; use "-r" to specify a different revision.
135 135
136 136 To specify the type of archive to create, use "-t". Valid
137 137 types are:
138 138
139 139 "files" (default): a directory full of files
140 140 "tar": tar archive, uncompressed
141 141 "tbz2": tar archive, compressed using bzip2
142 142 "tgz": tar archive, compressed using gzip
143 143 "uzip": zip archive, uncompressed
144 144 "zip": zip archive, compressed using deflate
145 145
146 146 The exact name of the destination archive or directory is given
147 147 using a format string; see "hg help export" for details.
148 148
149 149 Each member added to an archive file has a directory prefix
150 150 prepended. Use "-p" to specify a format string for the prefix.
151 151 The default is the basename of the archive, with suffixes removed.
152 152 '''
153 153
154 154 ctx = repo.changectx(opts['rev'])
155 155 if not ctx:
156 156 raise util.Abort(_('repository has no revisions'))
157 157 node = ctx.node()
158 158 dest = cmdutil.make_filename(repo, dest, node)
159 159 if os.path.realpath(dest) == repo.root:
160 160 raise util.Abort(_('repository root cannot be destination'))
161 161 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
162 162 kind = opts.get('type') or 'files'
163 163 prefix = opts['prefix']
164 164 if dest == '-':
165 165 if kind == 'files':
166 166 raise util.Abort(_('cannot archive plain files to stdout'))
167 167 dest = sys.stdout
168 168 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
169 169 prefix = cmdutil.make_filename(repo, prefix, node)
170 170 archival.archive(repo, dest, node, kind, not opts['no_decode'],
171 171 matchfn, prefix)
172 172
173 173 def backout(ui, repo, node=None, rev=None, **opts):
174 174 '''reverse effect of earlier changeset
175 175
176 176 Commit the backed out changes as a new changeset. The new
177 177 changeset is a child of the backed out changeset.
178 178
179 179 If you back out a changeset other than the tip, a new head is
180 180 created. This head will be the new tip and you should merge this
181 181 backout changeset with another head (current one by default).
182 182
183 183 The --merge option remembers the parent of the working directory
184 184 before starting the backout, then merges the new head with that
185 185 changeset afterwards. This saves you from doing the merge by
186 186 hand. The result of this merge is not committed, as for a normal
187 187 merge.
188 188
189 189 See 'hg help dates' for a list of formats valid for -d/--date.
190 190 '''
191 191 if rev and node:
192 192 raise util.Abort(_("please specify just one revision"))
193 193
194 194 if not rev:
195 195 rev = node
196 196
197 197 if not rev:
198 198 raise util.Abort(_("please specify a revision to backout"))
199 199
200 200 date = opts.get('date')
201 201 if date:
202 202 opts['date'] = util.parsedate(date)
203 203
204 204 cmdutil.bail_if_changed(repo)
205 205 node = repo.lookup(rev)
206 206
207 207 op1, op2 = repo.dirstate.parents()
208 208 a = repo.changelog.ancestor(op1, node)
209 209 if a != node:
210 210 raise util.Abort(_('cannot back out change on a different branch'))
211 211
212 212 p1, p2 = repo.changelog.parents(node)
213 213 if p1 == nullid:
214 214 raise util.Abort(_('cannot back out a change with no parents'))
215 215 if p2 != nullid:
216 216 if not opts['parent']:
217 217 raise util.Abort(_('cannot back out a merge changeset without '
218 218 '--parent'))
219 219 p = repo.lookup(opts['parent'])
220 220 if p not in (p1, p2):
221 221 raise util.Abort(_('%s is not a parent of %s') %
222 222 (short(p), short(node)))
223 223 parent = p
224 224 else:
225 225 if opts['parent']:
226 226 raise util.Abort(_('cannot use --parent on non-merge changeset'))
227 227 parent = p1
228 228
229 229 hg.clean(repo, node, show_stats=False)
230 230 revert_opts = opts.copy()
231 231 revert_opts['date'] = None
232 232 revert_opts['all'] = True
233 233 revert_opts['rev'] = hex(parent)
234 234 revert_opts['no_backup'] = None
235 235 revert(ui, repo, **revert_opts)
236 236 commit_opts = opts.copy()
237 237 commit_opts['addremove'] = False
238 238 if not commit_opts['message'] and not commit_opts['logfile']:
239 239 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
240 240 commit_opts['force_editor'] = True
241 241 commit(ui, repo, **commit_opts)
242 242 def nice(node):
243 243 return '%d:%s' % (repo.changelog.rev(node), short(node))
244 244 ui.status(_('changeset %s backs out changeset %s\n') %
245 245 (nice(repo.changelog.tip()), nice(node)))
246 246 if op1 != node:
247 247 hg.clean(repo, op1, show_stats=False)
248 248 if opts['merge']:
249 249 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
250 250 hg.merge(repo, hex(repo.changelog.tip()))
251 251 else:
252 252 ui.status(_('the backout changeset is a new head - '
253 253 'do not forget to merge\n'))
254 254 ui.status(_('(use "backout --merge" '
255 255 'if you want to auto-merge)\n'))
256 256
257 257 def bisect(ui, repo, rev=None, extra=None,
258 258 reset=None, good=None, bad=None, skip=None, noupdate=None):
259 259 """subdivision search of changesets
260 260
261 261 This command helps to find changesets which introduce problems.
262 262 To use, mark the earliest changeset you know exhibits the problem
263 263 as bad, then mark the latest changeset which is free from the
264 264 problem as good. Bisect will update your working directory to a
265 265 revision for testing. Once you have performed tests, mark the
266 266 working directory as bad or good and bisect will either update to
267 267 another candidate changeset or announce that it has found the bad
268 268 revision.
269 269 """
270 270 # backward compatibility
271 271 if rev in "good bad reset init".split():
272 272 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
273 273 cmd, rev, extra = rev, extra, None
274 274 if cmd == "good":
275 275 good = True
276 276 elif cmd == "bad":
277 277 bad = True
278 278 else:
279 279 reset = True
280 280 elif extra or good + bad + skip + reset > 1:
281 281 raise util.Abort("Incompatible arguments")
282 282
283 283 if reset:
284 284 p = repo.join("bisect.state")
285 285 if os.path.exists(p):
286 286 os.unlink(p)
287 287 return
288 288
289 289 # load state
290 290 state = {'good': [], 'bad': [], 'skip': []}
291 291 if os.path.exists(repo.join("bisect.state")):
292 292 for l in repo.opener("bisect.state"):
293 293 kind, node = l[:-1].split()
294 294 node = repo.lookup(node)
295 295 if kind not in state:
296 296 raise util.Abort(_("unknown bisect kind %s") % kind)
297 297 state[kind].append(node)
298 298
299 299 # update state
300 300 node = repo.lookup(rev or '.')
301 301 if good:
302 302 state['good'].append(node)
303 303 elif bad:
304 304 state['bad'].append(node)
305 305 elif skip:
306 306 state['skip'].append(node)
307 307
308 308 # save state
309 309 f = repo.opener("bisect.state", "w", atomictemp=True)
310 310 wlock = repo.wlock()
311 311 try:
312 312 for kind in state:
313 313 for node in state[kind]:
314 314 f.write("%s %s\n" % (kind, hg.hex(node)))
315 315 f.rename()
316 316 finally:
317 317 del wlock
318 318
319 319 if not state['good'] or not state['bad']:
320 320 return
321 321
322 322 # actually bisect
323 323 node, changesets, good = hbisect.bisect(repo.changelog, state)
324 324 if changesets == 0:
325 325 ui.write(_("The first %s revision is:\n") % (good and "good" or "bad"))
326 326 displayer = cmdutil.show_changeset(ui, repo, {})
327 327 displayer.show(changenode=node)
328 328 elif node is not None:
329 329 # compute the approximate number of remaining tests
330 330 tests, size = 0, 2
331 331 while size <= changesets:
332 332 tests, size = tests + 1, size * 2
333 333 rev = repo.changelog.rev(node)
334 334 ui.write(_("Testing changeset %s:%s "
335 335 "(%s changesets remaining, ~%s tests)\n")
336 336 % (rev, hg.short(node), changesets, tests))
337 337 if not noupdate:
338 338 cmdutil.bail_if_changed(repo)
339 339 return hg.clean(repo, node)
340 340
341 341 def branch(ui, repo, label=None, **opts):
342 342 """set or show the current branch name
343 343
344 344 With no argument, show the current branch name. With one argument,
345 345 set the working directory branch name (the branch does not exist in
346 346 the repository until the next commit).
347 347
348 348 Unless --force is specified, branch will not let you set a
349 349 branch name that shadows an existing branch.
350 350
351 351 Use the command 'hg update' to switch to an existing branch.
352 352 """
353 353
354 354 if label:
355 355 if not opts.get('force') and label in repo.branchtags():
356 356 if label not in [p.branch() for p in repo.workingctx().parents()]:
357 357 raise util.Abort(_('a branch of the same name already exists'
358 358 ' (use --force to override)'))
359 359 repo.dirstate.setbranch(util.fromlocal(label))
360 360 ui.status(_('marked working directory as branch %s\n') % label)
361 361 else:
362 362 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
363 363
364 364 def branches(ui, repo, active=False):
365 365 """list repository named branches
366 366
367 367 List the repository's named branches, indicating which ones are
368 368 inactive. If active is specified, only show active branches.
369 369
370 370 A branch is considered active if it contains unmerged heads.
371 371
372 372 Use the command 'hg update' to switch to an existing branch.
373 373 """
374 374 b = repo.branchtags()
375 375 heads = dict.fromkeys(repo.heads(), 1)
376 376 l = [((n in heads), repo.changelog.rev(n), n, t) for t, n in b.items()]
377 377 l.sort()
378 378 l.reverse()
379 379 for ishead, r, n, t in l:
380 380 if active and not ishead:
381 381 # If we're only displaying active branches, abort the loop on
382 382 # encountering the first inactive head
383 383 break
384 384 else:
385 385 hexfunc = ui.debugflag and hex or short
386 386 if ui.quiet:
387 387 ui.write("%s\n" % t)
388 388 else:
389 389 spaces = " " * (30 - util.locallen(t))
390 390 # The code only gets here if inactive branches are being
391 391 # displayed or the branch is active.
392 392 isinactive = ((not ishead) and " (inactive)") or ''
393 393 ui.write("%s%s %s:%s%s\n" % (t, spaces, r, hexfunc(n), isinactive))
394 394
395 395 def bundle(ui, repo, fname, dest=None, **opts):
396 396 """create a changegroup file
397 397
398 398 Generate a compressed changegroup file collecting changesets not
399 399 found in the other repository.
400 400
401 401 If no destination repository is specified the destination is
402 402 assumed to have all the nodes specified by one or more --base
403 403 parameters. To create a bundle containing all changesets, use
404 404 --all (or --base null).
405 405
406 406 The bundle file can then be transferred using conventional means and
407 407 applied to another repository with the unbundle or pull command.
408 408 This is useful when direct push and pull are not available or when
409 409 exporting an entire repository is undesirable.
410 410
411 411 Applying bundles preserves all changeset contents including
412 412 permissions, copy/rename information, and revision history.
413 413 """
414 414 revs = opts.get('rev') or None
415 415 if revs:
416 416 revs = [repo.lookup(rev) for rev in revs]
417 417 if opts.get('all'):
418 418 base = ['null']
419 419 else:
420 420 base = opts.get('base')
421 421 if base:
422 422 if dest:
423 423 raise util.Abort(_("--base is incompatible with specifiying "
424 424 "a destination"))
425 425 base = [repo.lookup(rev) for rev in base]
426 426 # create the right base
427 427 # XXX: nodesbetween / changegroup* should be "fixed" instead
428 428 o = []
429 429 has = {nullid: None}
430 430 for n in base:
431 431 has.update(repo.changelog.reachable(n))
432 432 if revs:
433 433 visit = list(revs)
434 434 else:
435 435 visit = repo.changelog.heads()
436 436 seen = {}
437 437 while visit:
438 438 n = visit.pop(0)
439 439 parents = [p for p in repo.changelog.parents(n) if p not in has]
440 440 if len(parents) == 0:
441 441 o.insert(0, n)
442 442 else:
443 443 for p in parents:
444 444 if p not in seen:
445 445 seen[p] = 1
446 446 visit.append(p)
447 447 else:
448 448 cmdutil.setremoteconfig(ui, opts)
449 449 dest, revs, checkout = hg.parseurl(
450 450 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
451 451 other = hg.repository(ui, dest)
452 452 o = repo.findoutgoing(other, force=opts['force'])
453 453
454 454 if revs:
455 455 cg = repo.changegroupsubset(o, revs, 'bundle')
456 456 else:
457 457 cg = repo.changegroup(o, 'bundle')
458 458 changegroup.writebundle(cg, fname, "HG10BZ")
459 459
460 460 def cat(ui, repo, file1, *pats, **opts):
461 461 """output the current or given revision of files
462 462
463 463 Print the specified files as they were at the given revision.
464 464 If no revision is given, the parent of the working directory is used,
465 465 or tip if no revision is checked out.
466 466
467 467 Output may be to a file, in which case the name of the file is
468 468 given using a format string. The formatting rules are the same as
469 469 for the export command, with the following additions:
470 470
471 471 %s basename of file being printed
472 472 %d dirname of file being printed, or '.' if in repo root
473 473 %p root-relative path name of file being printed
474 474 """
475 475 ctx = repo.changectx(opts['rev'])
476 476 err = 1
477 477 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
478 478 ctx.node()):
479 479 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
480 480 data = ctx.filectx(abs).data()
481 481 if opts.get('decode'):
482 482 data = repo.wwritedata(abs, data)
483 483 fp.write(data)
484 484 err = 0
485 485 return err
486 486
487 487 def clone(ui, source, dest=None, **opts):
488 488 """make a copy of an existing repository
489 489
490 490 Create a copy of an existing repository in a new directory.
491 491
492 492 If no destination directory name is specified, it defaults to the
493 493 basename of the source.
494 494
495 495 The location of the source is added to the new repository's
496 496 .hg/hgrc file, as the default to be used for future pulls.
497 497
498 498 For efficiency, hardlinks are used for cloning whenever the source
499 499 and destination are on the same filesystem (note this applies only
500 500 to the repository data, not to the checked out files). Some
501 501 filesystems, such as AFS, implement hardlinking incorrectly, but
502 502 do not report errors. In these cases, use the --pull option to
503 503 avoid hardlinking.
504 504
505 505 You can safely clone repositories and checked out files using full
506 506 hardlinks with
507 507
508 508 $ cp -al REPO REPOCLONE
509 509
510 510 which is the fastest way to clone. However, the operation is not
511 511 atomic (making sure REPO is not modified during the operation is
512 512 up to you) and you have to make sure your editor breaks hardlinks
513 513 (Emacs and most Linux Kernel tools do so).
514 514
515 515 If you use the -r option to clone up to a specific revision, no
516 516 subsequent revisions will be present in the cloned repository.
517 517 This option implies --pull, even on local repositories.
518 518
519 519 See pull for valid source format details.
520 520
521 521 It is possible to specify an ssh:// URL as the destination, but no
522 522 .hg/hgrc and working directory will be created on the remote side.
523 523 Look at the help text for the pull command for important details
524 524 about ssh:// URLs.
525 525 """
526 526 cmdutil.setremoteconfig(ui, opts)
527 527 hg.clone(ui, source, dest,
528 528 pull=opts['pull'],
529 529 stream=opts['uncompressed'],
530 530 rev=opts['rev'],
531 531 update=not opts['noupdate'])
532 532
533 533 def commit(ui, repo, *pats, **opts):
534 534 """commit the specified files or all outstanding changes
535 535
536 536 Commit changes to the given files into the repository.
537 537
538 538 If a list of files is omitted, all changes reported by "hg status"
539 539 will be committed.
540 540
541 541 If no commit message is specified, the configured editor is started to
542 542 enter a message.
543 543
544 544 See 'hg help dates' for a list of formats valid for -d/--date.
545 545 """
546 546 def commitfunc(ui, repo, files, message, match, opts):
547 547 return repo.commit(files, message, opts['user'], opts['date'], match,
548 548 force_editor=opts.get('force_editor'))
549 549 cmdutil.commit(ui, repo, commitfunc, pats, opts)
550 550
551 551 def copy(ui, repo, *pats, **opts):
552 552 """mark files as copied for the next commit
553 553
554 554 Mark dest as having copies of source files. If dest is a
555 555 directory, copies are put in that directory. If dest is a file,
556 556 there can only be one source.
557 557
558 558 By default, this command copies the contents of files as they
559 559 stand in the working directory. If invoked with --after, the
560 560 operation is recorded, but no copying is performed.
561 561
562 562 This command takes effect in the next commit. To undo a copy
563 563 before that, see hg revert.
564 564 """
565 565 wlock = repo.wlock(False)
566 566 try:
567 567 return cmdutil.copy(ui, repo, pats, opts)
568 568 finally:
569 569 del wlock
570 570
571 571 def debugancestor(ui, repo, *args):
572 572 """find the ancestor revision of two revisions in a given index"""
573 573 if len(args) == 3:
574 574 index, rev1, rev2 = args
575 575 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
576 576 elif len(args) == 2:
577 577 if not repo:
578 578 raise util.Abort(_("There is no Mercurial repository here "
579 579 "(.hg not found)"))
580 580 rev1, rev2 = args
581 581 r = repo.changelog
582 582 else:
583 583 raise util.Abort(_('either two or three arguments required'))
584 584 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
585 585 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
586 586
587 587 def debugcomplete(ui, cmd='', **opts):
588 588 """returns the completion list associated with the given command"""
589 589
590 590 if opts['options']:
591 591 options = []
592 592 otables = [globalopts]
593 593 if cmd:
594 594 aliases, entry = cmdutil.findcmd(ui, cmd, table)
595 595 otables.append(entry[1])
596 596 for t in otables:
597 597 for o in t:
598 598 if o[0]:
599 599 options.append('-%s' % o[0])
600 600 options.append('--%s' % o[1])
601 601 ui.write("%s\n" % "\n".join(options))
602 602 return
603 603
604 604 clist = cmdutil.findpossible(ui, cmd, table).keys()
605 605 clist.sort()
606 606 ui.write("%s\n" % "\n".join(clist))
607 607
608 608 def debugfsinfo(ui, path = "."):
609 609 file('.debugfsinfo', 'w').write('')
610 610 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
611 611 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
612 612 ui.write('case-sensitive: %s\n' % (util.checkfolding('.debugfsinfo')
613 613 and 'yes' or 'no'))
614 614 os.unlink('.debugfsinfo')
615 615
616 616 def debugrebuildstate(ui, repo, rev=""):
617 617 """rebuild the dirstate as it would look like for the given revision"""
618 618 if rev == "":
619 619 rev = repo.changelog.tip()
620 620 ctx = repo.changectx(rev)
621 621 files = ctx.manifest()
622 622 wlock = repo.wlock()
623 623 try:
624 624 repo.dirstate.rebuild(rev, files)
625 625 finally:
626 626 del wlock
627 627
628 628 def debugcheckstate(ui, repo):
629 629 """validate the correctness of the current dirstate"""
630 630 parent1, parent2 = repo.dirstate.parents()
631 631 m1 = repo.changectx(parent1).manifest()
632 632 m2 = repo.changectx(parent2).manifest()
633 633 errors = 0
634 634 for f in repo.dirstate:
635 635 state = repo.dirstate[f]
636 636 if state in "nr" and f not in m1:
637 637 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
638 638 errors += 1
639 639 if state in "a" and f in m1:
640 640 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
641 641 errors += 1
642 642 if state in "m" and f not in m1 and f not in m2:
643 643 ui.warn(_("%s in state %s, but not in either manifest\n") %
644 644 (f, state))
645 645 errors += 1
646 646 for f in m1:
647 647 state = repo.dirstate[f]
648 648 if state not in "nrm":
649 649 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
650 650 errors += 1
651 651 if errors:
652 652 error = _(".hg/dirstate inconsistent with current parent's manifest")
653 653 raise util.Abort(error)
654 654
655 655 def showconfig(ui, repo, *values, **opts):
656 656 """show combined config settings from all hgrc files
657 657
658 658 With no args, print names and values of all config items.
659 659
660 660 With one arg of the form section.name, print just the value of
661 661 that config item.
662 662
663 663 With multiple args, print names and values of all config items
664 664 with matching section names."""
665 665
666 666 untrusted = bool(opts.get('untrusted'))
667 667 if values:
668 668 if len([v for v in values if '.' in v]) > 1:
669 669 raise util.Abort(_('only one config item permitted'))
670 670 for section, name, value in ui.walkconfig(untrusted=untrusted):
671 671 sectname = section + '.' + name
672 672 if values:
673 673 for v in values:
674 674 if v == section:
675 675 ui.write('%s=%s\n' % (sectname, value))
676 676 elif v == sectname:
677 677 ui.write(value, '\n')
678 678 else:
679 679 ui.write('%s=%s\n' % (sectname, value))
680 680
681 681 def debugsetparents(ui, repo, rev1, rev2=None):
682 682 """manually set the parents of the current working directory
683 683
684 684 This is useful for writing repository conversion tools, but should
685 685 be used with care.
686 686 """
687 687
688 688 if not rev2:
689 689 rev2 = hex(nullid)
690 690
691 691 wlock = repo.wlock()
692 692 try:
693 693 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
694 694 finally:
695 695 del wlock
696 696
697 697 def debugstate(ui, repo):
698 698 """show the contents of the current dirstate"""
699 699 k = repo.dirstate._map.items()
700 700 k.sort()
701 701 for file_, ent in k:
702 702 if ent[3] == -1:
703 703 # Pad or slice to locale representation
704 704 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(0)))
705 705 timestr = 'unset'
706 706 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
707 707 else:
708 708 timestr = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(ent[3]))
709 709 if ent[1] & 020000:
710 710 mode = 'lnk'
711 711 else:
712 712 mode = '%3o' % (ent[1] & 0777)
713 713 ui.write("%c %s %10d %s %s\n" % (ent[0], mode, ent[2], timestr, file_))
714 714 for f in repo.dirstate.copies():
715 715 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
716 716
717 717 def debugdata(ui, file_, rev):
718 718 """dump the contents of a data file revision"""
719 719 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
720 720 try:
721 721 ui.write(r.revision(r.lookup(rev)))
722 722 except KeyError:
723 723 raise util.Abort(_('invalid revision identifier %s') % rev)
724 724
725 725 def debugdate(ui, date, range=None, **opts):
726 726 """parse and display a date"""
727 727 if opts["extended"]:
728 728 d = util.parsedate(date, util.extendeddateformats)
729 729 else:
730 730 d = util.parsedate(date)
731 731 ui.write("internal: %s %s\n" % d)
732 732 ui.write("standard: %s\n" % util.datestr(d))
733 733 if range:
734 734 m = util.matchdate(range)
735 735 ui.write("match: %s\n" % m(d[0]))
736 736
737 737 def debugindex(ui, file_):
738 738 """dump the contents of an index file"""
739 739 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
740 740 ui.write(" rev offset length base linkrev" +
741 741 " nodeid p1 p2\n")
742 742 for i in xrange(r.count()):
743 743 node = r.node(i)
744 744 try:
745 745 pp = r.parents(node)
746 746 except:
747 747 pp = [nullid, nullid]
748 748 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
749 749 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
750 750 short(node), short(pp[0]), short(pp[1])))
751 751
752 752 def debugindexdot(ui, file_):
753 753 """dump an index DAG as a .dot file"""
754 754 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
755 755 ui.write("digraph G {\n")
756 756 for i in xrange(r.count()):
757 757 node = r.node(i)
758 758 pp = r.parents(node)
759 759 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
760 760 if pp[1] != nullid:
761 761 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
762 762 ui.write("}\n")
763 763
764 764 def debuginstall(ui):
765 765 '''test Mercurial installation'''
766 766
767 767 def writetemp(contents):
768 768 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
769 769 f = os.fdopen(fd, "wb")
770 770 f.write(contents)
771 771 f.close()
772 772 return name
773 773
774 774 problems = 0
775 775
776 776 # encoding
777 777 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
778 778 try:
779 779 util.fromlocal("test")
780 780 except util.Abort, inst:
781 781 ui.write(" %s\n" % inst)
782 782 ui.write(_(" (check that your locale is properly set)\n"))
783 783 problems += 1
784 784
785 785 # compiled modules
786 786 ui.status(_("Checking extensions...\n"))
787 787 try:
788 788 import bdiff, mpatch, base85
789 789 except Exception, inst:
790 790 ui.write(" %s\n" % inst)
791 791 ui.write(_(" One or more extensions could not be found"))
792 792 ui.write(_(" (check that you compiled the extensions)\n"))
793 793 problems += 1
794 794
795 795 # templates
796 796 ui.status(_("Checking templates...\n"))
797 797 try:
798 798 import templater
799 799 t = templater.templater(templater.templatepath("map-cmdline.default"))
800 800 except Exception, inst:
801 801 ui.write(" %s\n" % inst)
802 802 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
803 803 problems += 1
804 804
805 805 # patch
806 806 ui.status(_("Checking patch...\n"))
807 807 patchproblems = 0
808 808 a = "1\n2\n3\n4\n"
809 809 b = "1\n2\n3\ninsert\n4\n"
810 810 fa = writetemp(a)
811 811 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
812 812 os.path.basename(fa))
813 813 fd = writetemp(d)
814 814
815 815 files = {}
816 816 try:
817 817 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
818 818 except util.Abort, e:
819 819 ui.write(_(" patch call failed:\n"))
820 820 ui.write(" " + str(e) + "\n")
821 821 patchproblems += 1
822 822 else:
823 823 if list(files) != [os.path.basename(fa)]:
824 824 ui.write(_(" unexpected patch output!\n"))
825 825 patchproblems += 1
826 826 a = file(fa).read()
827 827 if a != b:
828 828 ui.write(_(" patch test failed!\n"))
829 829 patchproblems += 1
830 830
831 831 if patchproblems:
832 832 if ui.config('ui', 'patch'):
833 833 ui.write(_(" (Current patch tool may be incompatible with patch,"
834 834 " or misconfigured. Please check your .hgrc file)\n"))
835 835 else:
836 836 ui.write(_(" Internal patcher failure, please report this error"
837 837 " to http://www.selenic.com/mercurial/bts\n"))
838 838 problems += patchproblems
839 839
840 840 os.unlink(fa)
841 841 os.unlink(fd)
842 842
843 843 # editor
844 844 ui.status(_("Checking commit editor...\n"))
845 845 editor = ui.geteditor()
846 846 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
847 847 if not cmdpath:
848 848 if editor == 'vi':
849 849 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
850 850 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
851 851 else:
852 852 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
853 853 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
854 854 problems += 1
855 855
856 856 # check username
857 857 ui.status(_("Checking username...\n"))
858 858 user = os.environ.get("HGUSER")
859 859 if user is None:
860 860 user = ui.config("ui", "username")
861 861 if user is None:
862 862 user = os.environ.get("EMAIL")
863 863 if not user:
864 864 ui.warn(" ")
865 865 ui.username()
866 866 ui.write(_(" (specify a username in your .hgrc file)\n"))
867 867
868 868 if not problems:
869 869 ui.status(_("No problems detected\n"))
870 870 else:
871 871 ui.write(_("%s problems detected,"
872 872 " please check your install!\n") % problems)
873 873
874 874 return problems
875 875
876 876 def debugrename(ui, repo, file1, *pats, **opts):
877 877 """dump rename information"""
878 878
879 879 ctx = repo.changectx(opts.get('rev', 'tip'))
880 880 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
881 881 ctx.node()):
882 882 fctx = ctx.filectx(abs)
883 883 m = fctx.filelog().renamed(fctx.filenode())
884 884 if m:
885 885 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
886 886 else:
887 887 ui.write(_("%s not renamed\n") % rel)
888 888
889 889 def debugwalk(ui, repo, *pats, **opts):
890 890 """show how files match on given patterns"""
891 891 items = list(cmdutil.walk(repo, pats, opts))
892 892 if not items:
893 893 return
894 894 fmt = '%%s %%-%ds %%-%ds %%s' % (
895 895 max([len(abs) for (src, abs, rel, exact) in items]),
896 896 max([len(rel) for (src, abs, rel, exact) in items]))
897 897 for src, abs, rel, exact in items:
898 898 line = fmt % (src, abs, rel, exact and 'exact' or '')
899 899 ui.write("%s\n" % line.rstrip())
900 900
901 901 def diff(ui, repo, *pats, **opts):
902 902 """diff repository (or selected files)
903 903
904 904 Show differences between revisions for the specified files.
905 905
906 906 Differences between files are shown using the unified diff format.
907 907
908 908 NOTE: diff may generate unexpected results for merges, as it will
909 909 default to comparing against the working directory's first parent
910 910 changeset if no revisions are specified.
911 911
912 912 When two revision arguments are given, then changes are shown
913 913 between those revisions. If only one revision is specified then
914 914 that revision is compared to the working directory, and, when no
915 915 revisions are specified, the working directory files are compared
916 916 to its parent.
917 917
918 918 Without the -a option, diff will avoid generating diffs of files
919 919 it detects as binary. With -a, diff will generate a diff anyway,
920 920 probably with undesirable results.
921 921 """
922 922 node1, node2 = cmdutil.revpair(repo, opts['rev'])
923 923
924 924 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
925 925
926 926 patch.diff(repo, node1, node2, fns, match=matchfn,
927 927 opts=patch.diffopts(ui, opts))
928 928
929 929 def export(ui, repo, *changesets, **opts):
930 930 """dump the header and diffs for one or more changesets
931 931
932 932 Print the changeset header and diffs for one or more revisions.
933 933
934 934 The information shown in the changeset header is: author,
935 935 changeset hash, parent(s) and commit comment.
936 936
937 937 NOTE: export may generate unexpected diff output for merge changesets,
938 938 as it will compare the merge changeset against its first parent only.
939 939
940 940 Output may be to a file, in which case the name of the file is
941 941 given using a format string. The formatting rules are as follows:
942 942
943 943 %% literal "%" character
944 944 %H changeset hash (40 bytes of hexadecimal)
945 945 %N number of patches being generated
946 946 %R changeset revision number
947 947 %b basename of the exporting repository
948 948 %h short-form changeset hash (12 bytes of hexadecimal)
949 949 %n zero-padded sequence number, starting at 1
950 950 %r zero-padded changeset revision number
951 951
952 952 Without the -a option, export will avoid generating diffs of files
953 953 it detects as binary. With -a, export will generate a diff anyway,
954 954 probably with undesirable results.
955 955
956 956 With the --switch-parent option, the diff will be against the second
957 957 parent. It can be useful to review a merge.
958 958 """
959 959 if not changesets:
960 960 raise util.Abort(_("export requires at least one changeset"))
961 961 revs = cmdutil.revrange(repo, changesets)
962 962 if len(revs) > 1:
963 963 ui.note(_('exporting patches:\n'))
964 964 else:
965 965 ui.note(_('exporting patch:\n'))
966 966 patch.export(repo, revs, template=opts['output'],
967 967 switch_parent=opts['switch_parent'],
968 968 opts=patch.diffopts(ui, opts))
969 969
970 970 def grep(ui, repo, pattern, *pats, **opts):
971 971 """search for a pattern in specified files and revisions
972 972
973 973 Search revisions of files for a regular expression.
974 974
975 975 This command behaves differently than Unix grep. It only accepts
976 976 Python/Perl regexps. It searches repository history, not the
977 977 working directory. It always prints the revision number in which
978 978 a match appears.
979 979
980 980 By default, grep only prints output for the first revision of a
981 981 file in which it finds a match. To get it to print every revision
982 982 that contains a change in match status ("-" for a match that
983 983 becomes a non-match, or "+" for a non-match that becomes a match),
984 984 use the --all flag.
985 985 """
986 986 reflags = 0
987 987 if opts['ignore_case']:
988 988 reflags |= re.I
989 989 try:
990 990 regexp = re.compile(pattern, reflags)
991 991 except Exception, inst:
992 992 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
993 993 return None
994 994 sep, eol = ':', '\n'
995 995 if opts['print0']:
996 996 sep = eol = '\0'
997 997
998 998 fcache = {}
999 999 def getfile(fn):
1000 1000 if fn not in fcache:
1001 1001 fcache[fn] = repo.file(fn)
1002 1002 return fcache[fn]
1003 1003
1004 1004 def matchlines(body):
1005 1005 begin = 0
1006 1006 linenum = 0
1007 1007 while True:
1008 1008 match = regexp.search(body, begin)
1009 1009 if not match:
1010 1010 break
1011 1011 mstart, mend = match.span()
1012 1012 linenum += body.count('\n', begin, mstart) + 1
1013 1013 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1014 1014 lend = body.find('\n', mend)
1015 1015 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1016 1016 begin = lend + 1
1017 1017
1018 1018 class linestate(object):
1019 1019 def __init__(self, line, linenum, colstart, colend):
1020 1020 self.line = line
1021 1021 self.linenum = linenum
1022 1022 self.colstart = colstart
1023 1023 self.colend = colend
1024 1024
1025 1025 def __eq__(self, other):
1026 1026 return self.line == other.line
1027 1027
1028 1028 matches = {}
1029 1029 copies = {}
1030 1030 def grepbody(fn, rev, body):
1031 1031 matches[rev].setdefault(fn, [])
1032 1032 m = matches[rev][fn]
1033 1033 for lnum, cstart, cend, line in matchlines(body):
1034 1034 s = linestate(line, lnum, cstart, cend)
1035 1035 m.append(s)
1036 1036
1037 1037 def difflinestates(a, b):
1038 1038 sm = difflib.SequenceMatcher(None, a, b)
1039 1039 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1040 1040 if tag == 'insert':
1041 1041 for i in xrange(blo, bhi):
1042 1042 yield ('+', b[i])
1043 1043 elif tag == 'delete':
1044 1044 for i in xrange(alo, ahi):
1045 1045 yield ('-', a[i])
1046 1046 elif tag == 'replace':
1047 1047 for i in xrange(alo, ahi):
1048 1048 yield ('-', a[i])
1049 1049 for i in xrange(blo, bhi):
1050 1050 yield ('+', b[i])
1051 1051
1052 1052 prev = {}
1053 1053 def display(fn, rev, states, prevstates):
1054 1054 datefunc = ui.quiet and util.shortdate or util.datestr
1055 1055 found = False
1056 1056 filerevmatches = {}
1057 1057 r = prev.get(fn, -1)
1058 1058 if opts['all']:
1059 1059 iter = difflinestates(states, prevstates)
1060 1060 else:
1061 1061 iter = [('', l) for l in prevstates]
1062 1062 for change, l in iter:
1063 1063 cols = [fn, str(r)]
1064 1064 if opts['line_number']:
1065 1065 cols.append(str(l.linenum))
1066 1066 if opts['all']:
1067 1067 cols.append(change)
1068 1068 if opts['user']:
1069 1069 cols.append(ui.shortuser(get(r)[1]))
1070 1070 if opts.get('date'):
1071 1071 cols.append(datefunc(get(r)[2]))
1072 1072 if opts['files_with_matches']:
1073 1073 c = (fn, r)
1074 1074 if c in filerevmatches:
1075 1075 continue
1076 1076 filerevmatches[c] = 1
1077 1077 else:
1078 1078 cols.append(l.line)
1079 1079 ui.write(sep.join(cols), eol)
1080 1080 found = True
1081 1081 return found
1082 1082
1083 1083 fstate = {}
1084 1084 skip = {}
1085 1085 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1086 1086 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1087 1087 found = False
1088 1088 follow = opts.get('follow')
1089 1089 for st, rev, fns in changeiter:
1090 1090 if st == 'window':
1091 1091 matches.clear()
1092 1092 elif st == 'add':
1093 1093 ctx = repo.changectx(rev)
1094 1094 matches[rev] = {}
1095 1095 for fn in fns:
1096 1096 if fn in skip:
1097 1097 continue
1098 1098 try:
1099 1099 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1100 1100 fstate.setdefault(fn, [])
1101 1101 if follow:
1102 1102 copied = getfile(fn).renamed(ctx.filenode(fn))
1103 1103 if copied:
1104 1104 copies.setdefault(rev, {})[fn] = copied[0]
1105 1105 except revlog.LookupError:
1106 1106 pass
1107 1107 elif st == 'iter':
1108 1108 states = matches[rev].items()
1109 1109 states.sort()
1110 1110 for fn, m in states:
1111 1111 copy = copies.get(rev, {}).get(fn)
1112 1112 if fn in skip:
1113 1113 if copy:
1114 1114 skip[copy] = True
1115 1115 continue
1116 1116 if fn in prev or fstate[fn]:
1117 1117 r = display(fn, rev, m, fstate[fn])
1118 1118 found = found or r
1119 1119 if r and not opts['all']:
1120 1120 skip[fn] = True
1121 1121 if copy:
1122 1122 skip[copy] = True
1123 1123 fstate[fn] = m
1124 1124 if copy:
1125 1125 fstate[copy] = m
1126 1126 prev[fn] = rev
1127 1127
1128 1128 fstate = fstate.items()
1129 1129 fstate.sort()
1130 1130 for fn, state in fstate:
1131 1131 if fn in skip:
1132 1132 continue
1133 1133 if fn not in copies.get(prev[fn], {}):
1134 1134 found = display(fn, rev, {}, state) or found
1135 1135 return (not found and 1) or 0
1136 1136
1137 1137 def heads(ui, repo, *branchrevs, **opts):
1138 1138 """show current repository heads or show branch heads
1139 1139
1140 1140 With no arguments, show all repository head changesets.
1141 1141
1142 1142 If branch or revisions names are given this will show the heads of
1143 1143 the specified branches or the branches those revisions are tagged
1144 1144 with.
1145 1145
1146 1146 Repository "heads" are changesets that don't have child
1147 1147 changesets. They are where development generally takes place and
1148 1148 are the usual targets for update and merge operations.
1149 1149
1150 1150 Branch heads are changesets that have a given branch tag, but have
1151 1151 no child changesets with that tag. They are usually where
1152 1152 development on the given branch takes place.
1153 1153 """
1154 1154 if opts['rev']:
1155 1155 start = repo.lookup(opts['rev'])
1156 1156 else:
1157 1157 start = None
1158 1158 if not branchrevs:
1159 1159 # Assume we're looking repo-wide heads if no revs were specified.
1160 1160 heads = repo.heads(start)
1161 1161 else:
1162 1162 heads = []
1163 1163 visitedset = util.set()
1164 1164 for branchrev in branchrevs:
1165 1165 branch = repo.changectx(branchrev).branch()
1166 1166 if branch in visitedset:
1167 1167 continue
1168 1168 visitedset.add(branch)
1169 1169 bheads = repo.branchheads(branch, start)
1170 1170 if not bheads:
1171 1171 if branch != branchrev:
1172 1172 ui.warn(_("no changes on branch %s containing %s are "
1173 1173 "reachable from %s\n")
1174 1174 % (branch, branchrev, opts['rev']))
1175 1175 else:
1176 1176 ui.warn(_("no changes on branch %s are reachable from %s\n")
1177 1177 % (branch, opts['rev']))
1178 1178 heads.extend(bheads)
1179 1179 if not heads:
1180 1180 return 1
1181 1181 displayer = cmdutil.show_changeset(ui, repo, opts)
1182 1182 for n in heads:
1183 1183 displayer.show(changenode=n)
1184 1184
1185 1185 def help_(ui, name=None, with_version=False):
1186 1186 """show help for a command, extension, or list of commands
1187 1187
1188 1188 With no arguments, print a list of commands and short help.
1189 1189
1190 1190 Given a command name, print help for that command.
1191 1191
1192 1192 Given an extension name, print help for that extension, and the
1193 1193 commands it provides."""
1194 1194 option_lists = []
1195 1195
1196 1196 def addglobalopts(aliases):
1197 1197 if ui.verbose:
1198 1198 option_lists.append((_("global options:"), globalopts))
1199 1199 if name == 'shortlist':
1200 1200 option_lists.append((_('use "hg help" for the full list '
1201 1201 'of commands'), ()))
1202 1202 else:
1203 1203 if name == 'shortlist':
1204 1204 msg = _('use "hg help" for the full list of commands '
1205 1205 'or "hg -v" for details')
1206 1206 elif aliases:
1207 1207 msg = _('use "hg -v help%s" to show aliases and '
1208 1208 'global options') % (name and " " + name or "")
1209 1209 else:
1210 1210 msg = _('use "hg -v help %s" to show global options') % name
1211 1211 option_lists.append((msg, ()))
1212 1212
1213 1213 def helpcmd(name):
1214 1214 if with_version:
1215 1215 version_(ui)
1216 1216 ui.write('\n')
1217 1217 aliases, i = cmdutil.findcmd(ui, name, table)
1218 1218 # synopsis
1219 1219 ui.write("%s\n" % i[2])
1220 1220
1221 1221 # aliases
1222 1222 if not ui.quiet and len(aliases) > 1:
1223 1223 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1224 1224
1225 1225 # description
1226 1226 doc = i[0].__doc__
1227 1227 if not doc:
1228 1228 doc = _("(No help text available)")
1229 1229 if ui.quiet:
1230 1230 doc = doc.splitlines(0)[0]
1231 1231 ui.write("\n%s\n" % doc.rstrip())
1232 1232
1233 1233 if not ui.quiet:
1234 1234 # options
1235 1235 if i[1]:
1236 1236 option_lists.append((_("options:\n"), i[1]))
1237 1237
1238 1238 addglobalopts(False)
1239 1239
1240 1240 def helplist(header, select=None):
1241 1241 h = {}
1242 1242 cmds = {}
1243 1243 for c, e in table.items():
1244 1244 f = c.split("|", 1)[0]
1245 1245 if select and not select(f):
1246 1246 continue
1247 1247 if name == "shortlist" and not f.startswith("^"):
1248 1248 continue
1249 1249 f = f.lstrip("^")
1250 1250 if not ui.debugflag and f.startswith("debug"):
1251 1251 continue
1252 1252 doc = e[0].__doc__
1253 1253 if not doc:
1254 1254 doc = _("(No help text available)")
1255 1255 h[f] = doc.splitlines(0)[0].rstrip()
1256 1256 cmds[f] = c.lstrip("^")
1257 1257
1258 1258 if not h:
1259 1259 ui.status(_('no commands defined\n'))
1260 1260 return
1261 1261
1262 1262 ui.status(header)
1263 1263 fns = h.keys()
1264 1264 fns.sort()
1265 1265 m = max(map(len, fns))
1266 1266 for f in fns:
1267 1267 if ui.verbose:
1268 1268 commands = cmds[f].replace("|",", ")
1269 1269 ui.write(" %s:\n %s\n"%(commands, h[f]))
1270 1270 else:
1271 1271 ui.write(' %-*s %s\n' % (m, f, h[f]))
1272 1272
1273 1273 if not ui.quiet:
1274 1274 addglobalopts(True)
1275 1275
1276 1276 def helptopic(name):
1277 1277 v = None
1278 1278 for i in help.helptable:
1279 1279 l = i.split('|')
1280 1280 if name in l:
1281 1281 v = i
1282 1282 header = l[-1]
1283 1283 if not v:
1284 1284 raise cmdutil.UnknownCommand(name)
1285 1285
1286 1286 # description
1287 1287 doc = help.helptable[v]
1288 1288 if not doc:
1289 1289 doc = _("(No help text available)")
1290 1290 if callable(doc):
1291 1291 doc = doc()
1292 1292
1293 1293 ui.write("%s\n" % header)
1294 1294 ui.write("%s\n" % doc.rstrip())
1295 1295
1296 1296 def helpext(name):
1297 1297 try:
1298 1298 mod = extensions.find(name)
1299 1299 except KeyError:
1300 1300 raise cmdutil.UnknownCommand(name)
1301 1301
1302 1302 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1303 1303 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1304 1304 for d in doc[1:]:
1305 1305 ui.write(d, '\n')
1306 1306
1307 1307 ui.status('\n')
1308 1308
1309 1309 try:
1310 1310 ct = mod.cmdtable
1311 1311 except AttributeError:
1312 1312 ct = {}
1313 1313
1314 1314 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1315 1315 helplist(_('list of commands:\n\n'), modcmds.has_key)
1316 1316
1317 1317 if name and name != 'shortlist':
1318 1318 i = None
1319 1319 for f in (helpcmd, helptopic, helpext):
1320 1320 try:
1321 1321 f(name)
1322 1322 i = None
1323 1323 break
1324 1324 except cmdutil.UnknownCommand, inst:
1325 1325 i = inst
1326 1326 if i:
1327 1327 raise i
1328 1328
1329 1329 else:
1330 1330 # program name
1331 1331 if ui.verbose or with_version:
1332 1332 version_(ui)
1333 1333 else:
1334 1334 ui.status(_("Mercurial Distributed SCM\n"))
1335 1335 ui.status('\n')
1336 1336
1337 1337 # list of commands
1338 1338 if name == "shortlist":
1339 1339 header = _('basic commands:\n\n')
1340 1340 else:
1341 1341 header = _('list of commands:\n\n')
1342 1342
1343 1343 helplist(header)
1344 1344
1345 1345 # list all option lists
1346 1346 opt_output = []
1347 1347 for title, options in option_lists:
1348 1348 opt_output.append(("\n%s" % title, None))
1349 1349 for shortopt, longopt, default, desc in options:
1350 1350 if "DEPRECATED" in desc and not ui.verbose: continue
1351 1351 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1352 1352 longopt and " --%s" % longopt),
1353 1353 "%s%s" % (desc,
1354 1354 default
1355 1355 and _(" (default: %s)") % default
1356 1356 or "")))
1357 1357
1358 1358 if opt_output:
1359 1359 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1360 1360 for first, second in opt_output:
1361 1361 if second:
1362 1362 ui.write(" %-*s %s\n" % (opts_len, first, second))
1363 1363 else:
1364 1364 ui.write("%s\n" % first)
1365 1365
1366 1366 def identify(ui, repo, source=None,
1367 1367 rev=None, num=None, id=None, branch=None, tags=None):
1368 1368 """identify the working copy or specified revision
1369 1369
1370 1370 With no revision, print a summary of the current state of the repo.
1371 1371
1372 1372 With a path, do a lookup in another repository.
1373 1373
1374 1374 This summary identifies the repository state using one or two parent
1375 1375 hash identifiers, followed by a "+" if there are uncommitted changes
1376 1376 in the working directory, a list of tags for this revision and a branch
1377 1377 name for non-default branches.
1378 1378 """
1379 1379
1380 1380 if not repo and not source:
1381 1381 raise util.Abort(_("There is no Mercurial repository here "
1382 1382 "(.hg not found)"))
1383 1383
1384 1384 hexfunc = ui.debugflag and hex or short
1385 1385 default = not (num or id or branch or tags)
1386 1386 output = []
1387 1387
1388 1388 if source:
1389 1389 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1390 1390 srepo = hg.repository(ui, source)
1391 1391 if not rev and revs:
1392 1392 rev = revs[0]
1393 1393 if not rev:
1394 1394 rev = "tip"
1395 1395 if num or branch or tags:
1396 1396 raise util.Abort(
1397 1397 "can't query remote revision number, branch, or tags")
1398 1398 output = [hexfunc(srepo.lookup(rev))]
1399 1399 elif not rev:
1400 1400 ctx = repo.workingctx()
1401 1401 parents = ctx.parents()
1402 1402 changed = False
1403 1403 if default or id or num:
1404 1404 changed = ctx.files() + ctx.deleted()
1405 1405 if default or id:
1406 1406 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1407 1407 (changed) and "+" or "")]
1408 1408 if num:
1409 1409 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1410 1410 (changed) and "+" or ""))
1411 1411 else:
1412 1412 ctx = repo.changectx(rev)
1413 1413 if default or id:
1414 1414 output = [hexfunc(ctx.node())]
1415 1415 if num:
1416 1416 output.append(str(ctx.rev()))
1417 1417
1418 1418 if not source and default and not ui.quiet:
1419 1419 b = util.tolocal(ctx.branch())
1420 1420 if b != 'default':
1421 1421 output.append("(%s)" % b)
1422 1422
1423 1423 # multiple tags for a single parent separated by '/'
1424 1424 t = "/".join(ctx.tags())
1425 1425 if t:
1426 1426 output.append(t)
1427 1427
1428 1428 if branch:
1429 1429 output.append(util.tolocal(ctx.branch()))
1430 1430
1431 1431 if tags:
1432 1432 output.extend(ctx.tags())
1433 1433
1434 1434 ui.write("%s\n" % ' '.join(output))
1435 1435
1436 1436 def import_(ui, repo, patch1, *patches, **opts):
1437 1437 """import an ordered set of patches
1438 1438
1439 1439 Import a list of patches and commit them individually.
1440 1440
1441 1441 If there are outstanding changes in the working directory, import
1442 1442 will abort unless given the -f flag.
1443 1443
1444 1444 You can import a patch straight from a mail message. Even patches
1445 1445 as attachments work (body part must be type text/plain or
1446 1446 text/x-patch to be used). From and Subject headers of email
1447 1447 message are used as default committer and commit message. All
1448 1448 text/plain body parts before first diff are added to commit
1449 1449 message.
1450 1450
1451 1451 If the imported patch was generated by hg export, user and description
1452 1452 from patch override values from message headers and body. Values
1453 1453 given on command line with -m and -u override these.
1454 1454
1455 1455 If --exact is specified, import will set the working directory
1456 1456 to the parent of each patch before applying it, and will abort
1457 1457 if the resulting changeset has a different ID than the one
1458 1458 recorded in the patch. This may happen due to character set
1459 1459 problems or other deficiencies in the text patch format.
1460 1460
1461 1461 To read a patch from standard input, use patch name "-".
1462 1462 See 'hg help dates' for a list of formats valid for -d/--date.
1463 1463 """
1464 1464 patches = (patch1,) + patches
1465 1465
1466 1466 date = opts.get('date')
1467 1467 if date:
1468 1468 opts['date'] = util.parsedate(date)
1469 1469
1470 1470 if opts.get('exact') or not opts['force']:
1471 1471 cmdutil.bail_if_changed(repo)
1472 1472
1473 1473 d = opts["base"]
1474 1474 strip = opts["strip"]
1475 1475 wlock = lock = None
1476 1476 try:
1477 1477 wlock = repo.wlock()
1478 1478 lock = repo.lock()
1479 1479 for p in patches:
1480 1480 pf = os.path.join(d, p)
1481 1481
1482 1482 if pf == '-':
1483 1483 ui.status(_("applying patch from stdin\n"))
1484 1484 data = patch.extract(ui, sys.stdin)
1485 1485 else:
1486 1486 ui.status(_("applying %s\n") % p)
1487 1487 if os.path.exists(pf):
1488 1488 data = patch.extract(ui, file(pf, 'rb'))
1489 1489 else:
1490 1490 data = patch.extract(ui, urllib.urlopen(pf))
1491 1491 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1492 1492
1493 1493 if tmpname is None:
1494 1494 raise util.Abort(_('no diffs found'))
1495 1495
1496 1496 try:
1497 1497 cmdline_message = cmdutil.logmessage(opts)
1498 1498 if cmdline_message:
1499 1499 # pickup the cmdline msg
1500 1500 message = cmdline_message
1501 1501 elif message:
1502 1502 # pickup the patch msg
1503 1503 message = message.strip()
1504 1504 else:
1505 1505 # launch the editor
1506 1506 message = None
1507 1507 ui.debug(_('message:\n%s\n') % message)
1508 1508
1509 1509 wp = repo.workingctx().parents()
1510 1510 if opts.get('exact'):
1511 1511 if not nodeid or not p1:
1512 1512 raise util.Abort(_('not a mercurial patch'))
1513 1513 p1 = repo.lookup(p1)
1514 1514 p2 = repo.lookup(p2 or hex(nullid))
1515 1515
1516 1516 if p1 != wp[0].node():
1517 1517 hg.clean(repo, p1)
1518 1518 repo.dirstate.setparents(p1, p2)
1519 1519 elif p2:
1520 1520 try:
1521 1521 p1 = repo.lookup(p1)
1522 1522 p2 = repo.lookup(p2)
1523 1523 if p1 == wp[0].node():
1524 1524 repo.dirstate.setparents(p1, p2)
1525 1525 except hg.RepoError:
1526 1526 pass
1527 1527 if opts.get('exact') or opts.get('import_branch'):
1528 1528 repo.dirstate.setbranch(branch or 'default')
1529 1529
1530 1530 files = {}
1531 1531 try:
1532 1532 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1533 1533 files=files)
1534 1534 finally:
1535 1535 files = patch.updatedir(ui, repo, files)
1536 1536 if not opts.get('no_commit'):
1537 1537 n = repo.commit(files, message, opts.get('user') or user,
1538 1538 opts.get('date') or date)
1539 1539 if opts.get('exact'):
1540 1540 if hex(n) != nodeid:
1541 1541 repo.rollback()
1542 1542 raise util.Abort(_('patch is damaged'
1543 1543 ' or loses information'))
1544 1544 # Force a dirstate write so that the next transaction
1545 1545 # backups an up-do-date file.
1546 1546 repo.dirstate.write()
1547 1547 finally:
1548 1548 os.unlink(tmpname)
1549 1549 finally:
1550 1550 del lock, wlock
1551 1551
1552 1552 def incoming(ui, repo, source="default", **opts):
1553 1553 """show new changesets found in source
1554 1554
1555 1555 Show new changesets found in the specified path/URL or the default
1556 1556 pull location. These are the changesets that would be pulled if a pull
1557 1557 was requested.
1558 1558
1559 1559 For remote repository, using --bundle avoids downloading the changesets
1560 1560 twice if the incoming is followed by a pull.
1561 1561
1562 1562 See pull for valid source format details.
1563 1563 """
1564 1564 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
1565 1565 cmdutil.setremoteconfig(ui, opts)
1566 1566
1567 1567 other = hg.repository(ui, source)
1568 1568 ui.status(_('comparing with %s\n') % util.hidepassword(source))
1569 1569 if revs:
1570 1570 revs = [other.lookup(rev) for rev in revs]
1571 1571 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1572 1572 if not incoming:
1573 1573 try:
1574 1574 os.unlink(opts["bundle"])
1575 1575 except:
1576 1576 pass
1577 1577 ui.status(_("no changes found\n"))
1578 1578 return 1
1579 1579
1580 1580 cleanup = None
1581 1581 try:
1582 1582 fname = opts["bundle"]
1583 1583 if fname or not other.local():
1584 1584 # create a bundle (uncompressed if other repo is not local)
1585 1585 if revs is None:
1586 1586 cg = other.changegroup(incoming, "incoming")
1587 1587 else:
1588 1588 cg = other.changegroupsubset(incoming, revs, 'incoming')
1589 1589 bundletype = other.local() and "HG10BZ" or "HG10UN"
1590 1590 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1591 1591 # keep written bundle?
1592 1592 if opts["bundle"]:
1593 1593 cleanup = None
1594 1594 if not other.local():
1595 1595 # use the created uncompressed bundlerepo
1596 1596 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1597 1597
1598 1598 o = other.changelog.nodesbetween(incoming, revs)[0]
1599 1599 if opts['newest_first']:
1600 1600 o.reverse()
1601 1601 displayer = cmdutil.show_changeset(ui, other, opts)
1602 1602 for n in o:
1603 1603 parents = [p for p in other.changelog.parents(n) if p != nullid]
1604 1604 if opts['no_merges'] and len(parents) == 2:
1605 1605 continue
1606 1606 displayer.show(changenode=n)
1607 1607 finally:
1608 1608 if hasattr(other, 'close'):
1609 1609 other.close()
1610 1610 if cleanup:
1611 1611 os.unlink(cleanup)
1612 1612
1613 1613 def init(ui, dest=".", **opts):
1614 1614 """create a new repository in the given directory
1615 1615
1616 1616 Initialize a new repository in the given directory. If the given
1617 1617 directory does not exist, it is created.
1618 1618
1619 1619 If no directory is given, the current directory is used.
1620 1620
1621 1621 It is possible to specify an ssh:// URL as the destination.
1622 1622 Look at the help text for the pull command for important details
1623 1623 about ssh:// URLs.
1624 1624 """
1625 1625 cmdutil.setremoteconfig(ui, opts)
1626 1626 hg.repository(ui, dest, create=1)
1627 1627
1628 1628 def locate(ui, repo, *pats, **opts):
1629 1629 """locate files matching specific patterns
1630 1630
1631 1631 Print all files under Mercurial control whose names match the
1632 1632 given patterns.
1633 1633
1634 1634 This command searches the entire repository by default. To search
1635 1635 just the current directory and its subdirectories, use
1636 1636 "--include .".
1637 1637
1638 1638 If no patterns are given to match, this command prints all file
1639 1639 names.
1640 1640
1641 1641 If you want to feed the output of this command into the "xargs"
1642 1642 command, use the "-0" option to both this command and "xargs".
1643 1643 This will avoid the problem of "xargs" treating single filenames
1644 1644 that contain white space as multiple filenames.
1645 1645 """
1646 1646 end = opts['print0'] and '\0' or '\n'
1647 1647 rev = opts['rev']
1648 1648 if rev:
1649 1649 node = repo.lookup(rev)
1650 1650 else:
1651 1651 node = None
1652 1652
1653 1653 ret = 1
1654 1654 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1655 1655 badmatch=util.always,
1656 1656 default='relglob'):
1657 1657 if src == 'b':
1658 1658 continue
1659 1659 if not node and abs not in repo.dirstate:
1660 1660 continue
1661 1661 if opts['fullpath']:
1662 1662 ui.write(os.path.join(repo.root, abs), end)
1663 1663 else:
1664 1664 ui.write(((pats and rel) or abs), end)
1665 1665 ret = 0
1666 1666
1667 1667 return ret
1668 1668
1669 1669 def log(ui, repo, *pats, **opts):
1670 1670 """show revision history of entire repository or files
1671 1671
1672 1672 Print the revision history of the specified files or the entire
1673 1673 project.
1674 1674
1675 1675 File history is shown without following rename or copy history of
1676 1676 files. Use -f/--follow with a file name to follow history across
1677 1677 renames and copies. --follow without a file name will only show
1678 1678 ancestors or descendants of the starting revision. --follow-first
1679 1679 only follows the first parent of merge revisions.
1680 1680
1681 1681 If no revision range is specified, the default is tip:0 unless
1682 1682 --follow is set, in which case the working directory parent is
1683 1683 used as the starting revision.
1684 1684
1685 1685 See 'hg help dates' for a list of formats valid for -d/--date.
1686 1686
1687 1687 By default this command outputs: changeset id and hash, tags,
1688 1688 non-trivial parents, user, date and time, and a summary for each
1689 1689 commit. When the -v/--verbose switch is used, the list of changed
1690 1690 files and full commit message is shown.
1691 1691
1692 1692 NOTE: log -p may generate unexpected diff output for merge
1693 1693 changesets, as it will compare the merge changeset against its
1694 1694 first parent only. Also, the files: list will only reflect files
1695 1695 that are different from BOTH parents.
1696 1696
1697 1697 """
1698 1698
1699 1699 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1700 1700 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1701 1701
1702 if opts['limit']:
1703 try:
1704 limit = int(opts['limit'])
1705 except ValueError:
1706 raise util.Abort(_('limit must be a positive integer'))
1707 if limit <= 0: raise util.Abort(_('limit must be positive'))
1708 else:
1709 limit = sys.maxint
1702 limit = cmdutil.loglimit(opts)
1710 1703 count = 0
1711 1704
1712 1705 if opts['copies'] and opts['rev']:
1713 1706 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1714 1707 else:
1715 1708 endrev = repo.changelog.count()
1716 1709 rcache = {}
1717 1710 ncache = {}
1718 1711 def getrenamed(fn, rev):
1719 1712 '''looks up all renames for a file (up to endrev) the first
1720 1713 time the file is given. It indexes on the changerev and only
1721 1714 parses the manifest if linkrev != changerev.
1722 1715 Returns rename info for fn at changerev rev.'''
1723 1716 if fn not in rcache:
1724 1717 rcache[fn] = {}
1725 1718 ncache[fn] = {}
1726 1719 fl = repo.file(fn)
1727 1720 for i in xrange(fl.count()):
1728 1721 node = fl.node(i)
1729 1722 lr = fl.linkrev(node)
1730 1723 renamed = fl.renamed(node)
1731 1724 rcache[fn][lr] = renamed
1732 1725 if renamed:
1733 1726 ncache[fn][node] = renamed
1734 1727 if lr >= endrev:
1735 1728 break
1736 1729 if rev in rcache[fn]:
1737 1730 return rcache[fn][rev]
1738 1731
1739 1732 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1740 1733 # filectx logic.
1741 1734
1742 1735 try:
1743 1736 return repo.changectx(rev).filectx(fn).renamed()
1744 1737 except revlog.LookupError:
1745 1738 pass
1746 1739 return None
1747 1740
1748 1741 df = False
1749 1742 if opts["date"]:
1750 1743 df = util.matchdate(opts["date"])
1751 1744
1752 1745 only_branches = opts['only_branch']
1753 1746
1754 1747 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1755 1748 for st, rev, fns in changeiter:
1756 1749 if st == 'add':
1757 1750 changenode = repo.changelog.node(rev)
1758 1751 parents = [p for p in repo.changelog.parentrevs(rev)
1759 1752 if p != nullrev]
1760 1753 if opts['no_merges'] and len(parents) == 2:
1761 1754 continue
1762 1755 if opts['only_merges'] and len(parents) != 2:
1763 1756 continue
1764 1757
1765 1758 if only_branches:
1766 1759 revbranch = get(rev)[5]['branch']
1767 1760 if revbranch not in only_branches:
1768 1761 continue
1769 1762
1770 1763 if df:
1771 1764 changes = get(rev)
1772 1765 if not df(changes[2][0]):
1773 1766 continue
1774 1767
1775 1768 if opts['keyword']:
1776 1769 changes = get(rev)
1777 1770 miss = 0
1778 1771 for k in [kw.lower() for kw in opts['keyword']]:
1779 1772 if not (k in changes[1].lower() or
1780 1773 k in changes[4].lower() or
1781 1774 k in " ".join(changes[3]).lower()):
1782 1775 miss = 1
1783 1776 break
1784 1777 if miss:
1785 1778 continue
1786 1779
1787 1780 copies = []
1788 1781 if opts.get('copies') and rev:
1789 1782 for fn in get(rev)[3]:
1790 1783 rename = getrenamed(fn, rev)
1791 1784 if rename:
1792 1785 copies.append((fn, rename[0]))
1793 1786 displayer.show(rev, changenode, copies=copies)
1794 1787 elif st == 'iter':
1795 1788 if count == limit: break
1796 1789 if displayer.flush(rev):
1797 1790 count += 1
1798 1791
1799 1792 def manifest(ui, repo, node=None, rev=None):
1800 1793 """output the current or given revision of the project manifest
1801 1794
1802 1795 Print a list of version controlled files for the given revision.
1803 1796 If no revision is given, the parent of the working directory is used,
1804 1797 or tip if no revision is checked out.
1805 1798
1806 1799 The manifest is the list of files being version controlled. If no revision
1807 1800 is given then the first parent of the working directory is used.
1808 1801
1809 1802 With -v flag, print file permissions, symlink and executable bits. With
1810 1803 --debug flag, print file revision hashes.
1811 1804 """
1812 1805
1813 1806 if rev and node:
1814 1807 raise util.Abort(_("please specify just one revision"))
1815 1808
1816 1809 if not node:
1817 1810 node = rev
1818 1811
1819 1812 m = repo.changectx(node).manifest()
1820 1813 files = m.keys()
1821 1814 files.sort()
1822 1815
1823 1816 for f in files:
1824 1817 if ui.debugflag:
1825 1818 ui.write("%40s " % hex(m[f]))
1826 1819 if ui.verbose:
1827 1820 type = m.execf(f) and "*" or m.linkf(f) and "@" or " "
1828 1821 perm = m.execf(f) and "755" or "644"
1829 1822 ui.write("%3s %1s " % (perm, type))
1830 1823 ui.write("%s\n" % f)
1831 1824
1832 1825 def merge(ui, repo, node=None, force=None, rev=None):
1833 1826 """merge working directory with another revision
1834 1827
1835 1828 Merge the contents of the current working directory and the
1836 1829 requested revision. Files that changed between either parent are
1837 1830 marked as changed for the next commit and a commit must be
1838 1831 performed before any further updates are allowed.
1839 1832
1840 1833 If no revision is specified, the working directory's parent is a
1841 1834 head revision, and the repository contains exactly one other head,
1842 1835 the other head is merged with by default. Otherwise, an explicit
1843 1836 revision to merge with must be provided.
1844 1837 """
1845 1838
1846 1839 if rev and node:
1847 1840 raise util.Abort(_("please specify just one revision"))
1848 1841 if not node:
1849 1842 node = rev
1850 1843
1851 1844 if not node:
1852 1845 heads = repo.heads()
1853 1846 if len(heads) > 2:
1854 1847 raise util.Abort(_('repo has %d heads - '
1855 1848 'please merge with an explicit rev') %
1856 1849 len(heads))
1857 1850 parent = repo.dirstate.parents()[0]
1858 1851 if len(heads) == 1:
1859 1852 msg = _('there is nothing to merge')
1860 1853 if parent != repo.lookup(repo.workingctx().branch()):
1861 1854 msg = _('%s - use "hg update" instead') % msg
1862 1855 raise util.Abort(msg)
1863 1856
1864 1857 if parent not in heads:
1865 1858 raise util.Abort(_('working dir not at a head rev - '
1866 1859 'use "hg update" or merge with an explicit rev'))
1867 1860 node = parent == heads[0] and heads[-1] or heads[0]
1868 1861 return hg.merge(repo, node, force=force)
1869 1862
1870 1863 def outgoing(ui, repo, dest=None, **opts):
1871 1864 """show changesets not found in destination
1872 1865
1873 1866 Show changesets not found in the specified destination repository or
1874 1867 the default push location. These are the changesets that would be pushed
1875 1868 if a push was requested.
1876 1869
1877 1870 See pull for valid destination format details.
1878 1871 """
1879 1872 dest, revs, checkout = hg.parseurl(
1880 1873 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1881 1874 cmdutil.setremoteconfig(ui, opts)
1882 1875 if revs:
1883 1876 revs = [repo.lookup(rev) for rev in revs]
1884 1877
1885 1878 other = hg.repository(ui, dest)
1886 1879 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
1887 1880 o = repo.findoutgoing(other, force=opts['force'])
1888 1881 if not o:
1889 1882 ui.status(_("no changes found\n"))
1890 1883 return 1
1891 1884 o = repo.changelog.nodesbetween(o, revs)[0]
1892 1885 if opts['newest_first']:
1893 1886 o.reverse()
1894 1887 displayer = cmdutil.show_changeset(ui, repo, opts)
1895 1888 for n in o:
1896 1889 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1897 1890 if opts['no_merges'] and len(parents) == 2:
1898 1891 continue
1899 1892 displayer.show(changenode=n)
1900 1893
1901 1894 def parents(ui, repo, file_=None, **opts):
1902 1895 """show the parents of the working dir or revision
1903 1896
1904 1897 Print the working directory's parent revisions. If a
1905 1898 revision is given via --rev, the parent of that revision
1906 1899 will be printed. If a file argument is given, revision in
1907 1900 which the file was last changed (before the working directory
1908 1901 revision or the argument to --rev if given) is printed.
1909 1902 """
1910 1903 rev = opts.get('rev')
1911 1904 if rev:
1912 1905 ctx = repo.changectx(rev)
1913 1906 else:
1914 1907 ctx = repo.workingctx()
1915 1908
1916 1909 if file_:
1917 1910 files, match, anypats = cmdutil.matchpats(repo, (file_,), opts)
1918 1911 if anypats or len(files) != 1:
1919 1912 raise util.Abort(_('can only specify an explicit file name'))
1920 1913 file_ = files[0]
1921 1914 filenodes = []
1922 1915 for cp in ctx.parents():
1923 1916 if not cp:
1924 1917 continue
1925 1918 try:
1926 1919 filenodes.append(cp.filenode(file_))
1927 1920 except revlog.LookupError:
1928 1921 pass
1929 1922 if not filenodes:
1930 1923 raise util.Abort(_("'%s' not found in manifest!") % file_)
1931 1924 fl = repo.file(file_)
1932 1925 p = [repo.lookup(fl.linkrev(fn)) for fn in filenodes]
1933 1926 else:
1934 1927 p = [cp.node() for cp in ctx.parents()]
1935 1928
1936 1929 displayer = cmdutil.show_changeset(ui, repo, opts)
1937 1930 for n in p:
1938 1931 if n != nullid:
1939 1932 displayer.show(changenode=n)
1940 1933
1941 1934 def paths(ui, repo, search=None):
1942 1935 """show definition of symbolic path names
1943 1936
1944 1937 Show definition of symbolic path name NAME. If no name is given, show
1945 1938 definition of available names.
1946 1939
1947 1940 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1948 1941 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1949 1942 """
1950 1943 if search:
1951 1944 for name, path in ui.configitems("paths"):
1952 1945 if name == search:
1953 1946 ui.write("%s\n" % path)
1954 1947 return
1955 1948 ui.warn(_("not found!\n"))
1956 1949 return 1
1957 1950 else:
1958 1951 for name, path in ui.configitems("paths"):
1959 1952 ui.write("%s = %s\n" % (name, path))
1960 1953
1961 1954 def postincoming(ui, repo, modheads, optupdate, checkout):
1962 1955 if modheads == 0:
1963 1956 return
1964 1957 if optupdate:
1965 1958 if modheads <= 1 or checkout:
1966 1959 return hg.update(repo, checkout)
1967 1960 else:
1968 1961 ui.status(_("not updating, since new heads added\n"))
1969 1962 if modheads > 1:
1970 1963 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1971 1964 else:
1972 1965 ui.status(_("(run 'hg update' to get a working copy)\n"))
1973 1966
1974 1967 def pull(ui, repo, source="default", **opts):
1975 1968 """pull changes from the specified source
1976 1969
1977 1970 Pull changes from a remote repository to a local one.
1978 1971
1979 1972 This finds all changes from the repository at the specified path
1980 1973 or URL and adds them to the local repository. By default, this
1981 1974 does not update the copy of the project in the working directory.
1982 1975
1983 1976 Valid URLs are of the form:
1984 1977
1985 1978 local/filesystem/path (or file://local/filesystem/path)
1986 1979 http://[user@]host[:port]/[path]
1987 1980 https://[user@]host[:port]/[path]
1988 1981 ssh://[user@]host[:port]/[path]
1989 1982 static-http://host[:port]/[path]
1990 1983
1991 1984 Paths in the local filesystem can either point to Mercurial
1992 1985 repositories or to bundle files (as created by 'hg bundle' or
1993 1986 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1994 1987 allows access to a Mercurial repository where you simply use a web
1995 1988 server to publish the .hg directory as static content.
1996 1989
1997 1990 An optional identifier after # indicates a particular branch, tag,
1998 1991 or changeset to pull.
1999 1992
2000 1993 Some notes about using SSH with Mercurial:
2001 1994 - SSH requires an accessible shell account on the destination machine
2002 1995 and a copy of hg in the remote path or specified with as remotecmd.
2003 1996 - path is relative to the remote user's home directory by default.
2004 1997 Use an extra slash at the start of a path to specify an absolute path:
2005 1998 ssh://example.com//tmp/repository
2006 1999 - Mercurial doesn't use its own compression via SSH; the right thing
2007 2000 to do is to configure it in your ~/.ssh/config, e.g.:
2008 2001 Host *.mylocalnetwork.example.com
2009 2002 Compression no
2010 2003 Host *
2011 2004 Compression yes
2012 2005 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2013 2006 with the --ssh command line option.
2014 2007 """
2015 2008 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
2016 2009 cmdutil.setremoteconfig(ui, opts)
2017 2010
2018 2011 other = hg.repository(ui, source)
2019 2012 ui.status(_('pulling from %s\n') % util.hidepassword(source))
2020 2013 if revs:
2021 2014 try:
2022 2015 revs = [other.lookup(rev) for rev in revs]
2023 2016 except repo.NoCapability:
2024 2017 error = _("Other repository doesn't support revision lookup, "
2025 2018 "so a rev cannot be specified.")
2026 2019 raise util.Abort(error)
2027 2020
2028 2021 modheads = repo.pull(other, heads=revs, force=opts['force'])
2029 2022 return postincoming(ui, repo, modheads, opts['update'], checkout)
2030 2023
2031 2024 def push(ui, repo, dest=None, **opts):
2032 2025 """push changes to the specified destination
2033 2026
2034 2027 Push changes from the local repository to the given destination.
2035 2028
2036 2029 This is the symmetrical operation for pull. It helps to move
2037 2030 changes from the current repository to a different one. If the
2038 2031 destination is local this is identical to a pull in that directory
2039 2032 from the current one.
2040 2033
2041 2034 By default, push will refuse to run if it detects the result would
2042 2035 increase the number of remote heads. This generally indicates the
2043 2036 the client has forgotten to sync and merge before pushing.
2044 2037
2045 2038 Valid URLs are of the form:
2046 2039
2047 2040 local/filesystem/path (or file://local/filesystem/path)
2048 2041 ssh://[user@]host[:port]/[path]
2049 2042 http://[user@]host[:port]/[path]
2050 2043 https://[user@]host[:port]/[path]
2051 2044
2052 2045 An optional identifier after # indicates a particular branch, tag,
2053 2046 or changeset to push.
2054 2047
2055 2048 Look at the help text for the pull command for important details
2056 2049 about ssh:// URLs.
2057 2050
2058 2051 Pushing to http:// and https:// URLs is only possible, if this
2059 2052 feature is explicitly enabled on the remote Mercurial server.
2060 2053 """
2061 2054 dest, revs, checkout = hg.parseurl(
2062 2055 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2063 2056 cmdutil.setremoteconfig(ui, opts)
2064 2057
2065 2058 other = hg.repository(ui, dest)
2066 2059 ui.status('pushing to %s\n' % util.hidepassword(dest))
2067 2060 if revs:
2068 2061 revs = [repo.lookup(rev) for rev in revs]
2069 2062 r = repo.push(other, opts['force'], revs=revs)
2070 2063 return r == 0
2071 2064
2072 2065 def rawcommit(ui, repo, *pats, **opts):
2073 2066 """raw commit interface (DEPRECATED)
2074 2067
2075 2068 (DEPRECATED)
2076 2069 Lowlevel commit, for use in helper scripts.
2077 2070
2078 2071 This command is not intended to be used by normal users, as it is
2079 2072 primarily useful for importing from other SCMs.
2080 2073
2081 2074 This command is now deprecated and will be removed in a future
2082 2075 release, please use debugsetparents and commit instead.
2083 2076 """
2084 2077
2085 2078 ui.warn(_("(the rawcommit command is deprecated)\n"))
2086 2079
2087 2080 message = cmdutil.logmessage(opts)
2088 2081
2089 2082 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2090 2083 if opts['files']:
2091 2084 files += open(opts['files']).read().splitlines()
2092 2085
2093 2086 parents = [repo.lookup(p) for p in opts['parent']]
2094 2087
2095 2088 try:
2096 2089 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2097 2090 except ValueError, inst:
2098 2091 raise util.Abort(str(inst))
2099 2092
2100 2093 def recover(ui, repo):
2101 2094 """roll back an interrupted transaction
2102 2095
2103 2096 Recover from an interrupted commit or pull.
2104 2097
2105 2098 This command tries to fix the repository status after an interrupted
2106 2099 operation. It should only be necessary when Mercurial suggests it.
2107 2100 """
2108 2101 if repo.recover():
2109 2102 return hg.verify(repo)
2110 2103 return 1
2111 2104
2112 2105 def remove(ui, repo, *pats, **opts):
2113 2106 """remove the specified files on the next commit
2114 2107
2115 2108 Schedule the indicated files for removal from the repository.
2116 2109
2117 2110 This only removes files from the current branch, not from the
2118 2111 entire project history. If the files still exist in the working
2119 2112 directory, they will be deleted from it. If invoked with --after,
2120 2113 files are marked as removed, but not actually unlinked unless --force
2121 2114 is also given. Without exact file names, --after will only mark
2122 2115 files as removed if they are no longer in the working directory.
2123 2116
2124 2117 This command schedules the files to be removed at the next commit.
2125 2118 To undo a remove before that, see hg revert.
2126 2119
2127 2120 Modified files and added files are not removed by default. To
2128 2121 remove them, use the -f/--force option.
2129 2122 """
2130 2123 if not opts['after'] and not pats:
2131 2124 raise util.Abort(_('no files specified'))
2132 2125 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2133 2126 exact = dict.fromkeys(files)
2134 2127 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2135 2128 modified, added, removed, deleted, unknown = mardu
2136 2129 remove, forget = [], []
2137 2130 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2138 2131 reason = None
2139 2132 if abs in modified and not opts['force']:
2140 2133 reason = _('is modified (use -f to force removal)')
2141 2134 elif abs in added:
2142 2135 if opts['force']:
2143 2136 forget.append(abs)
2144 2137 continue
2145 2138 reason = _('has been marked for add (use -f to force removal)')
2146 2139 exact = 1 # force the message
2147 2140 elif abs not in repo.dirstate:
2148 2141 reason = _('is not managed')
2149 2142 elif opts['after'] and not exact and abs not in deleted:
2150 2143 continue
2151 2144 elif abs in removed:
2152 2145 continue
2153 2146 if reason:
2154 2147 if exact:
2155 2148 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2156 2149 else:
2157 2150 if ui.verbose or not exact:
2158 2151 ui.status(_('removing %s\n') % rel)
2159 2152 remove.append(abs)
2160 2153 repo.forget(forget)
2161 2154 repo.remove(remove, unlink=opts['force'] or not opts['after'])
2162 2155
2163 2156 def rename(ui, repo, *pats, **opts):
2164 2157 """rename files; equivalent of copy + remove
2165 2158
2166 2159 Mark dest as copies of sources; mark sources for deletion. If
2167 2160 dest is a directory, copies are put in that directory. If dest is
2168 2161 a file, there can only be one source.
2169 2162
2170 2163 By default, this command copies the contents of files as they
2171 2164 stand in the working directory. If invoked with --after, the
2172 2165 operation is recorded, but no copying is performed.
2173 2166
2174 2167 This command takes effect in the next commit. To undo a rename
2175 2168 before that, see hg revert.
2176 2169 """
2177 2170 wlock = repo.wlock(False)
2178 2171 try:
2179 2172 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2180 2173 finally:
2181 2174 del wlock
2182 2175
2183 2176 def revert(ui, repo, *pats, **opts):
2184 2177 """restore individual files or dirs to an earlier state
2185 2178
2186 2179 (use update -r to check out earlier revisions, revert does not
2187 2180 change the working dir parents)
2188 2181
2189 2182 With no revision specified, revert the named files or directories
2190 2183 to the contents they had in the parent of the working directory.
2191 2184 This restores the contents of the affected files to an unmodified
2192 2185 state and unschedules adds, removes, copies, and renames. If the
2193 2186 working directory has two parents, you must explicitly specify the
2194 2187 revision to revert to.
2195 2188
2196 2189 Using the -r option, revert the given files or directories to their
2197 2190 contents as of a specific revision. This can be helpful to "roll
2198 2191 back" some or all of an earlier change.
2199 2192 See 'hg help dates' for a list of formats valid for -d/--date.
2200 2193
2201 2194 Revert modifies the working directory. It does not commit any
2202 2195 changes, or change the parent of the working directory. If you
2203 2196 revert to a revision other than the parent of the working
2204 2197 directory, the reverted files will thus appear modified
2205 2198 afterwards.
2206 2199
2207 2200 If a file has been deleted, it is restored. If the executable
2208 2201 mode of a file was changed, it is reset.
2209 2202
2210 2203 If names are given, all files matching the names are reverted.
2211 2204 If no arguments are given, no files are reverted.
2212 2205
2213 2206 Modified files are saved with a .orig suffix before reverting.
2214 2207 To disable these backups, use --no-backup.
2215 2208 """
2216 2209
2217 2210 if opts["date"]:
2218 2211 if opts["rev"]:
2219 2212 raise util.Abort(_("you can't specify a revision and a date"))
2220 2213 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2221 2214
2222 2215 if not pats and not opts['all']:
2223 2216 raise util.Abort(_('no files or directories specified; '
2224 2217 'use --all to revert the whole repo'))
2225 2218
2226 2219 parent, p2 = repo.dirstate.parents()
2227 2220 if not opts['rev'] and p2 != nullid:
2228 2221 raise util.Abort(_('uncommitted merge - please provide a '
2229 2222 'specific revision'))
2230 2223 ctx = repo.changectx(opts['rev'])
2231 2224 node = ctx.node()
2232 2225 mf = ctx.manifest()
2233 2226 if node == parent:
2234 2227 pmf = mf
2235 2228 else:
2236 2229 pmf = None
2237 2230
2238 2231 # need all matching names in dirstate and manifest of target rev,
2239 2232 # so have to walk both. do not print errors if files exist in one
2240 2233 # but not other.
2241 2234
2242 2235 names = {}
2243 2236
2244 2237 wlock = repo.wlock()
2245 2238 try:
2246 2239 # walk dirstate.
2247 2240 files = []
2248 2241 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2249 2242 badmatch=mf.has_key):
2250 2243 names[abs] = (rel, exact)
2251 2244 if src != 'b':
2252 2245 files.append(abs)
2253 2246
2254 2247 # walk target manifest.
2255 2248
2256 2249 def badmatch(path):
2257 2250 if path in names:
2258 2251 return True
2259 2252 path_ = path + '/'
2260 2253 for f in names:
2261 2254 if f.startswith(path_):
2262 2255 return True
2263 2256 return False
2264 2257
2265 2258 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2266 2259 badmatch=badmatch):
2267 2260 if abs in names or src == 'b':
2268 2261 continue
2269 2262 names[abs] = (rel, exact)
2270 2263
2271 2264 changes = repo.status(files=files, match=names.has_key)[:4]
2272 2265 modified, added, removed, deleted = map(dict.fromkeys, changes)
2273 2266
2274 2267 # if f is a rename, also revert the source
2275 2268 cwd = repo.getcwd()
2276 2269 for f in added:
2277 2270 src = repo.dirstate.copied(f)
2278 2271 if src and src not in names and repo.dirstate[src] == 'r':
2279 2272 removed[src] = None
2280 2273 names[src] = (repo.pathto(src, cwd), True)
2281 2274
2282 2275 def removeforget(abs):
2283 2276 if repo.dirstate[abs] == 'a':
2284 2277 return _('forgetting %s\n')
2285 2278 return _('removing %s\n')
2286 2279
2287 2280 revert = ([], _('reverting %s\n'))
2288 2281 add = ([], _('adding %s\n'))
2289 2282 remove = ([], removeforget)
2290 2283 undelete = ([], _('undeleting %s\n'))
2291 2284
2292 2285 disptable = (
2293 2286 # dispatch table:
2294 2287 # file state
2295 2288 # action if in target manifest
2296 2289 # action if not in target manifest
2297 2290 # make backup if in target manifest
2298 2291 # make backup if not in target manifest
2299 2292 (modified, revert, remove, True, True),
2300 2293 (added, revert, remove, True, False),
2301 2294 (removed, undelete, None, False, False),
2302 2295 (deleted, revert, remove, False, False),
2303 2296 )
2304 2297
2305 2298 entries = names.items()
2306 2299 entries.sort()
2307 2300
2308 2301 for abs, (rel, exact) in entries:
2309 2302 mfentry = mf.get(abs)
2310 2303 target = repo.wjoin(abs)
2311 2304 def handle(xlist, dobackup):
2312 2305 xlist[0].append(abs)
2313 2306 if dobackup and not opts['no_backup'] and util.lexists(target):
2314 2307 bakname = "%s.orig" % rel
2315 2308 ui.note(_('saving current version of %s as %s\n') %
2316 2309 (rel, bakname))
2317 2310 if not opts.get('dry_run'):
2318 2311 util.copyfile(target, bakname)
2319 2312 if ui.verbose or not exact:
2320 2313 msg = xlist[1]
2321 2314 if not isinstance(msg, basestring):
2322 2315 msg = msg(abs)
2323 2316 ui.status(msg % rel)
2324 2317 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2325 2318 if abs not in table: continue
2326 2319 # file has changed in dirstate
2327 2320 if mfentry:
2328 2321 handle(hitlist, backuphit)
2329 2322 elif misslist is not None:
2330 2323 handle(misslist, backupmiss)
2331 2324 break
2332 2325 else:
2333 2326 if abs not in repo.dirstate:
2334 2327 if mfentry:
2335 2328 handle(add, True)
2336 2329 elif exact:
2337 2330 ui.warn(_('file not managed: %s\n') % rel)
2338 2331 continue
2339 2332 # file has not changed in dirstate
2340 2333 if node == parent:
2341 2334 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2342 2335 continue
2343 2336 if pmf is None:
2344 2337 # only need parent manifest in this unlikely case,
2345 2338 # so do not read by default
2346 2339 pmf = repo.changectx(parent).manifest()
2347 2340 if abs in pmf:
2348 2341 if mfentry:
2349 2342 # if version of file is same in parent and target
2350 2343 # manifests, do nothing
2351 2344 if (pmf[abs] != mfentry or
2352 2345 pmf.flags(abs) != mf.flags(abs)):
2353 2346 handle(revert, False)
2354 2347 else:
2355 2348 handle(remove, False)
2356 2349
2357 2350 if not opts.get('dry_run'):
2358 2351 def checkout(f):
2359 2352 fc = ctx[f]
2360 2353 repo.wwrite(f, fc.data(), fc.fileflags())
2361 2354
2362 2355 audit_path = util.path_auditor(repo.root)
2363 2356 for f in remove[0]:
2364 2357 if repo.dirstate[f] == 'a':
2365 2358 repo.dirstate.forget(f)
2366 2359 continue
2367 2360 audit_path(f)
2368 2361 try:
2369 2362 util.unlink(repo.wjoin(f))
2370 2363 except OSError:
2371 2364 pass
2372 2365 repo.dirstate.remove(f)
2373 2366
2374 2367 for f in revert[0]:
2375 2368 checkout(f)
2376 2369
2377 2370 for f in add[0]:
2378 2371 checkout(f)
2379 2372 repo.dirstate.add(f)
2380 2373
2381 2374 normal = repo.dirstate.normallookup
2382 2375 if node == parent and p2 == nullid:
2383 2376 normal = repo.dirstate.normal
2384 2377 for f in undelete[0]:
2385 2378 checkout(f)
2386 2379 normal(f)
2387 2380
2388 2381 finally:
2389 2382 del wlock
2390 2383
2391 2384 def rollback(ui, repo):
2392 2385 """roll back the last transaction
2393 2386
2394 2387 This command should be used with care. There is only one level of
2395 2388 rollback, and there is no way to undo a rollback. It will also
2396 2389 restore the dirstate at the time of the last transaction, losing
2397 2390 any dirstate changes since that time.
2398 2391
2399 2392 Transactions are used to encapsulate the effects of all commands
2400 2393 that create new changesets or propagate existing changesets into a
2401 2394 repository. For example, the following commands are transactional,
2402 2395 and their effects can be rolled back:
2403 2396
2404 2397 commit
2405 2398 import
2406 2399 pull
2407 2400 push (with this repository as destination)
2408 2401 unbundle
2409 2402
2410 2403 This command is not intended for use on public repositories. Once
2411 2404 changes are visible for pull by other users, rolling a transaction
2412 2405 back locally is ineffective (someone else may already have pulled
2413 2406 the changes). Furthermore, a race is possible with readers of the
2414 2407 repository; for example an in-progress pull from the repository
2415 2408 may fail if a rollback is performed.
2416 2409 """
2417 2410 repo.rollback()
2418 2411
2419 2412 def root(ui, repo):
2420 2413 """print the root (top) of the current working dir
2421 2414
2422 2415 Print the root directory of the current repository.
2423 2416 """
2424 2417 ui.write(repo.root + "\n")
2425 2418
2426 2419 def serve(ui, repo, **opts):
2427 2420 """export the repository via HTTP
2428 2421
2429 2422 Start a local HTTP repository browser and pull server.
2430 2423
2431 2424 By default, the server logs accesses to stdout and errors to
2432 2425 stderr. Use the "-A" and "-E" options to log to files.
2433 2426 """
2434 2427
2435 2428 if opts["stdio"]:
2436 2429 if repo is None:
2437 2430 raise hg.RepoError(_("There is no Mercurial repository here"
2438 2431 " (.hg not found)"))
2439 2432 s = sshserver.sshserver(ui, repo)
2440 2433 s.serve_forever()
2441 2434
2442 2435 parentui = ui.parentui or ui
2443 2436 optlist = ("name templates style address port prefix ipv6"
2444 2437 " accesslog errorlog webdir_conf certificate")
2445 2438 for o in optlist.split():
2446 2439 if opts[o]:
2447 2440 parentui.setconfig("web", o, str(opts[o]))
2448 2441 if (repo is not None) and (repo.ui != parentui):
2449 2442 repo.ui.setconfig("web", o, str(opts[o]))
2450 2443
2451 2444 if repo is None and not ui.config("web", "webdir_conf"):
2452 2445 raise hg.RepoError(_("There is no Mercurial repository here"
2453 2446 " (.hg not found)"))
2454 2447
2455 2448 class service:
2456 2449 def init(self):
2457 2450 util.set_signal_handler()
2458 2451 try:
2459 2452 self.httpd = hgweb.server.create_server(parentui, repo)
2460 2453 except socket.error, inst:
2461 2454 raise util.Abort(_('cannot start server: ') + inst.args[1])
2462 2455
2463 2456 if not ui.verbose: return
2464 2457
2465 2458 if self.httpd.prefix:
2466 2459 prefix = self.httpd.prefix.strip('/') + '/'
2467 2460 else:
2468 2461 prefix = ''
2469 2462
2470 2463 if self.httpd.port != 80:
2471 2464 ui.status(_('listening at http://%s:%d/%s\n') %
2472 2465 (self.httpd.addr, self.httpd.port, prefix))
2473 2466 else:
2474 2467 ui.status(_('listening at http://%s/%s\n') %
2475 2468 (self.httpd.addr, prefix))
2476 2469
2477 2470 def run(self):
2478 2471 self.httpd.serve_forever()
2479 2472
2480 2473 service = service()
2481 2474
2482 2475 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2483 2476
2484 2477 def status(ui, repo, *pats, **opts):
2485 2478 """show changed files in the working directory
2486 2479
2487 2480 Show status of files in the repository. If names are given, only
2488 2481 files that match are shown. Files that are clean or ignored or
2489 2482 source of a copy/move operation, are not listed unless -c (clean),
2490 2483 -i (ignored), -C (copies) or -A is given. Unless options described
2491 2484 with "show only ..." are given, the options -mardu are used.
2492 2485
2493 2486 NOTE: status may appear to disagree with diff if permissions have
2494 2487 changed or a merge has occurred. The standard diff format does not
2495 2488 report permission changes and diff only reports changes relative
2496 2489 to one merge parent.
2497 2490
2498 2491 If one revision is given, it is used as the base revision.
2499 2492 If two revisions are given, the difference between them is shown.
2500 2493
2501 2494 The codes used to show the status of files are:
2502 2495 M = modified
2503 2496 A = added
2504 2497 R = removed
2505 2498 C = clean
2506 2499 ! = deleted, but still tracked
2507 2500 ? = not tracked
2508 2501 I = ignored
2509 2502 = the previous added file was copied from here
2510 2503 """
2511 2504
2512 2505 all = opts['all']
2513 2506 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2514 2507
2515 2508 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2516 2509 cwd = (pats and repo.getcwd()) or ''
2517 2510 modified, added, removed, deleted, unknown, ignored, clean = [
2518 2511 n for n in repo.status(node1=node1, node2=node2, files=files,
2519 2512 match=matchfn,
2520 2513 list_ignored=all or opts['ignored'],
2521 2514 list_clean=all or opts['clean'])]
2522 2515
2523 2516 changetypes = (('modified', 'M', modified),
2524 2517 ('added', 'A', added),
2525 2518 ('removed', 'R', removed),
2526 2519 ('deleted', '!', deleted),
2527 2520 ('unknown', '?', unknown),
2528 2521 ('ignored', 'I', ignored))
2529 2522
2530 2523 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2531 2524
2532 2525 end = opts['print0'] and '\0' or '\n'
2533 2526
2534 2527 for opt, char, changes in ([ct for ct in explicit_changetypes
2535 2528 if all or opts[ct[0]]]
2536 2529 or changetypes):
2537 2530 if opts['no_status']:
2538 2531 format = "%%s%s" % end
2539 2532 else:
2540 2533 format = "%s %%s%s" % (char, end)
2541 2534
2542 2535 for f in changes:
2543 2536 ui.write(format % repo.pathto(f, cwd))
2544 2537 if ((all or opts.get('copies')) and not opts.get('no_status')):
2545 2538 copied = repo.dirstate.copied(f)
2546 2539 if copied:
2547 2540 ui.write(' %s%s' % (repo.pathto(copied, cwd), end))
2548 2541
2549 2542 def tag(ui, repo, name, rev_=None, **opts):
2550 2543 """add a tag for the current or given revision
2551 2544
2552 2545 Name a particular revision using <name>.
2553 2546
2554 2547 Tags are used to name particular revisions of the repository and are
2555 2548 very useful to compare different revision, to go back to significant
2556 2549 earlier versions or to mark branch points as releases, etc.
2557 2550
2558 2551 If no revision is given, the parent of the working directory is used,
2559 2552 or tip if no revision is checked out.
2560 2553
2561 2554 To facilitate version control, distribution, and merging of tags,
2562 2555 they are stored as a file named ".hgtags" which is managed
2563 2556 similarly to other project files and can be hand-edited if
2564 2557 necessary. The file '.hg/localtags' is used for local tags (not
2565 2558 shared among repositories).
2566 2559
2567 2560 See 'hg help dates' for a list of formats valid for -d/--date.
2568 2561 """
2569 2562 if name in ['tip', '.', 'null']:
2570 2563 raise util.Abort(_("the name '%s' is reserved") % name)
2571 2564 if rev_ is not None:
2572 2565 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2573 2566 "please use 'hg tag [-r REV] NAME' instead\n"))
2574 2567 if opts['rev']:
2575 2568 raise util.Abort(_("use only one form to specify the revision"))
2576 2569 if opts['rev'] and opts['remove']:
2577 2570 raise util.Abort(_("--rev and --remove are incompatible"))
2578 2571 if opts['rev']:
2579 2572 rev_ = opts['rev']
2580 2573 message = opts['message']
2581 2574 if opts['remove']:
2582 2575 tagtype = repo.tagtype(name)
2583 2576
2584 2577 if not tagtype:
2585 2578 raise util.Abort(_('tag %s does not exist') % name)
2586 2579 if opts['local'] and tagtype == 'global':
2587 2580 raise util.Abort(_('%s tag is global') % name)
2588 2581 if not opts['local'] and tagtype == 'local':
2589 2582 raise util.Abort(_('%s tag is local') % name)
2590 2583
2591 2584 rev_ = nullid
2592 2585 if not message:
2593 2586 message = _('Removed tag %s') % name
2594 2587 elif name in repo.tags() and not opts['force']:
2595 2588 raise util.Abort(_('a tag named %s already exists (use -f to force)')
2596 2589 % name)
2597 2590 if not rev_ and repo.dirstate.parents()[1] != nullid:
2598 2591 raise util.Abort(_('uncommitted merge - please provide a '
2599 2592 'specific revision'))
2600 2593 r = repo.changectx(rev_).node()
2601 2594
2602 2595 if not message:
2603 2596 message = _('Added tag %s for changeset %s') % (name, short(r))
2604 2597
2605 2598 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2606 2599
2607 2600 def tags(ui, repo):
2608 2601 """list repository tags
2609 2602
2610 2603 List the repository tags.
2611 2604
2612 2605 This lists both regular and local tags. When the -v/--verbose switch
2613 2606 is used, a third column "local" is printed for local tags.
2614 2607 """
2615 2608
2616 2609 l = repo.tagslist()
2617 2610 l.reverse()
2618 2611 hexfunc = ui.debugflag and hex or short
2619 2612 tagtype = ""
2620 2613
2621 2614 for t, n in l:
2622 2615 if ui.quiet:
2623 2616 ui.write("%s\n" % t)
2624 2617 continue
2625 2618
2626 2619 try:
2627 2620 hn = hexfunc(n)
2628 2621 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2629 2622 except revlog.LookupError:
2630 2623 r = " ?:%s" % hn
2631 2624 else:
2632 2625 spaces = " " * (30 - util.locallen(t))
2633 2626 if ui.verbose:
2634 2627 if repo.tagtype(t) == 'local':
2635 2628 tagtype = " local"
2636 2629 else:
2637 2630 tagtype = ""
2638 2631 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2639 2632
2640 2633 def tip(ui, repo, **opts):
2641 2634 """show the tip revision
2642 2635
2643 2636 Show the tip revision.
2644 2637 """
2645 2638 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2646 2639
2647 2640 def unbundle(ui, repo, fname1, *fnames, **opts):
2648 2641 """apply one or more changegroup files
2649 2642
2650 2643 Apply one or more compressed changegroup files generated by the
2651 2644 bundle command.
2652 2645 """
2653 2646 fnames = (fname1,) + fnames
2654 2647
2655 2648 lock = None
2656 2649 try:
2657 2650 lock = repo.lock()
2658 2651 for fname in fnames:
2659 2652 if os.path.exists(fname):
2660 2653 f = open(fname, "rb")
2661 2654 else:
2662 2655 f = urllib.urlopen(fname)
2663 2656 gen = changegroup.readbundle(f, fname)
2664 2657 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2665 2658 finally:
2666 2659 del lock
2667 2660
2668 2661 return postincoming(ui, repo, modheads, opts['update'], None)
2669 2662
2670 2663 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2671 2664 """update working directory
2672 2665
2673 2666 Update the working directory to the specified revision, or the
2674 2667 tip of the current branch if none is specified.
2675 2668 See 'hg help dates' for a list of formats valid for -d/--date.
2676 2669
2677 2670 If there are no outstanding changes in the working directory and
2678 2671 there is a linear relationship between the current version and the
2679 2672 requested version, the result is the requested version.
2680 2673
2681 2674 To merge the working directory with another revision, use the
2682 2675 merge command.
2683 2676
2684 2677 By default, update will refuse to run if doing so would require
2685 2678 discarding local changes.
2686 2679 """
2687 2680 if rev and node:
2688 2681 raise util.Abort(_("please specify just one revision"))
2689 2682
2690 2683 if not rev:
2691 2684 rev = node
2692 2685
2693 2686 if date:
2694 2687 if rev:
2695 2688 raise util.Abort(_("you can't specify a revision and a date"))
2696 2689 rev = cmdutil.finddate(ui, repo, date)
2697 2690
2698 2691 if clean:
2699 2692 return hg.clean(repo, rev)
2700 2693 else:
2701 2694 return hg.update(repo, rev)
2702 2695
2703 2696 def verify(ui, repo):
2704 2697 """verify the integrity of the repository
2705 2698
2706 2699 Verify the integrity of the current repository.
2707 2700
2708 2701 This will perform an extensive check of the repository's
2709 2702 integrity, validating the hashes and checksums of each entry in
2710 2703 the changelog, manifest, and tracked files, as well as the
2711 2704 integrity of their crosslinks and indices.
2712 2705 """
2713 2706 return hg.verify(repo)
2714 2707
2715 2708 def version_(ui):
2716 2709 """output version and copyright information"""
2717 2710 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2718 2711 % version.get_version())
2719 2712 ui.status(_(
2720 2713 "\nCopyright (C) 2005-2008 Matt Mackall <mpm@selenic.com> and others\n"
2721 2714 "This is free software; see the source for copying conditions. "
2722 2715 "There is NO\nwarranty; "
2723 2716 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2724 2717 ))
2725 2718
2726 2719 # Command options and aliases are listed here, alphabetically
2727 2720
2728 2721 globalopts = [
2729 2722 ('R', 'repository', '',
2730 2723 _('repository root directory or symbolic path name')),
2731 2724 ('', 'cwd', '', _('change working directory')),
2732 2725 ('y', 'noninteractive', None,
2733 2726 _('do not prompt, assume \'yes\' for any required answers')),
2734 2727 ('q', 'quiet', None, _('suppress output')),
2735 2728 ('v', 'verbose', None, _('enable additional output')),
2736 2729 ('', 'config', [], _('set/override config option')),
2737 2730 ('', 'debug', None, _('enable debugging output')),
2738 2731 ('', 'debugger', None, _('start debugger')),
2739 2732 ('', 'encoding', util._encoding, _('set the charset encoding')),
2740 2733 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2741 2734 ('', 'lsprof', None, _('print improved command execution profile')),
2742 2735 ('', 'traceback', None, _('print traceback on exception')),
2743 2736 ('', 'time', None, _('time how long the command takes')),
2744 2737 ('', 'profile', None, _('print command execution profile')),
2745 2738 ('', 'version', None, _('output version information and exit')),
2746 2739 ('h', 'help', None, _('display help and exit')),
2747 2740 ]
2748 2741
2749 2742 dryrunopts = [('n', 'dry-run', None,
2750 2743 _('do not perform actions, just print output'))]
2751 2744
2752 2745 remoteopts = [
2753 2746 ('e', 'ssh', '', _('specify ssh command to use')),
2754 2747 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2755 2748 ]
2756 2749
2757 2750 walkopts = [
2758 2751 ('I', 'include', [], _('include names matching the given patterns')),
2759 2752 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2760 2753 ]
2761 2754
2762 2755 commitopts = [
2763 2756 ('m', 'message', '', _('use <text> as commit message')),
2764 2757 ('l', 'logfile', '', _('read commit message from <file>')),
2765 2758 ]
2766 2759
2767 2760 commitopts2 = [
2768 2761 ('d', 'date', '', _('record datecode as commit date')),
2769 2762 ('u', 'user', '', _('record user as committer')),
2770 2763 ]
2771 2764
2772 2765 table = {
2773 2766 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2774 2767 "addremove":
2775 2768 (addremove,
2776 2769 [('s', 'similarity', '',
2777 2770 _('guess renamed files by similarity (0<=s<=100)')),
2778 2771 ] + walkopts + dryrunopts,
2779 2772 _('hg addremove [OPTION]... [FILE]...')),
2780 2773 "^annotate|blame":
2781 2774 (annotate,
2782 2775 [('r', 'rev', '', _('annotate the specified revision')),
2783 2776 ('f', 'follow', None, _('follow file copies and renames')),
2784 2777 ('a', 'text', None, _('treat all files as text')),
2785 2778 ('u', 'user', None, _('list the author (long with -v)')),
2786 2779 ('d', 'date', None, _('list the date (short with -q)')),
2787 2780 ('n', 'number', None, _('list the revision number (default)')),
2788 2781 ('c', 'changeset', None, _('list the changeset')),
2789 2782 ('l', 'line-number', None,
2790 2783 _('show line number at the first appearance'))
2791 2784 ] + walkopts,
2792 2785 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
2793 2786 "archive":
2794 2787 (archive,
2795 2788 [('', 'no-decode', None, _('do not pass files through decoders')),
2796 2789 ('p', 'prefix', '', _('directory prefix for files in archive')),
2797 2790 ('r', 'rev', '', _('revision to distribute')),
2798 2791 ('t', 'type', '', _('type of distribution to create')),
2799 2792 ] + walkopts,
2800 2793 _('hg archive [OPTION]... DEST')),
2801 2794 "backout":
2802 2795 (backout,
2803 2796 [('', 'merge', None,
2804 2797 _('merge with old dirstate parent after backout')),
2805 2798 ('', 'parent', '', _('parent to choose when backing out merge')),
2806 2799 ('r', 'rev', '', _('revision to backout')),
2807 2800 ] + walkopts + commitopts + commitopts2,
2808 2801 _('hg backout [OPTION]... [-r] REV')),
2809 2802 "bisect":
2810 2803 (bisect,
2811 2804 [('r', 'reset', False, _('reset bisect state')),
2812 2805 ('g', 'good', False, _('mark changeset good')),
2813 2806 ('b', 'bad', False, _('mark changeset bad')),
2814 2807 ('s', 'skip', False, _('skip testing changeset')),
2815 2808 ('U', 'noupdate', False, _('do not update to target'))],
2816 2809 _("hg bisect [-gbsr] [REV]")),
2817 2810 "branch":
2818 2811 (branch,
2819 2812 [('f', 'force', None,
2820 2813 _('set branch name even if it shadows an existing branch'))],
2821 2814 _('hg branch [-f] [NAME]')),
2822 2815 "branches":
2823 2816 (branches,
2824 2817 [('a', 'active', False,
2825 2818 _('show only branches that have unmerged heads'))],
2826 2819 _('hg branches [-a]')),
2827 2820 "bundle":
2828 2821 (bundle,
2829 2822 [('f', 'force', None,
2830 2823 _('run even when remote repository is unrelated')),
2831 2824 ('r', 'rev', [],
2832 2825 _('a changeset you would like to bundle')),
2833 2826 ('', 'base', [],
2834 2827 _('a base changeset to specify instead of a destination')),
2835 2828 ('a', 'all', None,
2836 2829 _('bundle all changesets in the repository')),
2837 2830 ] + remoteopts,
2838 2831 _('hg bundle [-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
2839 2832 "cat":
2840 2833 (cat,
2841 2834 [('o', 'output', '', _('print output to file with formatted name')),
2842 2835 ('r', 'rev', '', _('print the given revision')),
2843 2836 ('', 'decode', None, _('apply any matching decode filter')),
2844 2837 ] + walkopts,
2845 2838 _('hg cat [OPTION]... FILE...')),
2846 2839 "^clone":
2847 2840 (clone,
2848 2841 [('U', 'noupdate', None, _('do not update the new working directory')),
2849 2842 ('r', 'rev', [],
2850 2843 _('a changeset you would like to have after cloning')),
2851 2844 ('', 'pull', None, _('use pull protocol to copy metadata')),
2852 2845 ('', 'uncompressed', None,
2853 2846 _('use uncompressed transfer (fast over LAN)')),
2854 2847 ] + remoteopts,
2855 2848 _('hg clone [OPTION]... SOURCE [DEST]')),
2856 2849 "^commit|ci":
2857 2850 (commit,
2858 2851 [('A', 'addremove', None,
2859 2852 _('mark new/missing files as added/removed before committing')),
2860 2853 ] + walkopts + commitopts + commitopts2,
2861 2854 _('hg commit [OPTION]... [FILE]...')),
2862 2855 "copy|cp":
2863 2856 (copy,
2864 2857 [('A', 'after', None, _('record a copy that has already occurred')),
2865 2858 ('f', 'force', None,
2866 2859 _('forcibly copy over an existing managed file')),
2867 2860 ] + walkopts + dryrunopts,
2868 2861 _('hg copy [OPTION]... [SOURCE]... DEST')),
2869 2862 "debugancestor": (debugancestor, [],
2870 2863 _('hg debugancestor [INDEX] REV1 REV2')),
2871 2864 "debugcheckstate": (debugcheckstate, [], _('hg debugcheckstate')),
2872 2865 "debugcomplete":
2873 2866 (debugcomplete,
2874 2867 [('o', 'options', None, _('show the command options'))],
2875 2868 _('hg debugcomplete [-o] CMD')),
2876 2869 "debugdate":
2877 2870 (debugdate,
2878 2871 [('e', 'extended', None, _('try extended date formats'))],
2879 2872 _('hg debugdate [-e] DATE [RANGE]')),
2880 2873 "debugdata": (debugdata, [], _('hg debugdata FILE REV')),
2881 2874 "debugfsinfo": (debugfsinfo, [], _('hg debugfsinfo [PATH]')),
2882 2875 "debugindex": (debugindex, [], _('hg debugindex FILE')),
2883 2876 "debugindexdot": (debugindexdot, [], _('hg debugindexdot FILE')),
2884 2877 "debuginstall": (debuginstall, [], _('hg debuginstall')),
2885 2878 "debugrawcommit|rawcommit":
2886 2879 (rawcommit,
2887 2880 [('p', 'parent', [], _('parent')),
2888 2881 ('F', 'files', '', _('file list'))
2889 2882 ] + commitopts + commitopts2,
2890 2883 _('hg debugrawcommit [OPTION]... [FILE]...')),
2891 2884 "debugrebuildstate":
2892 2885 (debugrebuildstate,
2893 2886 [('r', 'rev', '', _('revision to rebuild to'))],
2894 2887 _('hg debugrebuildstate [-r REV] [REV]')),
2895 2888 "debugrename":
2896 2889 (debugrename,
2897 2890 [('r', 'rev', '', _('revision to debug'))],
2898 2891 _('hg debugrename [-r REV] FILE')),
2899 2892 "debugsetparents":
2900 2893 (debugsetparents,
2901 2894 [],
2902 2895 _('hg debugsetparents REV1 [REV2]')),
2903 2896 "debugstate": (debugstate, [], _('hg debugstate')),
2904 2897 "debugwalk": (debugwalk, walkopts, _('hg debugwalk [OPTION]... [FILE]...')),
2905 2898 "^diff":
2906 2899 (diff,
2907 2900 [('r', 'rev', [], _('revision')),
2908 2901 ('a', 'text', None, _('treat all files as text')),
2909 2902 ('p', 'show-function', None,
2910 2903 _('show which function each change is in')),
2911 2904 ('g', 'git', None, _('use git extended diff format')),
2912 2905 ('', 'nodates', None, _("don't include dates in diff headers")),
2913 2906 ('w', 'ignore-all-space', None,
2914 2907 _('ignore white space when comparing lines')),
2915 2908 ('b', 'ignore-space-change', None,
2916 2909 _('ignore changes in the amount of white space')),
2917 2910 ('B', 'ignore-blank-lines', None,
2918 2911 _('ignore changes whose lines are all blank')),
2919 2912 ('U', 'unified', 3,
2920 2913 _('number of lines of context to show'))
2921 2914 ] + walkopts,
2922 2915 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2923 2916 "^export":
2924 2917 (export,
2925 2918 [('o', 'output', '', _('print output to file with formatted name')),
2926 2919 ('a', 'text', None, _('treat all files as text')),
2927 2920 ('g', 'git', None, _('use git extended diff format')),
2928 2921 ('', 'nodates', None, _("don't include dates in diff headers")),
2929 2922 ('', 'switch-parent', None, _('diff against the second parent'))],
2930 2923 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2931 2924 "grep":
2932 2925 (grep,
2933 2926 [('0', 'print0', None, _('end fields with NUL')),
2934 2927 ('', 'all', None, _('print all revisions that match')),
2935 2928 ('f', 'follow', None,
2936 2929 _('follow changeset history, or file history across copies and renames')),
2937 2930 ('i', 'ignore-case', None, _('ignore case when matching')),
2938 2931 ('l', 'files-with-matches', None,
2939 2932 _('print only filenames and revs that match')),
2940 2933 ('n', 'line-number', None, _('print matching line numbers')),
2941 2934 ('r', 'rev', [], _('search in given revision range')),
2942 2935 ('u', 'user', None, _('list the author (long with -v)')),
2943 2936 ('d', 'date', None, _('list the date (short with -q)')),
2944 2937 ] + walkopts,
2945 2938 _('hg grep [OPTION]... PATTERN [FILE]...')),
2946 2939 "heads":
2947 2940 (heads,
2948 2941 [('', 'style', '', _('display using template map file')),
2949 2942 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2950 2943 ('', 'template', '', _('display with template'))],
2951 2944 _('hg heads [-r REV] [REV]...')),
2952 2945 "help": (help_, [], _('hg help [COMMAND]')),
2953 2946 "identify|id":
2954 2947 (identify,
2955 2948 [('r', 'rev', '', _('identify the specified rev')),
2956 2949 ('n', 'num', None, _('show local revision number')),
2957 2950 ('i', 'id', None, _('show global revision id')),
2958 2951 ('b', 'branch', None, _('show branch')),
2959 2952 ('t', 'tags', None, _('show tags'))],
2960 2953 _('hg identify [-nibt] [-r REV] [SOURCE]')),
2961 2954 "import|patch":
2962 2955 (import_,
2963 2956 [('p', 'strip', 1,
2964 2957 _('directory strip option for patch. This has the same\n'
2965 2958 'meaning as the corresponding patch option')),
2966 2959 ('b', 'base', '', _('base path')),
2967 2960 ('f', 'force', None,
2968 2961 _('skip check for outstanding uncommitted changes')),
2969 2962 ('', 'no-commit', None, _("don't commit, just update the working directory")),
2970 2963 ('', 'exact', None,
2971 2964 _('apply patch to the nodes from which it was generated')),
2972 2965 ('', 'import-branch', None,
2973 2966 _('Use any branch information in patch (implied by --exact)'))] +
2974 2967 commitopts + commitopts2,
2975 2968 _('hg import [OPTION]... PATCH...')),
2976 2969 "incoming|in":
2977 2970 (incoming,
2978 2971 [('M', 'no-merges', None, _('do not show merges')),
2979 2972 ('f', 'force', None,
2980 2973 _('run even when remote repository is unrelated')),
2981 2974 ('', 'style', '', _('display using template map file')),
2982 2975 ('n', 'newest-first', None, _('show newest record first')),
2983 2976 ('', 'bundle', '', _('file to store the bundles into')),
2984 2977 ('p', 'patch', None, _('show patch')),
2985 2978 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2986 2979 ('', 'template', '', _('display with template')),
2987 2980 ] + remoteopts,
2988 2981 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2989 2982 ' [--bundle FILENAME] [SOURCE]')),
2990 2983 "^init":
2991 2984 (init,
2992 2985 remoteopts,
2993 2986 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2994 2987 "locate":
2995 2988 (locate,
2996 2989 [('r', 'rev', '', _('search the repository as it stood at rev')),
2997 2990 ('0', 'print0', None,
2998 2991 _('end filenames with NUL, for use with xargs')),
2999 2992 ('f', 'fullpath', None,
3000 2993 _('print complete paths from the filesystem root')),
3001 2994 ] + walkopts,
3002 2995 _('hg locate [OPTION]... [PATTERN]...')),
3003 2996 "^log|history":
3004 2997 (log,
3005 2998 [('f', 'follow', None,
3006 2999 _('follow changeset history, or file history across copies and renames')),
3007 3000 ('', 'follow-first', None,
3008 3001 _('only follow the first parent of merge changesets')),
3009 3002 ('d', 'date', '', _('show revs matching date spec')),
3010 3003 ('C', 'copies', None, _('show copied files')),
3011 3004 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3012 3005 ('l', 'limit', '', _('limit number of changes displayed')),
3013 3006 ('r', 'rev', [], _('show the specified revision or range')),
3014 3007 ('', 'removed', None, _('include revs where files were removed')),
3015 3008 ('M', 'no-merges', None, _('do not show merges')),
3016 3009 ('', 'style', '', _('display using template map file')),
3017 3010 ('m', 'only-merges', None, _('show only merges')),
3018 3011 ('b', 'only-branch', [],
3019 3012 _('show only changesets within the given named branch')),
3020 3013 ('p', 'patch', None, _('show patch')),
3021 3014 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3022 3015 ('', 'template', '', _('display with template')),
3023 3016 ] + walkopts,
3024 3017 _('hg log [OPTION]... [FILE]')),
3025 3018 "manifest":
3026 3019 (manifest,
3027 3020 [('r', 'rev', '', _('revision to display'))],
3028 3021 _('hg manifest [-r REV]')),
3029 3022 "^merge":
3030 3023 (merge,
3031 3024 [('f', 'force', None, _('force a merge with outstanding changes')),
3032 3025 ('r', 'rev', '', _('revision to merge')),
3033 3026 ],
3034 3027 _('hg merge [-f] [[-r] REV]')),
3035 3028 "outgoing|out":
3036 3029 (outgoing,
3037 3030 [('M', 'no-merges', None, _('do not show merges')),
3038 3031 ('f', 'force', None,
3039 3032 _('run even when remote repository is unrelated')),
3040 3033 ('p', 'patch', None, _('show patch')),
3041 3034 ('', 'style', '', _('display using template map file')),
3042 3035 ('r', 'rev', [], _('a specific revision you would like to push')),
3043 3036 ('n', 'newest-first', None, _('show newest record first')),
3044 3037 ('', 'template', '', _('display with template')),
3045 3038 ] + remoteopts,
3046 3039 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3047 3040 "^parents":
3048 3041 (parents,
3049 3042 [('r', 'rev', '', _('show parents from the specified rev')),
3050 3043 ('', 'style', '', _('display using template map file')),
3051 3044 ('', 'template', '', _('display with template'))],
3052 3045 _('hg parents [-r REV] [FILE]')),
3053 3046 "paths": (paths, [], _('hg paths [NAME]')),
3054 3047 "^pull":
3055 3048 (pull,
3056 3049 [('u', 'update', None,
3057 3050 _('update to new tip if changesets were pulled')),
3058 3051 ('f', 'force', None,
3059 3052 _('run even when remote repository is unrelated')),
3060 3053 ('r', 'rev', [],
3061 3054 _('a specific revision up to which you would like to pull')),
3062 3055 ] + remoteopts,
3063 3056 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3064 3057 "^push":
3065 3058 (push,
3066 3059 [('f', 'force', None, _('force push')),
3067 3060 ('r', 'rev', [], _('a specific revision you would like to push')),
3068 3061 ] + remoteopts,
3069 3062 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3070 3063 "recover": (recover, [], _('hg recover')),
3071 3064 "^remove|rm":
3072 3065 (remove,
3073 3066 [('A', 'after', None, _('record remove without deleting')),
3074 3067 ('f', 'force', None, _('remove file even if modified')),
3075 3068 ] + walkopts,
3076 3069 _('hg remove [OPTION]... FILE...')),
3077 3070 "rename|mv":
3078 3071 (rename,
3079 3072 [('A', 'after', None, _('record a rename that has already occurred')),
3080 3073 ('f', 'force', None,
3081 3074 _('forcibly copy over an existing managed file')),
3082 3075 ] + walkopts + dryrunopts,
3083 3076 _('hg rename [OPTION]... SOURCE... DEST')),
3084 3077 "revert":
3085 3078 (revert,
3086 3079 [('a', 'all', None, _('revert all changes when no arguments given')),
3087 3080 ('d', 'date', '', _('tipmost revision matching date')),
3088 3081 ('r', 'rev', '', _('revision to revert to')),
3089 3082 ('', 'no-backup', None, _('do not save backup copies of files')),
3090 3083 ] + walkopts + dryrunopts,
3091 3084 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3092 3085 "rollback": (rollback, [], _('hg rollback')),
3093 3086 "root": (root, [], _('hg root')),
3094 3087 "^serve":
3095 3088 (serve,
3096 3089 [('A', 'accesslog', '', _('name of access log file to write to')),
3097 3090 ('d', 'daemon', None, _('run server in background')),
3098 3091 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3099 3092 ('E', 'errorlog', '', _('name of error log file to write to')),
3100 3093 ('p', 'port', 0, _('port to use (default: 8000)')),
3101 3094 ('a', 'address', '', _('address to use')),
3102 3095 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3103 3096 ('n', 'name', '',
3104 3097 _('name to show in web pages (default: working dir)')),
3105 3098 ('', 'webdir-conf', '', _('name of the webdir config file'
3106 3099 ' (serve more than one repo)')),
3107 3100 ('', 'pid-file', '', _('name of file to write process ID to')),
3108 3101 ('', 'stdio', None, _('for remote clients')),
3109 3102 ('t', 'templates', '', _('web templates to use')),
3110 3103 ('', 'style', '', _('template style to use')),
3111 3104 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3112 3105 ('', 'certificate', '', _('SSL certificate file'))],
3113 3106 _('hg serve [OPTION]...')),
3114 3107 "showconfig|debugconfig":
3115 3108 (showconfig,
3116 3109 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3117 3110 _('hg showconfig [-u] [NAME]...')),
3118 3111 "^status|st":
3119 3112 (status,
3120 3113 [('A', 'all', None, _('show status of all files')),
3121 3114 ('m', 'modified', None, _('show only modified files')),
3122 3115 ('a', 'added', None, _('show only added files')),
3123 3116 ('r', 'removed', None, _('show only removed files')),
3124 3117 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3125 3118 ('c', 'clean', None, _('show only files without changes')),
3126 3119 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3127 3120 ('i', 'ignored', None, _('show only ignored files')),
3128 3121 ('n', 'no-status', None, _('hide status prefix')),
3129 3122 ('C', 'copies', None, _('show source of copied files')),
3130 3123 ('0', 'print0', None,
3131 3124 _('end filenames with NUL, for use with xargs')),
3132 3125 ('', 'rev', [], _('show difference from revision')),
3133 3126 ] + walkopts,
3134 3127 _('hg status [OPTION]... [FILE]...')),
3135 3128 "tag":
3136 3129 (tag,
3137 3130 [('f', 'force', None, _('replace existing tag')),
3138 3131 ('l', 'local', None, _('make the tag local')),
3139 3132 ('r', 'rev', '', _('revision to tag')),
3140 3133 ('', 'remove', None, _('remove a tag')),
3141 3134 # -l/--local is already there, commitopts cannot be used
3142 3135 ('m', 'message', '', _('use <text> as commit message')),
3143 3136 ] + commitopts2,
3144 3137 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3145 3138 "tags": (tags, [], _('hg tags')),
3146 3139 "tip":
3147 3140 (tip,
3148 3141 [('', 'style', '', _('display using template map file')),
3149 3142 ('p', 'patch', None, _('show patch')),
3150 3143 ('', 'template', '', _('display with template'))],
3151 3144 _('hg tip [-p]')),
3152 3145 "unbundle":
3153 3146 (unbundle,
3154 3147 [('u', 'update', None,
3155 3148 _('update to new tip if changesets were unbundled'))],
3156 3149 _('hg unbundle [-u] FILE...')),
3157 3150 "^update|up|checkout|co":
3158 3151 (update,
3159 3152 [('C', 'clean', None, _('overwrite locally modified files')),
3160 3153 ('d', 'date', '', _('tipmost revision matching date')),
3161 3154 ('r', 'rev', '', _('revision'))],
3162 3155 _('hg update [-C] [-d DATE] [[-r] REV]')),
3163 3156 "verify": (verify, [], _('hg verify')),
3164 3157 "version": (version_, [], _('hg version')),
3165 3158 }
3166 3159
3167 3160 norepo = ("clone init version help debugcomplete debugdata"
3168 3161 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3169 3162 optionalrepo = ("identify paths serve showconfig debugancestor")
General Comments 0
You need to be logged in to leave comments. Login now