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