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