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