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