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