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