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