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