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