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