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