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