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