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