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