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