##// END OF EJS Templates
drop {short,hex}(ctx.node()) calls in favor of ctx methods
Alexander Solovyov -
r14055:421d56a0 default
parent child Browse files
Show More
@@ -1,4901 +1,4900 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from node import hex, bin, 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, scmutil, util, revlog, extensions, copies, error, bookmarks
13 13 import patch, help, mdiff, url, encoding, templatekw, discovery
14 14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
15 15 import merge as mergemod
16 16 import minirst, revset, templatefilters
17 17 import dagparser
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 Returns 0 if all files are successfully added.
47 47 """
48 48
49 49 m = cmdutil.match(repo, pats, opts)
50 50 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
51 51 opts.get('subrepos'), prefix="")
52 52 return rejected and 1 or 0
53 53
54 54 def addremove(ui, repo, *pats, **opts):
55 55 """add all new files, delete all missing files
56 56
57 57 Add all new files and remove all missing files from the
58 58 repository.
59 59
60 60 New files are ignored if they match any of the patterns in
61 61 ``.hgignore``. As with add, these changes take effect at the next
62 62 commit.
63 63
64 64 Use the -s/--similarity option to detect renamed files. With a
65 65 parameter greater than 0, this compares every removed file with
66 66 every added file and records those similar enough as renames. This
67 67 option takes a percentage between 0 (disabled) and 100 (files must
68 68 be identical) as its parameter. Detecting renamed files this way
69 69 can be expensive. After using this option, :hg:`status -C` can be
70 70 used to check which files were identified as moved or renamed.
71 71
72 72 Returns 0 if all files are successfully added.
73 73 """
74 74 try:
75 75 sim = float(opts.get('similarity') or 100)
76 76 except ValueError:
77 77 raise util.Abort(_('similarity must be a number'))
78 78 if sim < 0 or sim > 100:
79 79 raise util.Abort(_('similarity must be between 0 and 100'))
80 80 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
81 81
82 82 def annotate(ui, repo, *pats, **opts):
83 83 """show changeset information by line for each file
84 84
85 85 List changes in files, showing the revision id responsible for
86 86 each line
87 87
88 88 This command is useful for discovering when a change was made and
89 89 by whom.
90 90
91 91 Without the -a/--text option, annotate will avoid processing files
92 92 it detects as binary. With -a, annotate will annotate the file
93 93 anyway, although the results will probably be neither useful
94 94 nor desirable.
95 95
96 96 Returns 0 on success.
97 97 """
98 98 if opts.get('follow'):
99 99 # --follow is deprecated and now just an alias for -f/--file
100 100 # to mimic the behavior of Mercurial before version 1.5
101 101 opts['file'] = 1
102 102
103 103 datefunc = ui.quiet and util.shortdate or util.datestr
104 104 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
105 105
106 106 if not pats:
107 107 raise util.Abort(_('at least one filename or pattern is required'))
108 108
109 109 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
110 110 ('number', lambda x: str(x[0].rev())),
111 111 ('changeset', lambda x: short(x[0].node())),
112 112 ('date', getdate),
113 113 ('file', lambda x: x[0].path()),
114 114 ]
115 115
116 116 if (not opts.get('user') and not opts.get('changeset')
117 117 and not opts.get('date') and not opts.get('file')):
118 118 opts['number'] = 1
119 119
120 120 linenumber = opts.get('line_number') is not None
121 121 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
122 122 raise util.Abort(_('at least one of -n/-c is required for -l'))
123 123
124 124 funcmap = [func for op, func in opmap if opts.get(op)]
125 125 if linenumber:
126 126 lastfunc = funcmap[-1]
127 127 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
128 128
129 129 def bad(x, y):
130 130 raise util.Abort("%s: %s" % (x, y))
131 131
132 132 ctx = cmdutil.revsingle(repo, opts.get('rev'))
133 133 m = cmdutil.match(repo, pats, opts)
134 134 m.bad = bad
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 sized = [(x, encoding.colwidth(x)) for x in l]
149 149 ml = max([w for x, w in sized])
150 150 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
151 151
152 152 if pieces:
153 153 for p, l in zip(zip(*pieces), lines):
154 154 ui.write("%s: %s" % (" ".join(p), l[1]))
155 155
156 156 def archive(ui, repo, dest, **opts):
157 157 '''create an unversioned archive of a repository revision
158 158
159 159 By default, the revision used is the parent of the working
160 160 directory; use -r/--rev to specify a different revision.
161 161
162 162 The archive type is automatically detected based on file
163 163 extension (or override using -t/--type).
164 164
165 165 Valid types are:
166 166
167 167 :``files``: a directory full of files (default)
168 168 :``tar``: tar archive, uncompressed
169 169 :``tbz2``: tar archive, compressed using bzip2
170 170 :``tgz``: tar archive, compressed using gzip
171 171 :``uzip``: zip archive, uncompressed
172 172 :``zip``: zip archive, compressed using deflate
173 173
174 174 The exact name of the destination archive or directory is given
175 175 using a format string; see :hg:`help export` for details.
176 176
177 177 Each member added to an archive file has a directory prefix
178 178 prepended. Use -p/--prefix to specify a format string for the
179 179 prefix. The default is the basename of the archive, with suffixes
180 180 removed.
181 181
182 182 Returns 0 on success.
183 183 '''
184 184
185 185 ctx = cmdutil.revsingle(repo, opts.get('rev'))
186 186 if not ctx:
187 187 raise util.Abort(_('no working directory: please specify a revision'))
188 188 node = ctx.node()
189 189 dest = cmdutil.make_filename(repo, dest, node)
190 190 if os.path.realpath(dest) == repo.root:
191 191 raise util.Abort(_('repository root cannot be destination'))
192 192
193 193 kind = opts.get('type') or archival.guesskind(dest) or 'files'
194 194 prefix = opts.get('prefix')
195 195
196 196 if dest == '-':
197 197 if kind == 'files':
198 198 raise util.Abort(_('cannot archive plain files to stdout'))
199 199 dest = sys.stdout
200 200 if not prefix:
201 201 prefix = os.path.basename(repo.root) + '-%h'
202 202
203 203 prefix = cmdutil.make_filename(repo, prefix, node)
204 204 matchfn = cmdutil.match(repo, [], opts)
205 205 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
206 206 matchfn, prefix, subrepos=opts.get('subrepos'))
207 207
208 208 def backout(ui, repo, node=None, rev=None, **opts):
209 209 '''reverse effect of earlier changeset
210 210
211 211 Prepare a new changeset with the effect of REV undone in the
212 212 current working directory.
213 213
214 214 If REV is the parent of the working directory, then this new changeset
215 215 is committed automatically. Otherwise, hg needs to merge the
216 216 changes and the merged result is left uncommitted.
217 217
218 218 By default, the pending changeset will have one parent,
219 219 maintaining a linear history. With --merge, the pending changeset
220 220 will instead have two parents: the old parent of the working
221 221 directory and a new child of REV that simply undoes REV.
222 222
223 223 Before version 1.7, the behavior without --merge was equivalent to
224 224 specifying --merge followed by :hg:`update --clean .` to cancel
225 225 the merge and leave the child of REV as a head to be merged
226 226 separately.
227 227
228 228 See :hg:`help dates` for a list of formats valid for -d/--date.
229 229
230 230 Returns 0 on success.
231 231 '''
232 232 if rev and node:
233 233 raise util.Abort(_("please specify just one revision"))
234 234
235 235 if not rev:
236 236 rev = node
237 237
238 238 if not rev:
239 239 raise util.Abort(_("please specify a revision to backout"))
240 240
241 241 date = opts.get('date')
242 242 if date:
243 243 opts['date'] = util.parsedate(date)
244 244
245 245 cmdutil.bail_if_changed(repo)
246 246 node = cmdutil.revsingle(repo, rev).node()
247 247
248 248 op1, op2 = repo.dirstate.parents()
249 249 a = repo.changelog.ancestor(op1, node)
250 250 if a != node:
251 251 raise util.Abort(_('cannot backout change on a different branch'))
252 252
253 253 p1, p2 = repo.changelog.parents(node)
254 254 if p1 == nullid:
255 255 raise util.Abort(_('cannot backout a change with no parents'))
256 256 if p2 != nullid:
257 257 if not opts.get('parent'):
258 258 raise util.Abort(_('cannot backout a merge changeset without '
259 259 '--parent'))
260 260 p = repo.lookup(opts['parent'])
261 261 if p not in (p1, p2):
262 262 raise util.Abort(_('%s is not a parent of %s') %
263 263 (short(p), short(node)))
264 264 parent = p
265 265 else:
266 266 if opts.get('parent'):
267 267 raise util.Abort(_('cannot use --parent on non-merge changeset'))
268 268 parent = p1
269 269
270 270 # the backout should appear on the same branch
271 271 branch = repo.dirstate.branch()
272 272 hg.clean(repo, node, show_stats=False)
273 273 repo.dirstate.setbranch(branch)
274 274 revert_opts = opts.copy()
275 275 revert_opts['date'] = None
276 276 revert_opts['all'] = True
277 277 revert_opts['rev'] = hex(parent)
278 278 revert_opts['no_backup'] = None
279 279 revert(ui, repo, **revert_opts)
280 280 if not opts.get('merge') and op1 != node:
281 281 try:
282 282 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
283 283 return hg.update(repo, op1)
284 284 finally:
285 285 ui.setconfig('ui', 'forcemerge', '')
286 286
287 287 commit_opts = opts.copy()
288 288 commit_opts['addremove'] = False
289 289 if not commit_opts['message'] and not commit_opts['logfile']:
290 290 # we don't translate commit messages
291 291 commit_opts['message'] = "Backed out changeset %s" % short(node)
292 292 commit_opts['force_editor'] = True
293 293 commit(ui, repo, **commit_opts)
294 294 def nice(node):
295 295 return '%d:%s' % (repo.changelog.rev(node), short(node))
296 296 ui.status(_('changeset %s backs out changeset %s\n') %
297 297 (nice(repo.changelog.tip()), nice(node)))
298 298 if opts.get('merge') and op1 != node:
299 299 hg.clean(repo, op1, show_stats=False)
300 300 ui.status(_('merging with changeset %s\n')
301 301 % nice(repo.changelog.tip()))
302 302 try:
303 303 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
304 304 return hg.merge(repo, hex(repo.changelog.tip()))
305 305 finally:
306 306 ui.setconfig('ui', 'forcemerge', '')
307 307 return 0
308 308
309 309 def bisect(ui, repo, rev=None, extra=None, command=None,
310 310 reset=None, good=None, bad=None, skip=None, extend=None,
311 311 noupdate=None):
312 312 """subdivision search of changesets
313 313
314 314 This command helps to find changesets which introduce problems. To
315 315 use, mark the earliest changeset you know exhibits the problem as
316 316 bad, then mark the latest changeset which is free from the problem
317 317 as good. Bisect will update your working directory to a revision
318 318 for testing (unless the -U/--noupdate option is specified). Once
319 319 you have performed tests, mark the working directory as good or
320 320 bad, and bisect will either update to another candidate changeset
321 321 or announce that it has found the bad revision.
322 322
323 323 As a shortcut, you can also use the revision argument to mark a
324 324 revision as good or bad without checking it out first.
325 325
326 326 If you supply a command, it will be used for automatic bisection.
327 327 Its exit status will be used to mark revisions as good or bad:
328 328 status 0 means good, 125 means to skip the revision, 127
329 329 (command not found) will abort the bisection, and any other
330 330 non-zero exit status means the revision is bad.
331 331
332 332 Returns 0 on success.
333 333 """
334 334 def extendbisectrange(nodes, good):
335 335 # bisect is incomplete when it ends on a merge node and
336 336 # one of the parent was not checked.
337 337 parents = repo[nodes[0]].parents()
338 338 if len(parents) > 1:
339 339 side = good and state['bad'] or state['good']
340 340 num = len(set(i.node() for i in parents) & set(side))
341 341 if num == 1:
342 342 return parents[0].ancestor(parents[1])
343 343 return None
344 344
345 345 def print_result(nodes, good):
346 346 displayer = cmdutil.show_changeset(ui, repo, {})
347 347 if len(nodes) == 1:
348 348 # narrowed it down to a single revision
349 349 if good:
350 350 ui.write(_("The first good revision is:\n"))
351 351 else:
352 352 ui.write(_("The first bad revision is:\n"))
353 353 displayer.show(repo[nodes[0]])
354 parents = repo[nodes[0]].parents()
355 354 extendnode = extendbisectrange(nodes, good)
356 355 if extendnode is not None:
357 356 ui.write(_('Not all ancestors of this changeset have been'
358 357 ' checked.\nUse bisect --extend to continue the '
359 358 'bisection from\nthe common ancestor, %s.\n')
360 % short(extendnode.node()))
359 % extendnode)
361 360 else:
362 361 # multiple possible revisions
363 362 if good:
364 363 ui.write(_("Due to skipped revisions, the first "
365 364 "good revision could be any of:\n"))
366 365 else:
367 366 ui.write(_("Due to skipped revisions, the first "
368 367 "bad revision could be any of:\n"))
369 368 for n in nodes:
370 369 displayer.show(repo[n])
371 370 displayer.close()
372 371
373 372 def check_state(state, interactive=True):
374 373 if not state['good'] or not state['bad']:
375 374 if (good or bad or skip or reset) and interactive:
376 375 return
377 376 if not state['good']:
378 377 raise util.Abort(_('cannot bisect (no known good revisions)'))
379 378 else:
380 379 raise util.Abort(_('cannot bisect (no known bad revisions)'))
381 380 return True
382 381
383 382 # backward compatibility
384 383 if rev in "good bad reset init".split():
385 384 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
386 385 cmd, rev, extra = rev, extra, None
387 386 if cmd == "good":
388 387 good = True
389 388 elif cmd == "bad":
390 389 bad = True
391 390 else:
392 391 reset = True
393 392 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
394 393 raise util.Abort(_('incompatible arguments'))
395 394
396 395 if reset:
397 396 p = repo.join("bisect.state")
398 397 if os.path.exists(p):
399 398 os.unlink(p)
400 399 return
401 400
402 401 state = hbisect.load_state(repo)
403 402
404 403 if command:
405 404 changesets = 1
406 405 try:
407 406 while changesets:
408 407 # update state
409 408 status = util.system(command)
410 409 if status == 125:
411 410 transition = "skip"
412 411 elif status == 0:
413 412 transition = "good"
414 413 # status < 0 means process was killed
415 414 elif status == 127:
416 415 raise util.Abort(_("failed to execute %s") % command)
417 416 elif status < 0:
418 417 raise util.Abort(_("%s killed") % command)
419 418 else:
420 419 transition = "bad"
421 420 ctx = cmdutil.revsingle(repo, rev)
422 421 rev = None # clear for future iterations
423 422 state[transition].append(ctx.node())
424 423 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
425 424 check_state(state, interactive=False)
426 425 # bisect
427 426 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
428 427 # update to next check
429 428 cmdutil.bail_if_changed(repo)
430 429 hg.clean(repo, nodes[0], show_stats=False)
431 430 finally:
432 431 hbisect.save_state(repo, state)
433 432 print_result(nodes, good)
434 433 return
435 434
436 435 # update state
437 436
438 437 if rev:
439 438 nodes = [repo.lookup(i) for i in cmdutil.revrange(repo, [rev])]
440 439 else:
441 440 nodes = [repo.lookup('.')]
442 441
443 442 if good or bad or skip:
444 443 if good:
445 444 state['good'] += nodes
446 445 elif bad:
447 446 state['bad'] += nodes
448 447 elif skip:
449 448 state['skip'] += nodes
450 449 hbisect.save_state(repo, state)
451 450
452 451 if not check_state(state):
453 452 return
454 453
455 454 # actually bisect
456 455 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
457 456 if extend:
458 457 if not changesets:
459 458 extendnode = extendbisectrange(nodes, good)
460 459 if extendnode is not None:
461 460 ui.write(_("Extending search to changeset %d:%s\n"
462 % (extendnode.rev(), short(extendnode.node()))))
461 % (extendnode.rev(), extendnode)))
463 462 if noupdate:
464 463 return
465 464 cmdutil.bail_if_changed(repo)
466 465 return hg.clean(repo, extendnode.node())
467 466 raise util.Abort(_("nothing to extend"))
468 467
469 468 if changesets == 0:
470 469 print_result(nodes, good)
471 470 else:
472 471 assert len(nodes) == 1 # only a single node can be tested next
473 472 node = nodes[0]
474 473 # compute the approximate number of remaining tests
475 474 tests, size = 0, 2
476 475 while size <= changesets:
477 476 tests, size = tests + 1, size * 2
478 477 rev = repo.changelog.rev(node)
479 478 ui.write(_("Testing changeset %d:%s "
480 479 "(%d changesets remaining, ~%d tests)\n")
481 480 % (rev, short(node), changesets, tests))
482 481 if not noupdate:
483 482 cmdutil.bail_if_changed(repo)
484 483 return hg.clean(repo, node)
485 484
486 485 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
487 486 '''track a line of development with movable markers
488 487
489 488 Bookmarks are pointers to certain commits that move when
490 489 committing. Bookmarks are local. They can be renamed, copied and
491 490 deleted. It is possible to use bookmark names in :hg:`merge` and
492 491 :hg:`update` to merge and update respectively to a given bookmark.
493 492
494 493 You can use :hg:`bookmark NAME` to set a bookmark on the working
495 494 directory's parent revision with the given name. If you specify
496 495 a revision using -r REV (where REV may be an existing bookmark),
497 496 the bookmark is assigned to that revision.
498 497
499 498 Bookmarks can be pushed and pulled between repositories (see :hg:`help
500 499 push` and :hg:`help pull`). This requires both the local and remote
501 500 repositories to support bookmarks. For versions prior to 1.8, this means
502 501 the bookmarks extension must be enabled.
503 502 '''
504 503 hexfn = ui.debugflag and hex or short
505 504 marks = repo._bookmarks
506 505 cur = repo.changectx('.').node()
507 506
508 507 if rename:
509 508 if rename not in marks:
510 509 raise util.Abort(_("bookmark '%s' does not exist") % rename)
511 510 if mark in marks and not force:
512 511 raise util.Abort(_("bookmark '%s' already exists "
513 512 "(use -f to force)") % mark)
514 513 if mark is None:
515 514 raise util.Abort(_("new bookmark name required"))
516 515 marks[mark] = marks[rename]
517 516 if repo._bookmarkcurrent == rename:
518 517 bookmarks.setcurrent(repo, mark)
519 518 del marks[rename]
520 519 bookmarks.write(repo)
521 520 return
522 521
523 522 if delete:
524 523 if mark is None:
525 524 raise util.Abort(_("bookmark name required"))
526 525 if mark not in marks:
527 526 raise util.Abort(_("bookmark '%s' does not exist") % mark)
528 527 if mark == repo._bookmarkcurrent:
529 528 bookmarks.setcurrent(repo, None)
530 529 del marks[mark]
531 530 bookmarks.write(repo)
532 531 return
533 532
534 533 if mark is not None:
535 534 if "\n" in mark:
536 535 raise util.Abort(_("bookmark name cannot contain newlines"))
537 536 mark = mark.strip()
538 537 if not mark:
539 538 raise util.Abort(_("bookmark names cannot consist entirely of "
540 539 "whitespace"))
541 540 if mark in marks and not force:
542 541 raise util.Abort(_("bookmark '%s' already exists "
543 542 "(use -f to force)") % mark)
544 543 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
545 544 and not force):
546 545 raise util.Abort(
547 546 _("a bookmark cannot have the name of an existing branch"))
548 547 if rev:
549 548 marks[mark] = repo.lookup(rev)
550 549 else:
551 550 marks[mark] = repo.changectx('.').node()
552 551 if repo.changectx('.').node() == marks[mark]:
553 552 bookmarks.setcurrent(repo, mark)
554 553 bookmarks.write(repo)
555 554 return
556 555
557 556 if mark is None:
558 557 if rev:
559 558 raise util.Abort(_("bookmark name required"))
560 559 if len(marks) == 0:
561 560 ui.status(_("no bookmarks set\n"))
562 561 else:
563 562 for bmark, n in sorted(marks.iteritems()):
564 563 current = repo._bookmarkcurrent
565 564 if bmark == current and n == cur:
566 565 prefix, label = '*', 'bookmarks.current'
567 566 else:
568 567 prefix, label = ' ', ''
569 568
570 569 if ui.quiet:
571 570 ui.write("%s\n" % bmark, label=label)
572 571 else:
573 572 ui.write(" %s %-25s %d:%s\n" % (
574 573 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
575 574 label=label)
576 575 return
577 576
578 577 def branch(ui, repo, label=None, **opts):
579 578 """set or show the current branch name
580 579
581 580 With no argument, show the current branch name. With one argument,
582 581 set the working directory branch name (the branch will not exist
583 582 in the repository until the next commit). Standard practice
584 583 recommends that primary development take place on the 'default'
585 584 branch.
586 585
587 586 Unless -f/--force is specified, branch will not let you set a
588 587 branch name that already exists, even if it's inactive.
589 588
590 589 Use -C/--clean to reset the working directory branch to that of
591 590 the parent of the working directory, negating a previous branch
592 591 change.
593 592
594 593 Use the command :hg:`update` to switch to an existing branch. Use
595 594 :hg:`commit --close-branch` to mark this branch as closed.
596 595
597 596 Returns 0 on success.
598 597 """
599 598
600 599 if opts.get('clean'):
601 600 label = repo[None].p1().branch()
602 601 repo.dirstate.setbranch(label)
603 602 ui.status(_('reset working directory to branch %s\n') % label)
604 603 elif label:
605 604 if not opts.get('force') and label in repo.branchtags():
606 605 if label not in [p.branch() for p in repo.parents()]:
607 606 raise util.Abort(_('a branch of the same name already exists'
608 607 " (use 'hg update' to switch to it)"))
609 608 repo.dirstate.setbranch(label)
610 609 ui.status(_('marked working directory as branch %s\n') % label)
611 610 else:
612 611 ui.write("%s\n" % repo.dirstate.branch())
613 612
614 613 def branches(ui, repo, active=False, closed=False):
615 614 """list repository named branches
616 615
617 616 List the repository's named branches, indicating which ones are
618 617 inactive. If -c/--closed is specified, also list branches which have
619 618 been marked closed (see :hg:`commit --close-branch`).
620 619
621 620 If -a/--active is specified, only show active branches. A branch
622 621 is considered active if it contains repository heads.
623 622
624 623 Use the command :hg:`update` to switch to an existing branch.
625 624
626 625 Returns 0.
627 626 """
628 627
629 628 hexfunc = ui.debugflag and hex or short
630 629 activebranches = [repo[n].branch() for n in repo.heads()]
631 630 def testactive(tag, node):
632 631 realhead = tag in activebranches
633 632 open = node in repo.branchheads(tag, closed=False)
634 633 return realhead and open
635 634 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
636 635 for tag, node in repo.branchtags().items()],
637 636 reverse=True)
638 637
639 638 for isactive, node, tag in branches:
640 639 if (not active) or isactive:
641 640 if ui.quiet:
642 641 ui.write("%s\n" % tag)
643 642 else:
644 643 hn = repo.lookup(node)
645 644 if isactive:
646 645 label = 'branches.active'
647 646 notice = ''
648 647 elif hn not in repo.branchheads(tag, closed=False):
649 648 if not closed:
650 649 continue
651 650 label = 'branches.closed'
652 651 notice = _(' (closed)')
653 652 else:
654 653 label = 'branches.inactive'
655 654 notice = _(' (inactive)')
656 655 if tag == repo.dirstate.branch():
657 656 label = 'branches.current'
658 657 rev = str(node).rjust(31 - encoding.colwidth(tag))
659 658 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
660 659 tag = ui.label(tag, label)
661 660 ui.write("%s %s%s\n" % (tag, rev, notice))
662 661
663 662 def bundle(ui, repo, fname, dest=None, **opts):
664 663 """create a changegroup file
665 664
666 665 Generate a compressed changegroup file collecting changesets not
667 666 known to be in another repository.
668 667
669 668 If you omit the destination repository, then hg assumes the
670 669 destination will have all the nodes you specify with --base
671 670 parameters. To create a bundle containing all changesets, use
672 671 -a/--all (or --base null).
673 672
674 673 You can change compression method with the -t/--type option.
675 674 The available compression methods are: none, bzip2, and
676 675 gzip (by default, bundles are compressed using bzip2).
677 676
678 677 The bundle file can then be transferred using conventional means
679 678 and applied to another repository with the unbundle or pull
680 679 command. This is useful when direct push and pull are not
681 680 available or when exporting an entire repository is undesirable.
682 681
683 682 Applying bundles preserves all changeset contents including
684 683 permissions, copy/rename information, and revision history.
685 684
686 685 Returns 0 on success, 1 if no changes found.
687 686 """
688 687 revs = None
689 688 if 'rev' in opts:
690 689 revs = cmdutil.revrange(repo, opts['rev'])
691 690
692 691 if opts.get('all'):
693 692 base = ['null']
694 693 else:
695 694 base = cmdutil.revrange(repo, opts.get('base'))
696 695 if base:
697 696 if dest:
698 697 raise util.Abort(_("--base is incompatible with specifying "
699 698 "a destination"))
700 699 base = [repo.lookup(rev) for rev in base]
701 700 # create the right base
702 701 # XXX: nodesbetween / changegroup* should be "fixed" instead
703 702 o = []
704 703 has = set((nullid,))
705 704 for n in base:
706 705 has.update(repo.changelog.reachable(n))
707 706 if revs:
708 707 revs = [repo.lookup(rev) for rev in revs]
709 708 visit = revs[:]
710 709 has.difference_update(visit)
711 710 else:
712 711 visit = repo.changelog.heads()
713 712 seen = {}
714 713 while visit:
715 714 n = visit.pop(0)
716 715 parents = [p for p in repo.changelog.parents(n) if p not in has]
717 716 if len(parents) == 0:
718 717 if n not in has:
719 718 o.append(n)
720 719 else:
721 720 for p in parents:
722 721 if p not in seen:
723 722 seen[p] = 1
724 723 visit.append(p)
725 724 else:
726 725 dest = ui.expandpath(dest or 'default-push', dest or 'default')
727 726 dest, branches = hg.parseurl(dest, opts.get('branch'))
728 727 other = hg.repository(hg.remoteui(repo, opts), dest)
729 728 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
730 729 if revs:
731 730 revs = [repo.lookup(rev) for rev in revs]
732 731 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
733 732
734 733 if not o:
735 734 ui.status(_("no changes found\n"))
736 735 return 1
737 736
738 737 if revs:
739 738 cg = repo.changegroupsubset(o, revs, 'bundle')
740 739 else:
741 740 cg = repo.changegroup(o, 'bundle')
742 741
743 742 bundletype = opts.get('type', 'bzip2').lower()
744 743 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
745 744 bundletype = btypes.get(bundletype)
746 745 if bundletype not in changegroup.bundletypes:
747 746 raise util.Abort(_('unknown bundle type specified with --type'))
748 747
749 748 changegroup.writebundle(cg, fname, bundletype)
750 749
751 750 def cat(ui, repo, file1, *pats, **opts):
752 751 """output the current or given revision of files
753 752
754 753 Print the specified files as they were at the given revision. If
755 754 no revision is given, the parent of the working directory is used,
756 755 or tip if no revision is checked out.
757 756
758 757 Output may be to a file, in which case the name of the file is
759 758 given using a format string. The formatting rules are the same as
760 759 for the export command, with the following additions:
761 760
762 761 :``%s``: basename of file being printed
763 762 :``%d``: dirname of file being printed, or '.' if in repository root
764 763 :``%p``: root-relative path name of file being printed
765 764
766 765 Returns 0 on success.
767 766 """
768 767 ctx = cmdutil.revsingle(repo, opts.get('rev'))
769 768 err = 1
770 769 m = cmdutil.match(repo, (file1,) + pats, opts)
771 770 for abs in ctx.walk(m):
772 771 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
773 772 data = ctx[abs].data()
774 773 if opts.get('decode'):
775 774 data = repo.wwritedata(abs, data)
776 775 fp.write(data)
777 776 fp.close()
778 777 err = 0
779 778 return err
780 779
781 780 def clone(ui, source, dest=None, **opts):
782 781 """make a copy of an existing repository
783 782
784 783 Create a copy of an existing repository in a new directory.
785 784
786 785 If no destination directory name is specified, it defaults to the
787 786 basename of the source.
788 787
789 788 The location of the source is added to the new repository's
790 789 ``.hg/hgrc`` file, as the default to be used for future pulls.
791 790
792 791 See :hg:`help urls` for valid source format details.
793 792
794 793 It is possible to specify an ``ssh://`` URL as the destination, but no
795 794 ``.hg/hgrc`` and working directory will be created on the remote side.
796 795 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
797 796
798 797 A set of changesets (tags, or branch names) to pull may be specified
799 798 by listing each changeset (tag, or branch name) with -r/--rev.
800 799 If -r/--rev is used, the cloned repository will contain only a subset
801 800 of the changesets of the source repository. Only the set of changesets
802 801 defined by all -r/--rev options (including all their ancestors)
803 802 will be pulled into the destination repository.
804 803 No subsequent changesets (including subsequent tags) will be present
805 804 in the destination.
806 805
807 806 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
808 807 local source repositories.
809 808
810 809 For efficiency, hardlinks are used for cloning whenever the source
811 810 and destination are on the same filesystem (note this applies only
812 811 to the repository data, not to the working directory). Some
813 812 filesystems, such as AFS, implement hardlinking incorrectly, but
814 813 do not report errors. In these cases, use the --pull option to
815 814 avoid hardlinking.
816 815
817 816 In some cases, you can clone repositories and the working directory
818 817 using full hardlinks with ::
819 818
820 819 $ cp -al REPO REPOCLONE
821 820
822 821 This is the fastest way to clone, but it is not always safe. The
823 822 operation is not atomic (making sure REPO is not modified during
824 823 the operation is up to you) and you have to make sure your editor
825 824 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
826 825 this is not compatible with certain extensions that place their
827 826 metadata under the .hg directory, such as mq.
828 827
829 828 Mercurial will update the working directory to the first applicable
830 829 revision from this list:
831 830
832 831 a) null if -U or the source repository has no changesets
833 832 b) if -u . and the source repository is local, the first parent of
834 833 the source repository's working directory
835 834 c) the changeset specified with -u (if a branch name, this means the
836 835 latest head of that branch)
837 836 d) the changeset specified with -r
838 837 e) the tipmost head specified with -b
839 838 f) the tipmost head specified with the url#branch source syntax
840 839 g) the tipmost head of the default branch
841 840 h) tip
842 841
843 842 Returns 0 on success.
844 843 """
845 844 if opts.get('noupdate') and opts.get('updaterev'):
846 845 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
847 846
848 847 r = hg.clone(hg.remoteui(ui, opts), source, dest,
849 848 pull=opts.get('pull'),
850 849 stream=opts.get('uncompressed'),
851 850 rev=opts.get('rev'),
852 851 update=opts.get('updaterev') or not opts.get('noupdate'),
853 852 branch=opts.get('branch'))
854 853
855 854 return r is None
856 855
857 856 def commit(ui, repo, *pats, **opts):
858 857 """commit the specified files or all outstanding changes
859 858
860 859 Commit changes to the given files into the repository. Unlike a
861 860 centralized SCM, this operation is a local operation. See
862 861 :hg:`push` for a way to actively distribute your changes.
863 862
864 863 If a list of files is omitted, all changes reported by :hg:`status`
865 864 will be committed.
866 865
867 866 If you are committing the result of a merge, do not provide any
868 867 filenames or -I/-X filters.
869 868
870 869 If no commit message is specified, Mercurial starts your
871 870 configured editor where you can enter a message. In case your
872 871 commit fails, you will find a backup of your message in
873 872 ``.hg/last-message.txt``.
874 873
875 874 See :hg:`help dates` for a list of formats valid for -d/--date.
876 875
877 876 Returns 0 on success, 1 if nothing changed.
878 877 """
879 878 extra = {}
880 879 if opts.get('close_branch'):
881 880 if repo['.'].node() not in repo.branchheads():
882 881 # The topo heads set is included in the branch heads set of the
883 882 # current branch, so it's sufficient to test branchheads
884 883 raise util.Abort(_('can only close branch heads'))
885 884 extra['close'] = 1
886 885 e = cmdutil.commiteditor
887 886 if opts.get('force_editor'):
888 887 e = cmdutil.commitforceeditor
889 888
890 889 def commitfunc(ui, repo, message, match, opts):
891 890 return repo.commit(message, opts.get('user'), opts.get('date'), match,
892 891 editor=e, extra=extra)
893 892
894 893 branch = repo[None].branch()
895 894 bheads = repo.branchheads(branch)
896 895
897 896 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
898 897 if not node:
899 898 stat = repo.status(match=cmdutil.match(repo, pats, opts))
900 899 if stat[3]:
901 900 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
902 901 % len(stat[3]))
903 902 else:
904 903 ui.status(_("nothing changed\n"))
905 904 return 1
906 905
907 906 ctx = repo[node]
908 907 parents = ctx.parents()
909 908
910 909 if bheads and not [x for x in parents
911 910 if x.node() in bheads and x.branch() == branch]:
912 911 ui.status(_('created new head\n'))
913 912 # The message is not printed for initial roots. For the other
914 913 # changesets, it is printed in the following situations:
915 914 #
916 915 # Par column: for the 2 parents with ...
917 916 # N: null or no parent
918 917 # B: parent is on another named branch
919 918 # C: parent is a regular non head changeset
920 919 # H: parent was a branch head of the current branch
921 920 # Msg column: whether we print "created new head" message
922 921 # In the following, it is assumed that there already exists some
923 922 # initial branch heads of the current branch, otherwise nothing is
924 923 # printed anyway.
925 924 #
926 925 # Par Msg Comment
927 926 # NN y additional topo root
928 927 #
929 928 # BN y additional branch root
930 929 # CN y additional topo head
931 930 # HN n usual case
932 931 #
933 932 # BB y weird additional branch root
934 933 # CB y branch merge
935 934 # HB n merge with named branch
936 935 #
937 936 # CC y additional head from merge
938 937 # CH n merge with a head
939 938 #
940 939 # HH n head merge: head count decreases
941 940
942 941 if not opts.get('close_branch'):
943 942 for r in parents:
944 943 if r.extra().get('close') and r.branch() == branch:
945 944 ui.status(_('reopening closed branch head %d\n') % r)
946 945
947 946 if ui.debugflag:
948 947 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
949 948 elif ui.verbose:
950 949 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
951 950
952 951 def copy(ui, repo, *pats, **opts):
953 952 """mark files as copied for the next commit
954 953
955 954 Mark dest as having copies of source files. If dest is a
956 955 directory, copies are put in that directory. If dest is a file,
957 956 the source must be a single file.
958 957
959 958 By default, this command copies the contents of files as they
960 959 exist in the working directory. If invoked with -A/--after, the
961 960 operation is recorded, but no copying is performed.
962 961
963 962 This command takes effect with the next commit. To undo a copy
964 963 before that, see :hg:`revert`.
965 964
966 965 Returns 0 on success, 1 if errors are encountered.
967 966 """
968 967 wlock = repo.wlock(False)
969 968 try:
970 969 return cmdutil.copy(ui, repo, pats, opts)
971 970 finally:
972 971 wlock.release()
973 972
974 973 def debugancestor(ui, repo, *args):
975 974 """find the ancestor revision of two revisions in a given index"""
976 975 if len(args) == 3:
977 976 index, rev1, rev2 = args
978 977 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
979 978 lookup = r.lookup
980 979 elif len(args) == 2:
981 980 if not repo:
982 981 raise util.Abort(_("there is no Mercurial repository here "
983 982 "(.hg not found)"))
984 983 rev1, rev2 = args
985 984 r = repo.changelog
986 985 lookup = repo.lookup
987 986 else:
988 987 raise util.Abort(_('either two or three arguments required'))
989 988 a = r.ancestor(lookup(rev1), lookup(rev2))
990 989 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
991 990
992 991 def debugbuilddag(ui, repo, text,
993 992 mergeable_file=False,
994 993 appended_file=False,
995 994 overwritten_file=False,
996 995 new_file=False):
997 996 """builds a repo with a given dag from scratch in the current empty repo
998 997
999 998 Elements:
1000 999
1001 1000 - "+n" is a linear run of n nodes based on the current default parent
1002 1001 - "." is a single node based on the current default parent
1003 1002 - "$" resets the default parent to null (implied at the start);
1004 1003 otherwise the default parent is always the last node created
1005 1004 - "<p" sets the default parent to the backref p
1006 1005 - "*p" is a fork at parent p, which is a backref
1007 1006 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1008 1007 - "/p2" is a merge of the preceding node and p2
1009 1008 - ":tag" defines a local tag for the preceding node
1010 1009 - "@branch" sets the named branch for subsequent nodes
1011 1010 - "!command" runs the command using your shell
1012 1011 - "!!my command\\n" is like "!", but to the end of the line
1013 1012 - "#...\\n" is a comment up to the end of the line
1014 1013
1015 1014 Whitespace between the above elements is ignored.
1016 1015
1017 1016 A backref is either
1018 1017
1019 1018 - a number n, which references the node curr-n, where curr is the current
1020 1019 node, or
1021 1020 - the name of a local tag you placed earlier using ":tag", or
1022 1021 - empty to denote the default parent.
1023 1022
1024 1023 All string valued-elements are either strictly alphanumeric, or must
1025 1024 be enclosed in double quotes ("..."), with "\\" as escape character.
1026 1025
1027 1026 Note that the --overwritten-file and --appended-file options imply the
1028 1027 use of "HGMERGE=internal:local" during DAG buildup.
1029 1028 """
1030 1029
1031 1030 if not (mergeable_file or appended_file or overwritten_file or new_file):
1032 1031 raise util.Abort(_('need at least one of -m, -a, -o, -n'))
1033 1032
1034 1033 if len(repo.changelog) > 0:
1035 1034 raise util.Abort(_('repository is not empty'))
1036 1035
1037 1036 if overwritten_file or appended_file:
1038 1037 # we don't want to fail in merges during buildup
1039 1038 os.environ['HGMERGE'] = 'internal:local'
1040 1039
1041 1040 def writefile(fname, text, fmode="wb"):
1042 1041 f = open(fname, fmode)
1043 1042 try:
1044 1043 f.write(text)
1045 1044 finally:
1046 1045 f.close()
1047 1046
1048 1047 if mergeable_file:
1049 1048 linesperrev = 2
1050 1049 # determine number of revs in DAG
1051 1050 n = 0
1052 1051 for type, data in dagparser.parsedag(text):
1053 1052 if type == 'n':
1054 1053 n += 1
1055 1054 # make a file with k lines per rev
1056 1055 writefile("mf", "\n".join(str(i) for i in xrange(0, n * linesperrev))
1057 1056 + "\n")
1058 1057
1059 1058 at = -1
1060 1059 atbranch = 'default'
1061 1060 for type, data in dagparser.parsedag(text):
1062 1061 if type == 'n':
1063 1062 ui.status('node %s\n' % str(data))
1064 1063 id, ps = data
1065 1064 p1 = ps[0]
1066 1065 if p1 != at:
1067 1066 update(ui, repo, node=str(p1), clean=True)
1068 1067 at = p1
1069 1068 if repo.dirstate.branch() != atbranch:
1070 1069 branch(ui, repo, atbranch, force=True)
1071 1070 if len(ps) > 1:
1072 1071 p2 = ps[1]
1073 1072 merge(ui, repo, node=p2)
1074 1073
1075 1074 if mergeable_file:
1076 1075 f = open("mf", "rb+")
1077 1076 try:
1078 1077 lines = f.read().split("\n")
1079 1078 lines[id * linesperrev] += " r%i" % id
1080 1079 f.seek(0)
1081 1080 f.write("\n".join(lines))
1082 1081 finally:
1083 1082 f.close()
1084 1083
1085 1084 if appended_file:
1086 1085 writefile("af", "r%i\n" % id, "ab")
1087 1086
1088 1087 if overwritten_file:
1089 1088 writefile("of", "r%i\n" % id)
1090 1089
1091 1090 if new_file:
1092 1091 writefile("nf%i" % id, "r%i\n" % id)
1093 1092
1094 1093 commit(ui, repo, addremove=True, message="r%i" % id, date=(id, 0))
1095 1094 at = id
1096 1095 elif type == 'l':
1097 1096 id, name = data
1098 1097 ui.status('tag %s\n' % name)
1099 1098 tag(ui, repo, name, local=True)
1100 1099 elif type == 'a':
1101 1100 ui.status('branch %s\n' % data)
1102 1101 atbranch = data
1103 1102 elif type in 'cC':
1104 1103 r = util.system(data, cwd=repo.root)
1105 1104 if r:
1106 1105 desc, r = util.explain_exit(r)
1107 1106 raise util.Abort(_('%s command %s') % (data, desc))
1108 1107
1109 1108 def debugcommands(ui, cmd='', *args):
1110 1109 """list all available commands and options"""
1111 1110 for cmd, vals in sorted(table.iteritems()):
1112 1111 cmd = cmd.split('|')[0].strip('^')
1113 1112 opts = ', '.join([i[1] for i in vals[1]])
1114 1113 ui.write('%s: %s\n' % (cmd, opts))
1115 1114
1116 1115 def debugcomplete(ui, cmd='', **opts):
1117 1116 """returns the completion list associated with the given command"""
1118 1117
1119 1118 if opts.get('options'):
1120 1119 options = []
1121 1120 otables = [globalopts]
1122 1121 if cmd:
1123 1122 aliases, entry = cmdutil.findcmd(cmd, table, False)
1124 1123 otables.append(entry[1])
1125 1124 for t in otables:
1126 1125 for o in t:
1127 1126 if "(DEPRECATED)" in o[3]:
1128 1127 continue
1129 1128 if o[0]:
1130 1129 options.append('-%s' % o[0])
1131 1130 options.append('--%s' % o[1])
1132 1131 ui.write("%s\n" % "\n".join(options))
1133 1132 return
1134 1133
1135 1134 cmdlist = cmdutil.findpossible(cmd, table)
1136 1135 if ui.verbose:
1137 1136 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1138 1137 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1139 1138
1140 1139 def debugfsinfo(ui, path = "."):
1141 1140 """show information detected about current filesystem"""
1142 1141 open('.debugfsinfo', 'w').write('')
1143 1142 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1144 1143 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1145 1144 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1146 1145 and 'yes' or 'no'))
1147 1146 os.unlink('.debugfsinfo')
1148 1147
1149 1148 def debugrebuildstate(ui, repo, rev="tip"):
1150 1149 """rebuild the dirstate as it would look like for the given revision"""
1151 1150 ctx = cmdutil.revsingle(repo, rev)
1152 1151 wlock = repo.wlock()
1153 1152 try:
1154 1153 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1155 1154 finally:
1156 1155 wlock.release()
1157 1156
1158 1157 def debugcheckstate(ui, repo):
1159 1158 """validate the correctness of the current dirstate"""
1160 1159 parent1, parent2 = repo.dirstate.parents()
1161 1160 m1 = repo[parent1].manifest()
1162 1161 m2 = repo[parent2].manifest()
1163 1162 errors = 0
1164 1163 for f in repo.dirstate:
1165 1164 state = repo.dirstate[f]
1166 1165 if state in "nr" and f not in m1:
1167 1166 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1168 1167 errors += 1
1169 1168 if state in "a" and f in m1:
1170 1169 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1171 1170 errors += 1
1172 1171 if state in "m" and f not in m1 and f not in m2:
1173 1172 ui.warn(_("%s in state %s, but not in either manifest\n") %
1174 1173 (f, state))
1175 1174 errors += 1
1176 1175 for f in m1:
1177 1176 state = repo.dirstate[f]
1178 1177 if state not in "nrm":
1179 1178 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1180 1179 errors += 1
1181 1180 if errors:
1182 1181 error = _(".hg/dirstate inconsistent with current parent's manifest")
1183 1182 raise util.Abort(error)
1184 1183
1185 1184 def showconfig(ui, repo, *values, **opts):
1186 1185 """show combined config settings from all hgrc files
1187 1186
1188 1187 With no arguments, print names and values of all config items.
1189 1188
1190 1189 With one argument of the form section.name, print just the value
1191 1190 of that config item.
1192 1191
1193 1192 With multiple arguments, print names and values of all config
1194 1193 items with matching section names.
1195 1194
1196 1195 With --debug, the source (filename and line number) is printed
1197 1196 for each config item.
1198 1197
1199 1198 Returns 0 on success.
1200 1199 """
1201 1200
1202 1201 for f in scmutil.rcpath():
1203 1202 ui.debug(_('read config from: %s\n') % f)
1204 1203 untrusted = bool(opts.get('untrusted'))
1205 1204 if values:
1206 1205 sections = [v for v in values if '.' not in v]
1207 1206 items = [v for v in values if '.' in v]
1208 1207 if len(items) > 1 or items and sections:
1209 1208 raise util.Abort(_('only one config item permitted'))
1210 1209 for section, name, value in ui.walkconfig(untrusted=untrusted):
1211 1210 value = str(value).replace('\n', '\\n')
1212 1211 sectname = section + '.' + name
1213 1212 if values:
1214 1213 for v in values:
1215 1214 if v == section:
1216 1215 ui.debug('%s: ' %
1217 1216 ui.configsource(section, name, untrusted))
1218 1217 ui.write('%s=%s\n' % (sectname, value))
1219 1218 elif v == sectname:
1220 1219 ui.debug('%s: ' %
1221 1220 ui.configsource(section, name, untrusted))
1222 1221 ui.write(value, '\n')
1223 1222 else:
1224 1223 ui.debug('%s: ' %
1225 1224 ui.configsource(section, name, untrusted))
1226 1225 ui.write('%s=%s\n' % (sectname, value))
1227 1226
1228 1227 def debugknown(ui, repopath, *ids, **opts):
1229 1228 """test whether node ids are known to a repo
1230 1229
1231 1230 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1232 1231 indicating unknown/known.
1233 1232 """
1234 1233 repo = hg.repository(ui, repopath)
1235 1234 if not repo.capable('known'):
1236 1235 raise util.Abort("known() not supported by target repository")
1237 1236 flags = repo.known([bin(s) for s in ids])
1238 1237 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1239 1238
1240 1239 def debugbundle(ui, bundlepath, all=None, **opts):
1241 1240 """lists the contents of a bundle"""
1242 1241 f = url.open(ui, bundlepath)
1243 1242 try:
1244 1243 gen = changegroup.readbundle(f, bundlepath)
1245 1244 if all:
1246 1245 ui.write("format: id, p1, p2, cset, len(delta)\n")
1247 1246
1248 1247 def showchunks(named):
1249 1248 ui.write("\n%s\n" % named)
1250 1249 while 1:
1251 1250 chunkdata = gen.parsechunk()
1252 1251 if not chunkdata:
1253 1252 break
1254 1253 node = chunkdata['node']
1255 1254 p1 = chunkdata['p1']
1256 1255 p2 = chunkdata['p2']
1257 1256 cs = chunkdata['cs']
1258 1257 delta = chunkdata['data']
1259 1258 ui.write("%s %s %s %s %s\n" %
1260 1259 (hex(node), hex(p1), hex(p2),
1261 1260 hex(cs), len(delta)))
1262 1261
1263 1262 showchunks("changelog")
1264 1263 showchunks("manifest")
1265 1264 while 1:
1266 1265 fname = gen.chunk()
1267 1266 if not fname:
1268 1267 break
1269 1268 showchunks(fname)
1270 1269 else:
1271 1270 while 1:
1272 1271 chunkdata = gen.parsechunk()
1273 1272 if not chunkdata:
1274 1273 break
1275 1274 node = chunkdata['node']
1276 1275 ui.write("%s\n" % hex(node))
1277 1276 finally:
1278 1277 f.close()
1279 1278
1280 1279 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1281 1280 """retrieves a bundle from a repo
1282 1281
1283 1282 Every ID must be a full-length hex node id string. Saves the bundle to the
1284 1283 given file.
1285 1284 """
1286 1285 repo = hg.repository(ui, repopath)
1287 1286 if not repo.capable('getbundle'):
1288 1287 raise util.Abort("getbundle() not supported by target repository")
1289 1288 args = {}
1290 1289 if common:
1291 1290 args['common'] = [bin(s) for s in common]
1292 1291 if head:
1293 1292 args['heads'] = [bin(s) for s in head]
1294 1293 bundle = repo.getbundle('debug', **args)
1295 1294
1296 1295 bundletype = opts.get('type', 'bzip2').lower()
1297 1296 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1298 1297 bundletype = btypes.get(bundletype)
1299 1298 if bundletype not in changegroup.bundletypes:
1300 1299 raise util.Abort(_('unknown bundle type specified with --type'))
1301 1300 changegroup.writebundle(bundle, bundlepath, bundletype)
1302 1301
1303 1302 def debugpushkey(ui, repopath, namespace, *keyinfo):
1304 1303 '''access the pushkey key/value protocol
1305 1304
1306 1305 With two args, list the keys in the given namespace.
1307 1306
1308 1307 With five args, set a key to new if it currently is set to old.
1309 1308 Reports success or failure.
1310 1309 '''
1311 1310
1312 1311 target = hg.repository(ui, repopath)
1313 1312 if keyinfo:
1314 1313 key, old, new = keyinfo
1315 1314 r = target.pushkey(namespace, key, old, new)
1316 1315 ui.status(str(r) + '\n')
1317 1316 return not r
1318 1317 else:
1319 1318 for k, v in target.listkeys(namespace).iteritems():
1320 1319 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1321 1320 v.encode('string-escape')))
1322 1321
1323 1322 def debugrevspec(ui, repo, expr):
1324 1323 '''parse and apply a revision specification'''
1325 1324 if ui.verbose:
1326 1325 tree = revset.parse(expr)[0]
1327 1326 ui.note(tree, "\n")
1328 1327 func = revset.match(expr)
1329 1328 for c in func(repo, range(len(repo))):
1330 1329 ui.write("%s\n" % c)
1331 1330
1332 1331 def debugsetparents(ui, repo, rev1, rev2=None):
1333 1332 """manually set the parents of the current working directory
1334 1333
1335 1334 This is useful for writing repository conversion tools, but should
1336 1335 be used with care.
1337 1336
1338 1337 Returns 0 on success.
1339 1338 """
1340 1339
1341 1340 r1 = cmdutil.revsingle(repo, rev1).node()
1342 1341 r2 = cmdutil.revsingle(repo, rev2, 'null').node()
1343 1342
1344 1343 wlock = repo.wlock()
1345 1344 try:
1346 1345 repo.dirstate.setparents(r1, r2)
1347 1346 finally:
1348 1347 wlock.release()
1349 1348
1350 1349 def debugstate(ui, repo, nodates=None, datesort=None):
1351 1350 """show the contents of the current dirstate"""
1352 1351 timestr = ""
1353 1352 showdate = not nodates
1354 1353 if datesort:
1355 1354 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
1356 1355 else:
1357 1356 keyfunc = None # sort by filename
1358 1357 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
1359 1358 if showdate:
1360 1359 if ent[3] == -1:
1361 1360 # Pad or slice to locale representation
1362 1361 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
1363 1362 time.localtime(0)))
1364 1363 timestr = 'unset'
1365 1364 timestr = (timestr[:locale_len] +
1366 1365 ' ' * (locale_len - len(timestr)))
1367 1366 else:
1368 1367 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
1369 1368 time.localtime(ent[3]))
1370 1369 if ent[1] & 020000:
1371 1370 mode = 'lnk'
1372 1371 else:
1373 1372 mode = '%3o' % (ent[1] & 0777)
1374 1373 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
1375 1374 for f in repo.dirstate.copies():
1376 1375 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1377 1376
1378 1377 def debugsub(ui, repo, rev=None):
1379 1378 ctx = cmdutil.revsingle(repo, rev, None)
1380 1379 for k, v in sorted(ctx.substate.items()):
1381 1380 ui.write('path %s\n' % k)
1382 1381 ui.write(' source %s\n' % v[0])
1383 1382 ui.write(' revision %s\n' % v[1])
1384 1383
1385 1384 def debugdag(ui, repo, file_=None, *revs, **opts):
1386 1385 """format the changelog or an index DAG as a concise textual description
1387 1386
1388 1387 If you pass a revlog index, the revlog's DAG is emitted. If you list
1389 1388 revision numbers, they get labelled in the output as rN.
1390 1389
1391 1390 Otherwise, the changelog DAG of the current repo is emitted.
1392 1391 """
1393 1392 spaces = opts.get('spaces')
1394 1393 dots = opts.get('dots')
1395 1394 if file_:
1396 1395 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1397 1396 revs = set((int(r) for r in revs))
1398 1397 def events():
1399 1398 for r in rlog:
1400 1399 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1401 1400 if r in revs:
1402 1401 yield 'l', (r, "r%i" % r)
1403 1402 elif repo:
1404 1403 cl = repo.changelog
1405 1404 tags = opts.get('tags')
1406 1405 branches = opts.get('branches')
1407 1406 if tags:
1408 1407 labels = {}
1409 1408 for l, n in repo.tags().items():
1410 1409 labels.setdefault(cl.rev(n), []).append(l)
1411 1410 def events():
1412 1411 b = "default"
1413 1412 for r in cl:
1414 1413 if branches:
1415 1414 newb = cl.read(cl.node(r))[5]['branch']
1416 1415 if newb != b:
1417 1416 yield 'a', newb
1418 1417 b = newb
1419 1418 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1420 1419 if tags:
1421 1420 ls = labels.get(r)
1422 1421 if ls:
1423 1422 for l in ls:
1424 1423 yield 'l', (r, l)
1425 1424 else:
1426 1425 raise util.Abort(_('need repo for changelog dag'))
1427 1426
1428 1427 for line in dagparser.dagtextlines(events(),
1429 1428 addspaces=spaces,
1430 1429 wraplabels=True,
1431 1430 wrapannotations=True,
1432 1431 wrapnonlinear=dots,
1433 1432 usedots=dots,
1434 1433 maxlinewidth=70):
1435 1434 ui.write(line)
1436 1435 ui.write("\n")
1437 1436
1438 1437 def debugdata(ui, repo, file_, rev):
1439 1438 """dump the contents of a data file revision"""
1440 1439 r = None
1441 1440 if repo:
1442 1441 filelog = repo.file(file_)
1443 1442 if len(filelog):
1444 1443 r = filelog
1445 1444 if not r:
1446 1445 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
1447 1446 file_[:-2] + ".i")
1448 1447 try:
1449 1448 ui.write(r.revision(r.lookup(rev)))
1450 1449 except KeyError:
1451 1450 raise util.Abort(_('invalid revision identifier %s') % rev)
1452 1451
1453 1452 def debugdate(ui, date, range=None, **opts):
1454 1453 """parse and display a date"""
1455 1454 if opts["extended"]:
1456 1455 d = util.parsedate(date, util.extendeddateformats)
1457 1456 else:
1458 1457 d = util.parsedate(date)
1459 1458 ui.write("internal: %s %s\n" % d)
1460 1459 ui.write("standard: %s\n" % util.datestr(d))
1461 1460 if range:
1462 1461 m = util.matchdate(range)
1463 1462 ui.write("match: %s\n" % m(d[0]))
1464 1463
1465 1464 def debugignore(ui, repo, *values, **opts):
1466 1465 """display the combined ignore pattern"""
1467 1466 ignore = repo.dirstate._ignore
1468 1467 if hasattr(ignore, 'includepat'):
1469 1468 ui.write("%s\n" % ignore.includepat)
1470 1469 else:
1471 1470 raise util.Abort(_("no ignore patterns found"))
1472 1471
1473 1472 def debugindex(ui, repo, file_, **opts):
1474 1473 """dump the contents of an index file"""
1475 1474 r = None
1476 1475 if repo:
1477 1476 filelog = repo.file(file_)
1478 1477 if len(filelog):
1479 1478 r = filelog
1480 1479
1481 1480 format = opts.get('format', 0)
1482 1481 if format not in (0, 1):
1483 1482 raise util.Abort(_("unknown format %d") % format)
1484 1483
1485 1484 if not r:
1486 1485 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1487 1486
1488 1487 if format == 0:
1489 1488 ui.write(" rev offset length base linkrev"
1490 1489 " nodeid p1 p2\n")
1491 1490 elif format == 1:
1492 1491 ui.write(" rev flag offset length"
1493 1492 " size base link p1 p2 nodeid\n")
1494 1493
1495 1494 for i in r:
1496 1495 node = r.node(i)
1497 1496 if format == 0:
1498 1497 try:
1499 1498 pp = r.parents(node)
1500 1499 except:
1501 1500 pp = [nullid, nullid]
1502 1501 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1503 1502 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1504 1503 short(node), short(pp[0]), short(pp[1])))
1505 1504 elif format == 1:
1506 1505 pr = r.parentrevs(i)
1507 1506 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1508 1507 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1509 1508 r.base(i), r.linkrev(i), pr[0], pr[1], short(node)))
1510 1509
1511 1510 def debugindexdot(ui, repo, file_):
1512 1511 """dump an index DAG as a graphviz dot file"""
1513 1512 r = None
1514 1513 if repo:
1515 1514 filelog = repo.file(file_)
1516 1515 if len(filelog):
1517 1516 r = filelog
1518 1517 if not r:
1519 1518 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1520 1519 ui.write("digraph G {\n")
1521 1520 for i in r:
1522 1521 node = r.node(i)
1523 1522 pp = r.parents(node)
1524 1523 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1525 1524 if pp[1] != nullid:
1526 1525 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1527 1526 ui.write("}\n")
1528 1527
1529 1528 def debuginstall(ui):
1530 1529 '''test Mercurial installation
1531 1530
1532 1531 Returns 0 on success.
1533 1532 '''
1534 1533
1535 1534 def writetemp(contents):
1536 1535 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1537 1536 f = os.fdopen(fd, "wb")
1538 1537 f.write(contents)
1539 1538 f.close()
1540 1539 return name
1541 1540
1542 1541 problems = 0
1543 1542
1544 1543 # encoding
1545 1544 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1546 1545 try:
1547 1546 encoding.fromlocal("test")
1548 1547 except util.Abort, inst:
1549 1548 ui.write(" %s\n" % inst)
1550 1549 ui.write(_(" (check that your locale is properly set)\n"))
1551 1550 problems += 1
1552 1551
1553 1552 # compiled modules
1554 1553 ui.status(_("Checking installed modules (%s)...\n")
1555 1554 % os.path.dirname(__file__))
1556 1555 try:
1557 1556 import bdiff, mpatch, base85, osutil
1558 1557 except Exception, inst:
1559 1558 ui.write(" %s\n" % inst)
1560 1559 ui.write(_(" One or more extensions could not be found"))
1561 1560 ui.write(_(" (check that you compiled the extensions)\n"))
1562 1561 problems += 1
1563 1562
1564 1563 # templates
1565 1564 ui.status(_("Checking templates...\n"))
1566 1565 try:
1567 1566 import templater
1568 1567 templater.templater(templater.templatepath("map-cmdline.default"))
1569 1568 except Exception, inst:
1570 1569 ui.write(" %s\n" % inst)
1571 1570 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1572 1571 problems += 1
1573 1572
1574 1573 # editor
1575 1574 ui.status(_("Checking commit editor...\n"))
1576 1575 editor = ui.geteditor()
1577 1576 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1578 1577 if not cmdpath:
1579 1578 if editor == 'vi':
1580 1579 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1581 1580 ui.write(_(" (specify a commit editor in your configuration"
1582 1581 " file)\n"))
1583 1582 else:
1584 1583 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1585 1584 ui.write(_(" (specify a commit editor in your configuration"
1586 1585 " file)\n"))
1587 1586 problems += 1
1588 1587
1589 1588 # check username
1590 1589 ui.status(_("Checking username...\n"))
1591 1590 try:
1592 1591 ui.username()
1593 1592 except util.Abort, e:
1594 1593 ui.write(" %s\n" % e)
1595 1594 ui.write(_(" (specify a username in your configuration file)\n"))
1596 1595 problems += 1
1597 1596
1598 1597 if not problems:
1599 1598 ui.status(_("No problems detected\n"))
1600 1599 else:
1601 1600 ui.write(_("%s problems detected,"
1602 1601 " please check your install!\n") % problems)
1603 1602
1604 1603 return problems
1605 1604
1606 1605 def debugrename(ui, repo, file1, *pats, **opts):
1607 1606 """dump rename information"""
1608 1607
1609 1608 ctx = cmdutil.revsingle(repo, opts.get('rev'))
1610 1609 m = cmdutil.match(repo, (file1,) + pats, opts)
1611 1610 for abs in ctx.walk(m):
1612 1611 fctx = ctx[abs]
1613 1612 o = fctx.filelog().renamed(fctx.filenode())
1614 1613 rel = m.rel(abs)
1615 1614 if o:
1616 1615 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1617 1616 else:
1618 1617 ui.write(_("%s not renamed\n") % rel)
1619 1618
1620 1619 def debugwalk(ui, repo, *pats, **opts):
1621 1620 """show how files match on given patterns"""
1622 1621 m = cmdutil.match(repo, pats, opts)
1623 1622 items = list(repo.walk(m))
1624 1623 if not items:
1625 1624 return
1626 1625 fmt = 'f %%-%ds %%-%ds %%s' % (
1627 1626 max([len(abs) for abs in items]),
1628 1627 max([len(m.rel(abs)) for abs in items]))
1629 1628 for abs in items:
1630 1629 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1631 1630 ui.write("%s\n" % line.rstrip())
1632 1631
1633 1632 def debugwireargs(ui, repopath, *vals, **opts):
1634 1633 repo = hg.repository(hg.remoteui(ui, opts), repopath)
1635 1634 for opt in remoteopts:
1636 1635 del opts[opt[1]]
1637 1636 args = {}
1638 1637 for k, v in opts.iteritems():
1639 1638 if v:
1640 1639 args[k] = v
1641 1640 # run twice to check that we don't mess up the stream for the next command
1642 1641 res1 = repo.debugwireargs(*vals, **args)
1643 1642 res2 = repo.debugwireargs(*vals, **args)
1644 1643 ui.write("%s\n" % res1)
1645 1644 if res1 != res2:
1646 1645 ui.warn("%s\n" % res2)
1647 1646
1648 1647 def diff(ui, repo, *pats, **opts):
1649 1648 """diff repository (or selected files)
1650 1649
1651 1650 Show differences between revisions for the specified files.
1652 1651
1653 1652 Differences between files are shown using the unified diff format.
1654 1653
1655 1654 .. note::
1656 1655 diff may generate unexpected results for merges, as it will
1657 1656 default to comparing against the working directory's first
1658 1657 parent changeset if no revisions are specified.
1659 1658
1660 1659 When two revision arguments are given, then changes are shown
1661 1660 between those revisions. If only one revision is specified then
1662 1661 that revision is compared to the working directory, and, when no
1663 1662 revisions are specified, the working directory files are compared
1664 1663 to its parent.
1665 1664
1666 1665 Alternatively you can specify -c/--change with a revision to see
1667 1666 the changes in that changeset relative to its first parent.
1668 1667
1669 1668 Without the -a/--text option, diff will avoid generating diffs of
1670 1669 files it detects as binary. With -a, diff will generate a diff
1671 1670 anyway, probably with undesirable results.
1672 1671
1673 1672 Use the -g/--git option to generate diffs in the git extended diff
1674 1673 format. For more information, read :hg:`help diffs`.
1675 1674
1676 1675 Returns 0 on success.
1677 1676 """
1678 1677
1679 1678 revs = opts.get('rev')
1680 1679 change = opts.get('change')
1681 1680 stat = opts.get('stat')
1682 1681 reverse = opts.get('reverse')
1683 1682
1684 1683 if revs and change:
1685 1684 msg = _('cannot specify --rev and --change at the same time')
1686 1685 raise util.Abort(msg)
1687 1686 elif change:
1688 1687 node2 = cmdutil.revsingle(repo, change, None).node()
1689 1688 node1 = repo[node2].p1().node()
1690 1689 else:
1691 1690 node1, node2 = cmdutil.revpair(repo, revs)
1692 1691
1693 1692 if reverse:
1694 1693 node1, node2 = node2, node1
1695 1694
1696 1695 diffopts = patch.diffopts(ui, opts)
1697 1696 m = cmdutil.match(repo, pats, opts)
1698 1697 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1699 1698 listsubrepos=opts.get('subrepos'))
1700 1699
1701 1700 def export(ui, repo, *changesets, **opts):
1702 1701 """dump the header and diffs for one or more changesets
1703 1702
1704 1703 Print the changeset header and diffs for one or more revisions.
1705 1704
1706 1705 The information shown in the changeset header is: author, date,
1707 1706 branch name (if non-default), changeset hash, parent(s) and commit
1708 1707 comment.
1709 1708
1710 1709 .. note::
1711 1710 export may generate unexpected diff output for merge
1712 1711 changesets, as it will compare the merge changeset against its
1713 1712 first parent only.
1714 1713
1715 1714 Output may be to a file, in which case the name of the file is
1716 1715 given using a format string. The formatting rules are as follows:
1717 1716
1718 1717 :``%%``: literal "%" character
1719 1718 :``%H``: changeset hash (40 hexadecimal digits)
1720 1719 :``%N``: number of patches being generated
1721 1720 :``%R``: changeset revision number
1722 1721 :``%b``: basename of the exporting repository
1723 1722 :``%h``: short-form changeset hash (12 hexadecimal digits)
1724 1723 :``%n``: zero-padded sequence number, starting at 1
1725 1724 :``%r``: zero-padded changeset revision number
1726 1725
1727 1726 Without the -a/--text option, export will avoid generating diffs
1728 1727 of files it detects as binary. With -a, export will generate a
1729 1728 diff anyway, probably with undesirable results.
1730 1729
1731 1730 Use the -g/--git option to generate diffs in the git extended diff
1732 1731 format. See :hg:`help diffs` for more information.
1733 1732
1734 1733 With the --switch-parent option, the diff will be against the
1735 1734 second parent. It can be useful to review a merge.
1736 1735
1737 1736 Returns 0 on success.
1738 1737 """
1739 1738 changesets += tuple(opts.get('rev', []))
1740 1739 if not changesets:
1741 1740 raise util.Abort(_("export requires at least one changeset"))
1742 1741 revs = cmdutil.revrange(repo, changesets)
1743 1742 if len(revs) > 1:
1744 1743 ui.note(_('exporting patches:\n'))
1745 1744 else:
1746 1745 ui.note(_('exporting patch:\n'))
1747 1746 cmdutil.export(repo, revs, template=opts.get('output'),
1748 1747 switch_parent=opts.get('switch_parent'),
1749 1748 opts=patch.diffopts(ui, opts))
1750 1749
1751 1750 def forget(ui, repo, *pats, **opts):
1752 1751 """forget the specified files on the next commit
1753 1752
1754 1753 Mark the specified files so they will no longer be tracked
1755 1754 after the next commit.
1756 1755
1757 1756 This only removes files from the current branch, not from the
1758 1757 entire project history, and it does not delete them from the
1759 1758 working directory.
1760 1759
1761 1760 To undo a forget before the next commit, see :hg:`add`.
1762 1761
1763 1762 Returns 0 on success.
1764 1763 """
1765 1764
1766 1765 if not pats:
1767 1766 raise util.Abort(_('no files specified'))
1768 1767
1769 1768 m = cmdutil.match(repo, pats, opts)
1770 1769 s = repo.status(match=m, clean=True)
1771 1770 forget = sorted(s[0] + s[1] + s[3] + s[6])
1772 1771 errs = 0
1773 1772
1774 1773 for f in m.files():
1775 1774 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1776 1775 ui.warn(_('not removing %s: file is already untracked\n')
1777 1776 % m.rel(f))
1778 1777 errs = 1
1779 1778
1780 1779 for f in forget:
1781 1780 if ui.verbose or not m.exact(f):
1782 1781 ui.status(_('removing %s\n') % m.rel(f))
1783 1782
1784 1783 repo[None].remove(forget, unlink=False)
1785 1784 return errs
1786 1785
1787 1786 def grep(ui, repo, pattern, *pats, **opts):
1788 1787 """search for a pattern in specified files and revisions
1789 1788
1790 1789 Search revisions of files for a regular expression.
1791 1790
1792 1791 This command behaves differently than Unix grep. It only accepts
1793 1792 Python/Perl regexps. It searches repository history, not the
1794 1793 working directory. It always prints the revision number in which a
1795 1794 match appears.
1796 1795
1797 1796 By default, grep only prints output for the first revision of a
1798 1797 file in which it finds a match. To get it to print every revision
1799 1798 that contains a change in match status ("-" for a match that
1800 1799 becomes a non-match, or "+" for a non-match that becomes a match),
1801 1800 use the --all flag.
1802 1801
1803 1802 Returns 0 if a match is found, 1 otherwise.
1804 1803 """
1805 1804 reflags = 0
1806 1805 if opts.get('ignore_case'):
1807 1806 reflags |= re.I
1808 1807 try:
1809 1808 regexp = re.compile(pattern, reflags)
1810 1809 except re.error, inst:
1811 1810 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1812 1811 return 1
1813 1812 sep, eol = ':', '\n'
1814 1813 if opts.get('print0'):
1815 1814 sep = eol = '\0'
1816 1815
1817 1816 getfile = util.lrucachefunc(repo.file)
1818 1817
1819 1818 def matchlines(body):
1820 1819 begin = 0
1821 1820 linenum = 0
1822 1821 while True:
1823 1822 match = regexp.search(body, begin)
1824 1823 if not match:
1825 1824 break
1826 1825 mstart, mend = match.span()
1827 1826 linenum += body.count('\n', begin, mstart) + 1
1828 1827 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1829 1828 begin = body.find('\n', mend) + 1 or len(body)
1830 1829 lend = begin - 1
1831 1830 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1832 1831
1833 1832 class linestate(object):
1834 1833 def __init__(self, line, linenum, colstart, colend):
1835 1834 self.line = line
1836 1835 self.linenum = linenum
1837 1836 self.colstart = colstart
1838 1837 self.colend = colend
1839 1838
1840 1839 def __hash__(self):
1841 1840 return hash((self.linenum, self.line))
1842 1841
1843 1842 def __eq__(self, other):
1844 1843 return self.line == other.line
1845 1844
1846 1845 matches = {}
1847 1846 copies = {}
1848 1847 def grepbody(fn, rev, body):
1849 1848 matches[rev].setdefault(fn, [])
1850 1849 m = matches[rev][fn]
1851 1850 for lnum, cstart, cend, line in matchlines(body):
1852 1851 s = linestate(line, lnum, cstart, cend)
1853 1852 m.append(s)
1854 1853
1855 1854 def difflinestates(a, b):
1856 1855 sm = difflib.SequenceMatcher(None, a, b)
1857 1856 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1858 1857 if tag == 'insert':
1859 1858 for i in xrange(blo, bhi):
1860 1859 yield ('+', b[i])
1861 1860 elif tag == 'delete':
1862 1861 for i in xrange(alo, ahi):
1863 1862 yield ('-', a[i])
1864 1863 elif tag == 'replace':
1865 1864 for i in xrange(alo, ahi):
1866 1865 yield ('-', a[i])
1867 1866 for i in xrange(blo, bhi):
1868 1867 yield ('+', b[i])
1869 1868
1870 1869 def display(fn, ctx, pstates, states):
1871 1870 rev = ctx.rev()
1872 1871 datefunc = ui.quiet and util.shortdate or util.datestr
1873 1872 found = False
1874 1873 filerevmatches = {}
1875 1874 def binary():
1876 1875 flog = getfile(fn)
1877 1876 return util.binary(flog.read(ctx.filenode(fn)))
1878 1877
1879 1878 if opts.get('all'):
1880 1879 iter = difflinestates(pstates, states)
1881 1880 else:
1882 1881 iter = [('', l) for l in states]
1883 1882 for change, l in iter:
1884 1883 cols = [fn, str(rev)]
1885 1884 before, match, after = None, None, None
1886 1885 if opts.get('line_number'):
1887 1886 cols.append(str(l.linenum))
1888 1887 if opts.get('all'):
1889 1888 cols.append(change)
1890 1889 if opts.get('user'):
1891 1890 cols.append(ui.shortuser(ctx.user()))
1892 1891 if opts.get('date'):
1893 1892 cols.append(datefunc(ctx.date()))
1894 1893 if opts.get('files_with_matches'):
1895 1894 c = (fn, rev)
1896 1895 if c in filerevmatches:
1897 1896 continue
1898 1897 filerevmatches[c] = 1
1899 1898 else:
1900 1899 before = l.line[:l.colstart]
1901 1900 match = l.line[l.colstart:l.colend]
1902 1901 after = l.line[l.colend:]
1903 1902 ui.write(sep.join(cols))
1904 1903 if before is not None:
1905 1904 if not opts.get('text') and binary():
1906 1905 ui.write(sep + " Binary file matches")
1907 1906 else:
1908 1907 ui.write(sep + before)
1909 1908 ui.write(match, label='grep.match')
1910 1909 ui.write(after)
1911 1910 ui.write(eol)
1912 1911 found = True
1913 1912 return found
1914 1913
1915 1914 skip = {}
1916 1915 revfiles = {}
1917 1916 matchfn = cmdutil.match(repo, pats, opts)
1918 1917 found = False
1919 1918 follow = opts.get('follow')
1920 1919
1921 1920 def prep(ctx, fns):
1922 1921 rev = ctx.rev()
1923 1922 pctx = ctx.p1()
1924 1923 parent = pctx.rev()
1925 1924 matches.setdefault(rev, {})
1926 1925 matches.setdefault(parent, {})
1927 1926 files = revfiles.setdefault(rev, [])
1928 1927 for fn in fns:
1929 1928 flog = getfile(fn)
1930 1929 try:
1931 1930 fnode = ctx.filenode(fn)
1932 1931 except error.LookupError:
1933 1932 continue
1934 1933
1935 1934 copied = flog.renamed(fnode)
1936 1935 copy = follow and copied and copied[0]
1937 1936 if copy:
1938 1937 copies.setdefault(rev, {})[fn] = copy
1939 1938 if fn in skip:
1940 1939 if copy:
1941 1940 skip[copy] = True
1942 1941 continue
1943 1942 files.append(fn)
1944 1943
1945 1944 if fn not in matches[rev]:
1946 1945 grepbody(fn, rev, flog.read(fnode))
1947 1946
1948 1947 pfn = copy or fn
1949 1948 if pfn not in matches[parent]:
1950 1949 try:
1951 1950 fnode = pctx.filenode(pfn)
1952 1951 grepbody(pfn, parent, flog.read(fnode))
1953 1952 except error.LookupError:
1954 1953 pass
1955 1954
1956 1955 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1957 1956 rev = ctx.rev()
1958 1957 parent = ctx.p1().rev()
1959 1958 for fn in sorted(revfiles.get(rev, [])):
1960 1959 states = matches[rev][fn]
1961 1960 copy = copies.get(rev, {}).get(fn)
1962 1961 if fn in skip:
1963 1962 if copy:
1964 1963 skip[copy] = True
1965 1964 continue
1966 1965 pstates = matches.get(parent, {}).get(copy or fn, [])
1967 1966 if pstates or states:
1968 1967 r = display(fn, ctx, pstates, states)
1969 1968 found = found or r
1970 1969 if r and not opts.get('all'):
1971 1970 skip[fn] = True
1972 1971 if copy:
1973 1972 skip[copy] = True
1974 1973 del matches[rev]
1975 1974 del revfiles[rev]
1976 1975
1977 1976 return not found
1978 1977
1979 1978 def heads(ui, repo, *branchrevs, **opts):
1980 1979 """show current repository heads or show branch heads
1981 1980
1982 1981 With no arguments, show all repository branch heads.
1983 1982
1984 1983 Repository "heads" are changesets with no child changesets. They are
1985 1984 where development generally takes place and are the usual targets
1986 1985 for update and merge operations. Branch heads are changesets that have
1987 1986 no child changeset on the same branch.
1988 1987
1989 1988 If one or more REVs are given, only branch heads on the branches
1990 1989 associated with the specified changesets are shown.
1991 1990
1992 1991 If -c/--closed is specified, also show branch heads marked closed
1993 1992 (see :hg:`commit --close-branch`).
1994 1993
1995 1994 If STARTREV is specified, only those heads that are descendants of
1996 1995 STARTREV will be displayed.
1997 1996
1998 1997 If -t/--topo is specified, named branch mechanics will be ignored and only
1999 1998 changesets without children will be shown.
2000 1999
2001 2000 Returns 0 if matching heads are found, 1 if not.
2002 2001 """
2003 2002
2004 2003 start = None
2005 2004 if 'rev' in opts:
2006 2005 start = cmdutil.revsingle(repo, opts['rev'], None).node()
2007 2006
2008 2007 if opts.get('topo'):
2009 2008 heads = [repo[h] for h in repo.heads(start)]
2010 2009 else:
2011 2010 heads = []
2012 2011 for b, ls in repo.branchmap().iteritems():
2013 2012 if start is None:
2014 2013 heads += [repo[h] for h in ls]
2015 2014 continue
2016 2015 startrev = repo.changelog.rev(start)
2017 2016 descendants = set(repo.changelog.descendants(startrev))
2018 2017 descendants.add(startrev)
2019 2018 rev = repo.changelog.rev
2020 2019 heads += [repo[h] for h in ls if rev(h) in descendants]
2021 2020
2022 2021 if branchrevs:
2023 2022 branches = set(repo[br].branch() for br in branchrevs)
2024 2023 heads = [h for h in heads if h.branch() in branches]
2025 2024
2026 2025 if not opts.get('closed'):
2027 2026 heads = [h for h in heads if not h.extra().get('close')]
2028 2027
2029 2028 if opts.get('active') and branchrevs:
2030 2029 dagheads = repo.heads(start)
2031 2030 heads = [h for h in heads if h.node() in dagheads]
2032 2031
2033 2032 if branchrevs:
2034 2033 haveheads = set(h.branch() for h in heads)
2035 2034 if branches - haveheads:
2036 2035 headless = ', '.join(b for b in branches - haveheads)
2037 2036 msg = _('no open branch heads found on branches %s')
2038 2037 if opts.get('rev'):
2039 2038 msg += _(' (started at %s)' % opts['rev'])
2040 2039 ui.warn((msg + '\n') % headless)
2041 2040
2042 2041 if not heads:
2043 2042 return 1
2044 2043
2045 2044 heads = sorted(heads, key=lambda x: -x.rev())
2046 2045 displayer = cmdutil.show_changeset(ui, repo, opts)
2047 2046 for ctx in heads:
2048 2047 displayer.show(ctx)
2049 2048 displayer.close()
2050 2049
2051 2050 def help_(ui, name=None, with_version=False, unknowncmd=False, full=True):
2052 2051 """show help for a given topic or a help overview
2053 2052
2054 2053 With no arguments, print a list of commands with short help messages.
2055 2054
2056 2055 Given a topic, extension, or command name, print help for that
2057 2056 topic.
2058 2057
2059 2058 Returns 0 if successful.
2060 2059 """
2061 2060 option_lists = []
2062 2061 textwidth = min(ui.termwidth(), 80) - 2
2063 2062
2064 2063 def addglobalopts(aliases):
2065 2064 if ui.verbose:
2066 2065 option_lists.append((_("global options:"), globalopts))
2067 2066 if name == 'shortlist':
2068 2067 option_lists.append((_('use "hg help" for the full list '
2069 2068 'of commands'), ()))
2070 2069 else:
2071 2070 if name == 'shortlist':
2072 2071 msg = _('use "hg help" for the full list of commands '
2073 2072 'or "hg -v" for details')
2074 2073 elif name and not full:
2075 2074 msg = _('use "hg help %s" to show the full help text' % name)
2076 2075 elif aliases:
2077 2076 msg = _('use "hg -v help%s" to show builtin aliases and '
2078 2077 'global options') % (name and " " + name or "")
2079 2078 else:
2080 2079 msg = _('use "hg -v help %s" to show global options') % name
2081 2080 option_lists.append((msg, ()))
2082 2081
2083 2082 def helpcmd(name):
2084 2083 if with_version:
2085 2084 version_(ui)
2086 2085 ui.write('\n')
2087 2086
2088 2087 try:
2089 2088 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
2090 2089 except error.AmbiguousCommand, inst:
2091 2090 # py3k fix: except vars can't be used outside the scope of the
2092 2091 # except block, nor can be used inside a lambda. python issue4617
2093 2092 prefix = inst.args[0]
2094 2093 select = lambda c: c.lstrip('^').startswith(prefix)
2095 2094 helplist(_('list of commands:\n\n'), select)
2096 2095 return
2097 2096
2098 2097 # check if it's an invalid alias and display its error if it is
2099 2098 if getattr(entry[0], 'badalias', False):
2100 2099 if not unknowncmd:
2101 2100 entry[0](ui)
2102 2101 return
2103 2102
2104 2103 # synopsis
2105 2104 if len(entry) > 2:
2106 2105 if entry[2].startswith('hg'):
2107 2106 ui.write("%s\n" % entry[2])
2108 2107 else:
2109 2108 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
2110 2109 else:
2111 2110 ui.write('hg %s\n' % aliases[0])
2112 2111
2113 2112 # aliases
2114 2113 if full and not ui.quiet and len(aliases) > 1:
2115 2114 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
2116 2115
2117 2116 # description
2118 2117 doc = gettext(entry[0].__doc__)
2119 2118 if not doc:
2120 2119 doc = _("(no help text available)")
2121 2120 if hasattr(entry[0], 'definition'): # aliased command
2122 2121 if entry[0].definition.startswith('!'): # shell alias
2123 2122 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
2124 2123 else:
2125 2124 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
2126 2125 if ui.quiet or not full:
2127 2126 doc = doc.splitlines()[0]
2128 2127 keep = ui.verbose and ['verbose'] or []
2129 2128 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
2130 2129 ui.write("\n%s\n" % formatted)
2131 2130 if pruned:
2132 2131 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
2133 2132
2134 2133 if not ui.quiet:
2135 2134 # options
2136 2135 if entry[1]:
2137 2136 option_lists.append((_("options:\n"), entry[1]))
2138 2137
2139 2138 addglobalopts(False)
2140 2139
2141 2140 def helplist(header, select=None):
2142 2141 h = {}
2143 2142 cmds = {}
2144 2143 for c, e in table.iteritems():
2145 2144 f = c.split("|", 1)[0]
2146 2145 if select and not select(f):
2147 2146 continue
2148 2147 if (not select and name != 'shortlist' and
2149 2148 e[0].__module__ != __name__):
2150 2149 continue
2151 2150 if name == "shortlist" and not f.startswith("^"):
2152 2151 continue
2153 2152 f = f.lstrip("^")
2154 2153 if not ui.debugflag and f.startswith("debug"):
2155 2154 continue
2156 2155 doc = e[0].__doc__
2157 2156 if doc and 'DEPRECATED' in doc and not ui.verbose:
2158 2157 continue
2159 2158 doc = gettext(doc)
2160 2159 if not doc:
2161 2160 doc = _("(no help text available)")
2162 2161 h[f] = doc.splitlines()[0].rstrip()
2163 2162 cmds[f] = c.lstrip("^")
2164 2163
2165 2164 if not h:
2166 2165 ui.status(_('no commands defined\n'))
2167 2166 return
2168 2167
2169 2168 ui.status(header)
2170 2169 fns = sorted(h)
2171 2170 m = max(map(len, fns))
2172 2171 for f in fns:
2173 2172 if ui.verbose:
2174 2173 commands = cmds[f].replace("|",", ")
2175 2174 ui.write(" %s:\n %s\n"%(commands, h[f]))
2176 2175 else:
2177 2176 ui.write('%s\n' % (util.wrap(h[f], textwidth,
2178 2177 initindent=' %-*s ' % (m, f),
2179 2178 hangindent=' ' * (m + 4))))
2180 2179
2181 2180 if not ui.quiet:
2182 2181 addglobalopts(True)
2183 2182
2184 2183 def helptopic(name):
2185 2184 for names, header, doc in help.helptable:
2186 2185 if name in names:
2187 2186 break
2188 2187 else:
2189 2188 raise error.UnknownCommand(name)
2190 2189
2191 2190 # description
2192 2191 if not doc:
2193 2192 doc = _("(no help text available)")
2194 2193 if hasattr(doc, '__call__'):
2195 2194 doc = doc()
2196 2195
2197 2196 ui.write("%s\n\n" % header)
2198 2197 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
2199 2198
2200 2199 def helpext(name):
2201 2200 try:
2202 2201 mod = extensions.find(name)
2203 2202 doc = gettext(mod.__doc__) or _('no help text available')
2204 2203 except KeyError:
2205 2204 mod = None
2206 2205 doc = extensions.disabledext(name)
2207 2206 if not doc:
2208 2207 raise error.UnknownCommand(name)
2209 2208
2210 2209 if '\n' not in doc:
2211 2210 head, tail = doc, ""
2212 2211 else:
2213 2212 head, tail = doc.split('\n', 1)
2214 2213 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
2215 2214 if tail:
2216 2215 ui.write(minirst.format(tail, textwidth))
2217 2216 ui.status('\n\n')
2218 2217
2219 2218 if mod:
2220 2219 try:
2221 2220 ct = mod.cmdtable
2222 2221 except AttributeError:
2223 2222 ct = {}
2224 2223 modcmds = set([c.split('|', 1)[0] for c in ct])
2225 2224 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2226 2225 else:
2227 2226 ui.write(_('use "hg help extensions" for information on enabling '
2228 2227 'extensions\n'))
2229 2228
2230 2229 def helpextcmd(name):
2231 2230 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
2232 2231 doc = gettext(mod.__doc__).splitlines()[0]
2233 2232
2234 2233 msg = help.listexts(_("'%s' is provided by the following "
2235 2234 "extension:") % cmd, {ext: doc}, len(ext),
2236 2235 indent=4)
2237 2236 ui.write(minirst.format(msg, textwidth))
2238 2237 ui.write('\n\n')
2239 2238 ui.write(_('use "hg help extensions" for information on enabling '
2240 2239 'extensions\n'))
2241 2240
2242 2241 help.addtopichook('revsets', revset.makedoc)
2243 2242 help.addtopichook('templates', templatekw.makedoc)
2244 2243 help.addtopichook('templates', templatefilters.makedoc)
2245 2244
2246 2245 if name and name != 'shortlist':
2247 2246 i = None
2248 2247 if unknowncmd:
2249 2248 queries = (helpextcmd,)
2250 2249 else:
2251 2250 queries = (helptopic, helpcmd, helpext, helpextcmd)
2252 2251 for f in queries:
2253 2252 try:
2254 2253 f(name)
2255 2254 i = None
2256 2255 break
2257 2256 except error.UnknownCommand, inst:
2258 2257 i = inst
2259 2258 if i:
2260 2259 raise i
2261 2260
2262 2261 else:
2263 2262 # program name
2264 2263 if ui.verbose or with_version:
2265 2264 version_(ui)
2266 2265 else:
2267 2266 ui.status(_("Mercurial Distributed SCM\n"))
2268 2267 ui.status('\n')
2269 2268
2270 2269 # list of commands
2271 2270 if name == "shortlist":
2272 2271 header = _('basic commands:\n\n')
2273 2272 else:
2274 2273 header = _('list of commands:\n\n')
2275 2274
2276 2275 helplist(header)
2277 2276 if name != 'shortlist':
2278 2277 exts, maxlength = extensions.enabled()
2279 2278 text = help.listexts(_('enabled extensions:'), exts, maxlength)
2280 2279 if text:
2281 2280 ui.write("\n%s\n" % minirst.format(text, textwidth))
2282 2281
2283 2282 # list all option lists
2284 2283 opt_output = []
2285 2284 multioccur = False
2286 2285 for title, options in option_lists:
2287 2286 opt_output.append(("\n%s" % title, None))
2288 2287 for option in options:
2289 2288 if len(option) == 5:
2290 2289 shortopt, longopt, default, desc, optlabel = option
2291 2290 else:
2292 2291 shortopt, longopt, default, desc = option
2293 2292 optlabel = _("VALUE") # default label
2294 2293
2295 2294 if _("DEPRECATED") in desc and not ui.verbose:
2296 2295 continue
2297 2296 if isinstance(default, list):
2298 2297 numqualifier = " %s [+]" % optlabel
2299 2298 multioccur = True
2300 2299 elif (default is not None) and not isinstance(default, bool):
2301 2300 numqualifier = " %s" % optlabel
2302 2301 else:
2303 2302 numqualifier = ""
2304 2303 opt_output.append(("%2s%s" %
2305 2304 (shortopt and "-%s" % shortopt,
2306 2305 longopt and " --%s%s" %
2307 2306 (longopt, numqualifier)),
2308 2307 "%s%s" % (desc,
2309 2308 default
2310 2309 and _(" (default: %s)") % default
2311 2310 or "")))
2312 2311 if multioccur:
2313 2312 msg = _("\n[+] marked option can be specified multiple times")
2314 2313 if ui.verbose and name != 'shortlist':
2315 2314 opt_output.append((msg, None))
2316 2315 else:
2317 2316 opt_output.insert(-1, (msg, None))
2318 2317
2319 2318 if not name:
2320 2319 ui.write(_("\nadditional help topics:\n\n"))
2321 2320 topics = []
2322 2321 for names, header, doc in help.helptable:
2323 2322 topics.append((sorted(names, key=len, reverse=True)[0], header))
2324 2323 topics_len = max([len(s[0]) for s in topics])
2325 2324 for t, desc in topics:
2326 2325 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2327 2326
2328 2327 if opt_output:
2329 2328 colwidth = encoding.colwidth
2330 2329 # normalize: (opt or message, desc or None, width of opt)
2331 2330 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2332 2331 for opt, desc in opt_output]
2333 2332 hanging = max([e[2] for e in entries])
2334 2333 for opt, desc, width in entries:
2335 2334 if desc:
2336 2335 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2337 2336 hangindent = ' ' * (hanging + 3)
2338 2337 ui.write('%s\n' % (util.wrap(desc, textwidth,
2339 2338 initindent=initindent,
2340 2339 hangindent=hangindent)))
2341 2340 else:
2342 2341 ui.write("%s\n" % opt)
2343 2342
2344 2343 def identify(ui, repo, source=None, rev=None,
2345 2344 num=None, id=None, branch=None, tags=None, bookmarks=None):
2346 2345 """identify the working copy or specified revision
2347 2346
2348 2347 Print a summary identifying the repository state at REV using one or
2349 2348 two parent hash identifiers, followed by a "+" if the working
2350 2349 directory has uncommitted changes, the branch name (if not default),
2351 2350 a list of tags, and a list of bookmarks.
2352 2351
2353 2352 When REV is not given, print a summary of the current state of the
2354 2353 repository.
2355 2354
2356 2355 Specifying a path to a repository root or Mercurial bundle will
2357 2356 cause lookup to operate on that repository/bundle.
2358 2357
2359 2358 Returns 0 if successful.
2360 2359 """
2361 2360
2362 2361 if not repo and not source:
2363 2362 raise util.Abort(_("there is no Mercurial repository here "
2364 2363 "(.hg not found)"))
2365 2364
2366 2365 hexfunc = ui.debugflag and hex or short
2367 2366 default = not (num or id or branch or tags or bookmarks)
2368 2367 output = []
2369 2368 revs = []
2370 2369
2371 2370 if source:
2372 2371 source, branches = hg.parseurl(ui.expandpath(source))
2373 2372 repo = hg.repository(ui, source)
2374 2373 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2375 2374
2376 2375 if not repo.local():
2377 2376 if num or branch or tags:
2378 2377 raise util.Abort(
2379 2378 _("can't query remote revision number, branch, or tags"))
2380 2379 if not rev and revs:
2381 2380 rev = revs[0]
2382 2381 if not rev:
2383 2382 rev = "tip"
2384 2383
2385 2384 remoterev = repo.lookup(rev)
2386 2385 if default or id:
2387 2386 output = [hexfunc(remoterev)]
2388 2387
2389 2388 def getbms():
2390 2389 bms = []
2391 2390
2392 2391 if 'bookmarks' in repo.listkeys('namespaces'):
2393 2392 hexremoterev = hex(remoterev)
2394 2393 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
2395 2394 if bmr == hexremoterev]
2396 2395
2397 2396 return bms
2398 2397
2399 2398 if bookmarks:
2400 2399 output.extend(getbms())
2401 2400 elif default and not ui.quiet:
2402 2401 # multiple bookmarks for a single parent separated by '/'
2403 2402 bm = '/'.join(getbms())
2404 2403 if bm:
2405 2404 output.append(bm)
2406 2405 else:
2407 2406 if not rev:
2408 2407 ctx = repo[None]
2409 2408 parents = ctx.parents()
2410 2409 changed = ""
2411 2410 if default or id or num:
2412 2411 changed = util.any(repo.status()) and "+" or ""
2413 2412 if default or id:
2414 2413 output = ["%s%s" %
2415 2414 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2416 2415 if num:
2417 2416 output.append("%s%s" %
2418 2417 ('+'.join([str(p.rev()) for p in parents]), changed))
2419 2418 else:
2420 2419 ctx = cmdutil.revsingle(repo, rev)
2421 2420 if default or id:
2422 2421 output = [hexfunc(ctx.node())]
2423 2422 if num:
2424 2423 output.append(str(ctx.rev()))
2425 2424
2426 2425 if default and not ui.quiet:
2427 2426 b = ctx.branch()
2428 2427 if b != 'default':
2429 2428 output.append("(%s)" % b)
2430 2429
2431 2430 # multiple tags for a single parent separated by '/'
2432 2431 t = '/'.join(ctx.tags())
2433 2432 if t:
2434 2433 output.append(t)
2435 2434
2436 2435 # multiple bookmarks for a single parent separated by '/'
2437 2436 bm = '/'.join(ctx.bookmarks())
2438 2437 if bm:
2439 2438 output.append(bm)
2440 2439 else:
2441 2440 if branch:
2442 2441 output.append(ctx.branch())
2443 2442
2444 2443 if tags:
2445 2444 output.extend(ctx.tags())
2446 2445
2447 2446 if bookmarks:
2448 2447 output.extend(ctx.bookmarks())
2449 2448
2450 2449 ui.write("%s\n" % ' '.join(output))
2451 2450
2452 2451 def import_(ui, repo, patch1, *patches, **opts):
2453 2452 """import an ordered set of patches
2454 2453
2455 2454 Import a list of patches and commit them individually (unless
2456 2455 --no-commit is specified).
2457 2456
2458 2457 If there are outstanding changes in the working directory, import
2459 2458 will abort unless given the -f/--force flag.
2460 2459
2461 2460 You can import a patch straight from a mail message. Even patches
2462 2461 as attachments work (to use the body part, it must have type
2463 2462 text/plain or text/x-patch). From and Subject headers of email
2464 2463 message are used as default committer and commit message. All
2465 2464 text/plain body parts before first diff are added to commit
2466 2465 message.
2467 2466
2468 2467 If the imported patch was generated by :hg:`export`, user and
2469 2468 description from patch override values from message headers and
2470 2469 body. Values given on command line with -m/--message and -u/--user
2471 2470 override these.
2472 2471
2473 2472 If --exact is specified, import will set the working directory to
2474 2473 the parent of each patch before applying it, and will abort if the
2475 2474 resulting changeset has a different ID than the one recorded in
2476 2475 the patch. This may happen due to character set problems or other
2477 2476 deficiencies in the text patch format.
2478 2477
2479 2478 With -s/--similarity, hg will attempt to discover renames and
2480 2479 copies in the patch in the same way as 'addremove'.
2481 2480
2482 2481 To read a patch from standard input, use "-" as the patch name. If
2483 2482 a URL is specified, the patch will be downloaded from it.
2484 2483 See :hg:`help dates` for a list of formats valid for -d/--date.
2485 2484
2486 2485 Returns 0 on success.
2487 2486 """
2488 2487 patches = (patch1,) + patches
2489 2488
2490 2489 date = opts.get('date')
2491 2490 if date:
2492 2491 opts['date'] = util.parsedate(date)
2493 2492
2494 2493 try:
2495 2494 sim = float(opts.get('similarity') or 0)
2496 2495 except ValueError:
2497 2496 raise util.Abort(_('similarity must be a number'))
2498 2497 if sim < 0 or sim > 100:
2499 2498 raise util.Abort(_('similarity must be between 0 and 100'))
2500 2499
2501 2500 if opts.get('exact') or not opts.get('force'):
2502 2501 cmdutil.bail_if_changed(repo)
2503 2502
2504 2503 d = opts["base"]
2505 2504 strip = opts["strip"]
2506 2505 wlock = lock = None
2507 2506 msgs = []
2508 2507
2509 2508 def tryone(ui, hunk):
2510 2509 tmpname, message, user, date, branch, nodeid, p1, p2 = \
2511 2510 patch.extract(ui, hunk)
2512 2511
2513 2512 if not tmpname:
2514 2513 return None
2515 2514 commitid = _('to working directory')
2516 2515
2517 2516 try:
2518 2517 cmdline_message = cmdutil.logmessage(opts)
2519 2518 if cmdline_message:
2520 2519 # pickup the cmdline msg
2521 2520 message = cmdline_message
2522 2521 elif message:
2523 2522 # pickup the patch msg
2524 2523 message = message.strip()
2525 2524 else:
2526 2525 # launch the editor
2527 2526 message = None
2528 2527 ui.debug('message:\n%s\n' % message)
2529 2528
2530 2529 wp = repo.parents()
2531 2530 if opts.get('exact'):
2532 2531 if not nodeid or not p1:
2533 2532 raise util.Abort(_('not a Mercurial patch'))
2534 2533 p1 = repo.lookup(p1)
2535 2534 p2 = repo.lookup(p2 or hex(nullid))
2536 2535
2537 2536 if p1 != wp[0].node():
2538 2537 hg.clean(repo, p1)
2539 2538 repo.dirstate.setparents(p1, p2)
2540 2539 elif p2:
2541 2540 try:
2542 2541 p1 = repo.lookup(p1)
2543 2542 p2 = repo.lookup(p2)
2544 2543 if p1 == wp[0].node():
2545 2544 repo.dirstate.setparents(p1, p2)
2546 2545 except error.RepoError:
2547 2546 pass
2548 2547 if opts.get('exact') or opts.get('import_branch'):
2549 2548 repo.dirstate.setbranch(branch or 'default')
2550 2549
2551 2550 files = {}
2552 2551 try:
2553 2552 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2554 2553 files=files, eolmode=None)
2555 2554 finally:
2556 2555 files = cmdutil.updatedir(ui, repo, files,
2557 2556 similarity=sim / 100.0)
2558 2557 if opts.get('no_commit'):
2559 2558 if message:
2560 2559 msgs.append(message)
2561 2560 else:
2562 2561 if opts.get('exact'):
2563 2562 m = None
2564 2563 else:
2565 2564 m = cmdutil.matchfiles(repo, files or [])
2566 2565 n = repo.commit(message, opts.get('user') or user,
2567 2566 opts.get('date') or date, match=m,
2568 2567 editor=cmdutil.commiteditor)
2569 2568 if opts.get('exact'):
2570 2569 if hex(n) != nodeid:
2571 2570 repo.rollback()
2572 2571 raise util.Abort(_('patch is damaged'
2573 2572 ' or loses information'))
2574 2573 # Force a dirstate write so that the next transaction
2575 2574 # backups an up-do-date file.
2576 2575 repo.dirstate.write()
2577 2576 if n:
2578 2577 commitid = short(n)
2579 2578
2580 2579 return commitid
2581 2580 finally:
2582 2581 os.unlink(tmpname)
2583 2582
2584 2583 try:
2585 2584 wlock = repo.wlock()
2586 2585 lock = repo.lock()
2587 2586 lastcommit = None
2588 2587 for p in patches:
2589 2588 pf = os.path.join(d, p)
2590 2589
2591 2590 if pf == '-':
2592 2591 ui.status(_("applying patch from stdin\n"))
2593 2592 pf = sys.stdin
2594 2593 else:
2595 2594 ui.status(_("applying %s\n") % p)
2596 2595 pf = url.open(ui, pf)
2597 2596
2598 2597 haspatch = False
2599 2598 for hunk in patch.split(pf):
2600 2599 commitid = tryone(ui, hunk)
2601 2600 if commitid:
2602 2601 haspatch = True
2603 2602 if lastcommit:
2604 2603 ui.status(_('applied %s\n') % lastcommit)
2605 2604 lastcommit = commitid
2606 2605
2607 2606 if not haspatch:
2608 2607 raise util.Abort(_('no diffs found'))
2609 2608
2610 2609 if msgs:
2611 2610 repo.opener('last-message.txt', 'wb').write('\n* * *\n'.join(msgs))
2612 2611 finally:
2613 2612 release(lock, wlock)
2614 2613
2615 2614 def incoming(ui, repo, source="default", **opts):
2616 2615 """show new changesets found in source
2617 2616
2618 2617 Show new changesets found in the specified path/URL or the default
2619 2618 pull location. These are the changesets that would have been pulled
2620 2619 if a pull at the time you issued this command.
2621 2620
2622 2621 For remote repository, using --bundle avoids downloading the
2623 2622 changesets twice if the incoming is followed by a pull.
2624 2623
2625 2624 See pull for valid source format details.
2626 2625
2627 2626 Returns 0 if there are incoming changes, 1 otherwise.
2628 2627 """
2629 2628 if opts.get('bundle') and opts.get('subrepos'):
2630 2629 raise util.Abort(_('cannot combine --bundle and --subrepos'))
2631 2630
2632 2631 if opts.get('bookmarks'):
2633 2632 source, branches = hg.parseurl(ui.expandpath(source),
2634 2633 opts.get('branch'))
2635 2634 other = hg.repository(hg.remoteui(repo, opts), source)
2636 2635 if 'bookmarks' not in other.listkeys('namespaces'):
2637 2636 ui.warn(_("remote doesn't support bookmarks\n"))
2638 2637 return 0
2639 2638 ui.status(_('comparing with %s\n') % url.hidepassword(source))
2640 2639 return bookmarks.diff(ui, repo, other)
2641 2640
2642 2641 ret = hg.incoming(ui, repo, source, opts)
2643 2642 return ret
2644 2643
2645 2644 def init(ui, dest=".", **opts):
2646 2645 """create a new repository in the given directory
2647 2646
2648 2647 Initialize a new repository in the given directory. If the given
2649 2648 directory does not exist, it will be created.
2650 2649
2651 2650 If no directory is given, the current directory is used.
2652 2651
2653 2652 It is possible to specify an ``ssh://`` URL as the destination.
2654 2653 See :hg:`help urls` for more information.
2655 2654
2656 2655 Returns 0 on success.
2657 2656 """
2658 2657 hg.repository(hg.remoteui(ui, opts), ui.expandpath(dest), create=1)
2659 2658
2660 2659 def locate(ui, repo, *pats, **opts):
2661 2660 """locate files matching specific patterns
2662 2661
2663 2662 Print files under Mercurial control in the working directory whose
2664 2663 names match the given patterns.
2665 2664
2666 2665 By default, this command searches all directories in the working
2667 2666 directory. To search just the current directory and its
2668 2667 subdirectories, use "--include .".
2669 2668
2670 2669 If no patterns are given to match, this command prints the names
2671 2670 of all files under Mercurial control in the working directory.
2672 2671
2673 2672 If you want to feed the output of this command into the "xargs"
2674 2673 command, use the -0 option to both this command and "xargs". This
2675 2674 will avoid the problem of "xargs" treating single filenames that
2676 2675 contain whitespace as multiple filenames.
2677 2676
2678 2677 Returns 0 if a match is found, 1 otherwise.
2679 2678 """
2680 2679 end = opts.get('print0') and '\0' or '\n'
2681 2680 rev = cmdutil.revsingle(repo, opts.get('rev'), None).node()
2682 2681
2683 2682 ret = 1
2684 2683 m = cmdutil.match(repo, pats, opts, default='relglob')
2685 2684 m.bad = lambda x, y: False
2686 2685 for abs in repo[rev].walk(m):
2687 2686 if not rev and abs not in repo.dirstate:
2688 2687 continue
2689 2688 if opts.get('fullpath'):
2690 2689 ui.write(repo.wjoin(abs), end)
2691 2690 else:
2692 2691 ui.write(((pats and m.rel(abs)) or abs), end)
2693 2692 ret = 0
2694 2693
2695 2694 return ret
2696 2695
2697 2696 def log(ui, repo, *pats, **opts):
2698 2697 """show revision history of entire repository or files
2699 2698
2700 2699 Print the revision history of the specified files or the entire
2701 2700 project.
2702 2701
2703 2702 File history is shown without following rename or copy history of
2704 2703 files. Use -f/--follow with a filename to follow history across
2705 2704 renames and copies. --follow without a filename will only show
2706 2705 ancestors or descendants of the starting revision. --follow-first
2707 2706 only follows the first parent of merge revisions.
2708 2707
2709 2708 If no revision range is specified, the default is ``tip:0`` unless
2710 2709 --follow is set, in which case the working directory parent is
2711 2710 used as the starting revision. You can specify a revision set for
2712 2711 log, see :hg:`help revsets` for more information.
2713 2712
2714 2713 See :hg:`help dates` for a list of formats valid for -d/--date.
2715 2714
2716 2715 By default this command prints revision number and changeset id,
2717 2716 tags, non-trivial parents, user, date and time, and a summary for
2718 2717 each commit. When the -v/--verbose switch is used, the list of
2719 2718 changed files and full commit message are shown.
2720 2719
2721 2720 .. note::
2722 2721 log -p/--patch may generate unexpected diff output for merge
2723 2722 changesets, as it will only compare the merge changeset against
2724 2723 its first parent. Also, only files different from BOTH parents
2725 2724 will appear in files:.
2726 2725
2727 2726 Returns 0 on success.
2728 2727 """
2729 2728
2730 2729 matchfn = cmdutil.match(repo, pats, opts)
2731 2730 limit = cmdutil.loglimit(opts)
2732 2731 count = 0
2733 2732
2734 2733 endrev = None
2735 2734 if opts.get('copies') and opts.get('rev'):
2736 2735 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2737 2736
2738 2737 df = False
2739 2738 if opts["date"]:
2740 2739 df = util.matchdate(opts["date"])
2741 2740
2742 2741 branches = opts.get('branch', []) + opts.get('only_branch', [])
2743 2742 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2744 2743
2745 2744 displayer = cmdutil.show_changeset(ui, repo, opts, True)
2746 2745 def prep(ctx, fns):
2747 2746 rev = ctx.rev()
2748 2747 parents = [p for p in repo.changelog.parentrevs(rev)
2749 2748 if p != nullrev]
2750 2749 if opts.get('no_merges') and len(parents) == 2:
2751 2750 return
2752 2751 if opts.get('only_merges') and len(parents) != 2:
2753 2752 return
2754 2753 if opts.get('branch') and ctx.branch() not in opts['branch']:
2755 2754 return
2756 2755 if df and not df(ctx.date()[0]):
2757 2756 return
2758 2757 if opts['user'] and not [k for k in opts['user']
2759 2758 if k.lower() in ctx.user().lower()]:
2760 2759 return
2761 2760 if opts.get('keyword'):
2762 2761 for k in [kw.lower() for kw in opts['keyword']]:
2763 2762 if (k in ctx.user().lower() or
2764 2763 k in ctx.description().lower() or
2765 2764 k in " ".join(ctx.files()).lower()):
2766 2765 break
2767 2766 else:
2768 2767 return
2769 2768
2770 2769 copies = None
2771 2770 if opts.get('copies') and rev:
2772 2771 copies = []
2773 2772 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2774 2773 for fn in ctx.files():
2775 2774 rename = getrenamed(fn, rev)
2776 2775 if rename:
2777 2776 copies.append((fn, rename[0]))
2778 2777
2779 2778 revmatchfn = None
2780 2779 if opts.get('patch') or opts.get('stat'):
2781 2780 if opts.get('follow') or opts.get('follow_first'):
2782 2781 # note: this might be wrong when following through merges
2783 2782 revmatchfn = cmdutil.match(repo, fns, default='path')
2784 2783 else:
2785 2784 revmatchfn = matchfn
2786 2785
2787 2786 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2788 2787
2789 2788 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2790 2789 if count == limit:
2791 2790 break
2792 2791 if displayer.flush(ctx.rev()):
2793 2792 count += 1
2794 2793 displayer.close()
2795 2794
2796 2795 def manifest(ui, repo, node=None, rev=None):
2797 2796 """output the current or given revision of the project manifest
2798 2797
2799 2798 Print a list of version controlled files for the given revision.
2800 2799 If no revision is given, the first parent of the working directory
2801 2800 is used, or the null revision if no revision is checked out.
2802 2801
2803 2802 With -v, print file permissions, symlink and executable bits.
2804 2803 With --debug, print file revision hashes.
2805 2804
2806 2805 Returns 0 on success.
2807 2806 """
2808 2807
2809 2808 if rev and node:
2810 2809 raise util.Abort(_("please specify just one revision"))
2811 2810
2812 2811 if not node:
2813 2812 node = rev
2814 2813
2815 2814 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2816 2815 ctx = cmdutil.revsingle(repo, node)
2817 2816 for f in ctx:
2818 2817 if ui.debugflag:
2819 2818 ui.write("%40s " % hex(ctx.manifest()[f]))
2820 2819 if ui.verbose:
2821 2820 ui.write(decor[ctx.flags(f)])
2822 2821 ui.write("%s\n" % f)
2823 2822
2824 2823 def merge(ui, repo, node=None, **opts):
2825 2824 """merge working directory with another revision
2826 2825
2827 2826 The current working directory is updated with all changes made in
2828 2827 the requested revision since the last common predecessor revision.
2829 2828
2830 2829 Files that changed between either parent are marked as changed for
2831 2830 the next commit and a commit must be performed before any further
2832 2831 updates to the repository are allowed. The next commit will have
2833 2832 two parents.
2834 2833
2835 2834 ``--tool`` can be used to specify the merge tool used for file
2836 2835 merges. It overrides the HGMERGE environment variable and your
2837 2836 configuration files. See :hg:`help merge-tools` for options.
2838 2837
2839 2838 If no revision is specified, the working directory's parent is a
2840 2839 head revision, and the current branch contains exactly one other
2841 2840 head, the other head is merged with by default. Otherwise, an
2842 2841 explicit revision with which to merge with must be provided.
2843 2842
2844 2843 :hg:`resolve` must be used to resolve unresolved files.
2845 2844
2846 2845 To undo an uncommitted merge, use :hg:`update --clean .` which
2847 2846 will check out a clean copy of the original merge parent, losing
2848 2847 all changes.
2849 2848
2850 2849 Returns 0 on success, 1 if there are unresolved files.
2851 2850 """
2852 2851
2853 2852 if opts.get('rev') and node:
2854 2853 raise util.Abort(_("please specify just one revision"))
2855 2854 if not node:
2856 2855 node = opts.get('rev')
2857 2856
2858 2857 if not node:
2859 2858 branch = repo[None].branch()
2860 2859 bheads = repo.branchheads(branch)
2861 2860 if len(bheads) > 2:
2862 2861 raise util.Abort(_(
2863 2862 'branch \'%s\' has %d heads - '
2864 2863 'please merge with an explicit rev\n'
2865 2864 '(run \'hg heads .\' to see heads)')
2866 2865 % (branch, len(bheads)))
2867 2866
2868 2867 parent = repo.dirstate.p1()
2869 2868 if len(bheads) == 1:
2870 2869 if len(repo.heads()) > 1:
2871 2870 raise util.Abort(_(
2872 2871 'branch \'%s\' has one head - '
2873 2872 'please merge with an explicit rev\n'
2874 2873 '(run \'hg heads\' to see all heads)')
2875 2874 % branch)
2876 2875 msg = _('there is nothing to merge')
2877 2876 if parent != repo.lookup(repo[None].branch()):
2878 2877 msg = _('%s - use "hg update" instead') % msg
2879 2878 raise util.Abort(msg)
2880 2879
2881 2880 if parent not in bheads:
2882 2881 raise util.Abort(_('working dir not at a head rev - '
2883 2882 'use "hg update" or merge with an explicit rev'))
2884 2883 node = parent == bheads[0] and bheads[-1] or bheads[0]
2885 2884 else:
2886 2885 node = cmdutil.revsingle(repo, node).node()
2887 2886
2888 2887 if opts.get('preview'):
2889 2888 # find nodes that are ancestors of p2 but not of p1
2890 2889 p1 = repo.lookup('.')
2891 2890 p2 = repo.lookup(node)
2892 2891 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2893 2892
2894 2893 displayer = cmdutil.show_changeset(ui, repo, opts)
2895 2894 for node in nodes:
2896 2895 displayer.show(repo[node])
2897 2896 displayer.close()
2898 2897 return 0
2899 2898
2900 2899 try:
2901 2900 # ui.forcemerge is an internal variable, do not document
2902 2901 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2903 2902 return hg.merge(repo, node, force=opts.get('force'))
2904 2903 finally:
2905 2904 ui.setconfig('ui', 'forcemerge', '')
2906 2905
2907 2906 def outgoing(ui, repo, dest=None, **opts):
2908 2907 """show changesets not found in the destination
2909 2908
2910 2909 Show changesets not found in the specified destination repository
2911 2910 or the default push location. These are the changesets that would
2912 2911 be pushed if a push was requested.
2913 2912
2914 2913 See pull for details of valid destination formats.
2915 2914
2916 2915 Returns 0 if there are outgoing changes, 1 otherwise.
2917 2916 """
2918 2917
2919 2918 if opts.get('bookmarks'):
2920 2919 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2921 2920 dest, branches = hg.parseurl(dest, opts.get('branch'))
2922 2921 other = hg.repository(hg.remoteui(repo, opts), dest)
2923 2922 if 'bookmarks' not in other.listkeys('namespaces'):
2924 2923 ui.warn(_("remote doesn't support bookmarks\n"))
2925 2924 return 0
2926 2925 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2927 2926 return bookmarks.diff(ui, other, repo)
2928 2927
2929 2928 ret = hg.outgoing(ui, repo, dest, opts)
2930 2929 return ret
2931 2930
2932 2931 def parents(ui, repo, file_=None, **opts):
2933 2932 """show the parents of the working directory or revision
2934 2933
2935 2934 Print the working directory's parent revisions. If a revision is
2936 2935 given via -r/--rev, the parent of that revision will be printed.
2937 2936 If a file argument is given, the revision in which the file was
2938 2937 last changed (before the working directory revision or the
2939 2938 argument to --rev if given) is printed.
2940 2939
2941 2940 Returns 0 on success.
2942 2941 """
2943 2942
2944 2943 ctx = cmdutil.revsingle(repo, opts.get('rev'), None)
2945 2944
2946 2945 if file_:
2947 2946 m = cmdutil.match(repo, (file_,), opts)
2948 2947 if m.anypats() or len(m.files()) != 1:
2949 2948 raise util.Abort(_('can only specify an explicit filename'))
2950 2949 file_ = m.files()[0]
2951 2950 filenodes = []
2952 2951 for cp in ctx.parents():
2953 2952 if not cp:
2954 2953 continue
2955 2954 try:
2956 2955 filenodes.append(cp.filenode(file_))
2957 2956 except error.LookupError:
2958 2957 pass
2959 2958 if not filenodes:
2960 2959 raise util.Abort(_("'%s' not found in manifest!") % file_)
2961 2960 fl = repo.file(file_)
2962 2961 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2963 2962 else:
2964 2963 p = [cp.node() for cp in ctx.parents()]
2965 2964
2966 2965 displayer = cmdutil.show_changeset(ui, repo, opts)
2967 2966 for n in p:
2968 2967 if n != nullid:
2969 2968 displayer.show(repo[n])
2970 2969 displayer.close()
2971 2970
2972 2971 def paths(ui, repo, search=None):
2973 2972 """show aliases for remote repositories
2974 2973
2975 2974 Show definition of symbolic path name NAME. If no name is given,
2976 2975 show definition of all available names.
2977 2976
2978 2977 Path names are defined in the [paths] section of your
2979 2978 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
2980 2979 repository, ``.hg/hgrc`` is used, too.
2981 2980
2982 2981 The path names ``default`` and ``default-push`` have a special
2983 2982 meaning. When performing a push or pull operation, they are used
2984 2983 as fallbacks if no location is specified on the command-line.
2985 2984 When ``default-push`` is set, it will be used for push and
2986 2985 ``default`` will be used for pull; otherwise ``default`` is used
2987 2986 as the fallback for both. When cloning a repository, the clone
2988 2987 source is written as ``default`` in ``.hg/hgrc``. Note that
2989 2988 ``default`` and ``default-push`` apply to all inbound (e.g.
2990 2989 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2991 2990 :hg:`bundle`) operations.
2992 2991
2993 2992 See :hg:`help urls` for more information.
2994 2993
2995 2994 Returns 0 on success.
2996 2995 """
2997 2996 if search:
2998 2997 for name, path in ui.configitems("paths"):
2999 2998 if name == search:
3000 2999 ui.write("%s\n" % url.hidepassword(path))
3001 3000 return
3002 3001 ui.warn(_("not found!\n"))
3003 3002 return 1
3004 3003 else:
3005 3004 for name, path in ui.configitems("paths"):
3006 3005 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
3007 3006
3008 3007 def postincoming(ui, repo, modheads, optupdate, checkout):
3009 3008 if modheads == 0:
3010 3009 return
3011 3010 if optupdate:
3012 3011 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
3013 3012 return hg.update(repo, checkout)
3014 3013 else:
3015 3014 ui.status(_("not updating, since new heads added\n"))
3016 3015 if modheads > 1:
3017 3016 currentbranchheads = len(repo.branchheads())
3018 3017 if currentbranchheads == modheads:
3019 3018 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3020 3019 elif currentbranchheads > 1:
3021 3020 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
3022 3021 else:
3023 3022 ui.status(_("(run 'hg heads' to see heads)\n"))
3024 3023 else:
3025 3024 ui.status(_("(run 'hg update' to get a working copy)\n"))
3026 3025
3027 3026 def pull(ui, repo, source="default", **opts):
3028 3027 """pull changes from the specified source
3029 3028
3030 3029 Pull changes from a remote repository to a local one.
3031 3030
3032 3031 This finds all changes from the repository at the specified path
3033 3032 or URL and adds them to a local repository (the current one unless
3034 3033 -R is specified). By default, this does not update the copy of the
3035 3034 project in the working directory.
3036 3035
3037 3036 Use :hg:`incoming` if you want to see what would have been added
3038 3037 by a pull at the time you issued this command. If you then decide
3039 3038 to add those changes to the repository, you should use :hg:`pull
3040 3039 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3041 3040
3042 3041 If SOURCE is omitted, the 'default' path will be used.
3043 3042 See :hg:`help urls` for more information.
3044 3043
3045 3044 Returns 0 on success, 1 if an update had unresolved files.
3046 3045 """
3047 3046 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3048 3047 other = hg.repository(hg.remoteui(repo, opts), source)
3049 3048 ui.status(_('pulling from %s\n') % url.hidepassword(source))
3050 3049 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3051 3050
3052 3051 if opts.get('bookmark'):
3053 3052 if not revs:
3054 3053 revs = []
3055 3054 rb = other.listkeys('bookmarks')
3056 3055 for b in opts['bookmark']:
3057 3056 if b not in rb:
3058 3057 raise util.Abort(_('remote bookmark %s not found!') % b)
3059 3058 revs.append(rb[b])
3060 3059
3061 3060 if revs:
3062 3061 try:
3063 3062 revs = [other.lookup(rev) for rev in revs]
3064 3063 except error.CapabilityError:
3065 3064 err = _("other repository doesn't support revision lookup, "
3066 3065 "so a rev cannot be specified.")
3067 3066 raise util.Abort(err)
3068 3067
3069 3068 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
3070 3069 bookmarks.updatefromremote(ui, repo, other)
3071 3070 if checkout:
3072 3071 checkout = str(repo.changelog.rev(other.lookup(checkout)))
3073 3072 repo._subtoppath = source
3074 3073 try:
3075 3074 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
3076 3075
3077 3076 finally:
3078 3077 del repo._subtoppath
3079 3078
3080 3079 # update specified bookmarks
3081 3080 if opts.get('bookmark'):
3082 3081 for b in opts['bookmark']:
3083 3082 # explicit pull overrides local bookmark if any
3084 3083 ui.status(_("importing bookmark %s\n") % b)
3085 3084 repo._bookmarks[b] = repo[rb[b]].node()
3086 3085 bookmarks.write(repo)
3087 3086
3088 3087 return ret
3089 3088
3090 3089 def push(ui, repo, dest=None, **opts):
3091 3090 """push changes to the specified destination
3092 3091
3093 3092 Push changesets from the local repository to the specified
3094 3093 destination.
3095 3094
3096 3095 This operation is symmetrical to pull: it is identical to a pull
3097 3096 in the destination repository from the current one.
3098 3097
3099 3098 By default, push will not allow creation of new heads at the
3100 3099 destination, since multiple heads would make it unclear which head
3101 3100 to use. In this situation, it is recommended to pull and merge
3102 3101 before pushing.
3103 3102
3104 3103 Use --new-branch if you want to allow push to create a new named
3105 3104 branch that is not present at the destination. This allows you to
3106 3105 only create a new branch without forcing other changes.
3107 3106
3108 3107 Use -f/--force to override the default behavior and push all
3109 3108 changesets on all branches.
3110 3109
3111 3110 If -r/--rev is used, the specified revision and all its ancestors
3112 3111 will be pushed to the remote repository.
3113 3112
3114 3113 Please see :hg:`help urls` for important details about ``ssh://``
3115 3114 URLs. If DESTINATION is omitted, a default path will be used.
3116 3115
3117 3116 Returns 0 if push was successful, 1 if nothing to push.
3118 3117 """
3119 3118
3120 3119 if opts.get('bookmark'):
3121 3120 for b in opts['bookmark']:
3122 3121 # translate -B options to -r so changesets get pushed
3123 3122 if b in repo._bookmarks:
3124 3123 opts.setdefault('rev', []).append(b)
3125 3124 else:
3126 3125 # if we try to push a deleted bookmark, translate it to null
3127 3126 # this lets simultaneous -r, -b options continue working
3128 3127 opts.setdefault('rev', []).append("null")
3129 3128
3130 3129 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3131 3130 dest, branches = hg.parseurl(dest, opts.get('branch'))
3132 3131 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
3133 3132 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
3134 3133 other = hg.repository(hg.remoteui(repo, opts), dest)
3135 3134 if revs:
3136 3135 revs = [repo.lookup(rev) for rev in revs]
3137 3136
3138 3137 repo._subtoppath = dest
3139 3138 try:
3140 3139 # push subrepos depth-first for coherent ordering
3141 3140 c = repo['']
3142 3141 subs = c.substate # only repos that are committed
3143 3142 for s in sorted(subs):
3144 3143 if not c.sub(s).push(opts.get('force')):
3145 3144 return False
3146 3145 finally:
3147 3146 del repo._subtoppath
3148 3147 result = repo.push(other, opts.get('force'), revs=revs,
3149 3148 newbranch=opts.get('new_branch'))
3150 3149
3151 3150 result = (result == 0)
3152 3151
3153 3152 if opts.get('bookmark'):
3154 3153 rb = other.listkeys('bookmarks')
3155 3154 for b in opts['bookmark']:
3156 3155 # explicit push overrides remote bookmark if any
3157 3156 if b in repo._bookmarks:
3158 3157 ui.status(_("exporting bookmark %s\n") % b)
3159 3158 new = repo[b].hex()
3160 3159 elif b in rb:
3161 3160 ui.status(_("deleting remote bookmark %s\n") % b)
3162 3161 new = '' # delete
3163 3162 else:
3164 3163 ui.warn(_('bookmark %s does not exist on the local '
3165 3164 'or remote repository!\n') % b)
3166 3165 return 2
3167 3166 old = rb.get(b, '')
3168 3167 r = other.pushkey('bookmarks', b, old, new)
3169 3168 if not r:
3170 3169 ui.warn(_('updating bookmark %s failed!\n') % b)
3171 3170 if not result:
3172 3171 result = 2
3173 3172
3174 3173 return result
3175 3174
3176 3175 def recover(ui, repo):
3177 3176 """roll back an interrupted transaction
3178 3177
3179 3178 Recover from an interrupted commit or pull.
3180 3179
3181 3180 This command tries to fix the repository status after an
3182 3181 interrupted operation. It should only be necessary when Mercurial
3183 3182 suggests it.
3184 3183
3185 3184 Returns 0 if successful, 1 if nothing to recover or verify fails.
3186 3185 """
3187 3186 if repo.recover():
3188 3187 return hg.verify(repo)
3189 3188 return 1
3190 3189
3191 3190 def remove(ui, repo, *pats, **opts):
3192 3191 """remove the specified files on the next commit
3193 3192
3194 3193 Schedule the indicated files for removal from the repository.
3195 3194
3196 3195 This only removes files from the current branch, not from the
3197 3196 entire project history. -A/--after can be used to remove only
3198 3197 files that have already been deleted, -f/--force can be used to
3199 3198 force deletion, and -Af can be used to remove files from the next
3200 3199 revision without deleting them from the working directory.
3201 3200
3202 3201 The following table details the behavior of remove for different
3203 3202 file states (columns) and option combinations (rows). The file
3204 3203 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
3205 3204 reported by :hg:`status`). The actions are Warn, Remove (from
3206 3205 branch) and Delete (from disk)::
3207 3206
3208 3207 A C M !
3209 3208 none W RD W R
3210 3209 -f R RD RD R
3211 3210 -A W W W R
3212 3211 -Af R R R R
3213 3212
3214 3213 This command schedules the files to be removed at the next commit.
3215 3214 To undo a remove before that, see :hg:`revert`.
3216 3215
3217 3216 Returns 0 on success, 1 if any warnings encountered.
3218 3217 """
3219 3218
3220 3219 ret = 0
3221 3220 after, force = opts.get('after'), opts.get('force')
3222 3221 if not pats and not after:
3223 3222 raise util.Abort(_('no files specified'))
3224 3223
3225 3224 m = cmdutil.match(repo, pats, opts)
3226 3225 s = repo.status(match=m, clean=True)
3227 3226 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
3228 3227
3229 3228 for f in m.files():
3230 3229 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
3231 3230 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
3232 3231 ret = 1
3233 3232
3234 3233 if force:
3235 3234 remove, forget = modified + deleted + clean, added
3236 3235 elif after:
3237 3236 remove, forget = deleted, []
3238 3237 for f in modified + added + clean:
3239 3238 ui.warn(_('not removing %s: file still exists (use -f'
3240 3239 ' to force removal)\n') % m.rel(f))
3241 3240 ret = 1
3242 3241 else:
3243 3242 remove, forget = deleted + clean, []
3244 3243 for f in modified:
3245 3244 ui.warn(_('not removing %s: file is modified (use -f'
3246 3245 ' to force removal)\n') % m.rel(f))
3247 3246 ret = 1
3248 3247 for f in added:
3249 3248 ui.warn(_('not removing %s: file has been marked for add (use -f'
3250 3249 ' to force removal)\n') % m.rel(f))
3251 3250 ret = 1
3252 3251
3253 3252 for f in sorted(remove + forget):
3254 3253 if ui.verbose or not m.exact(f):
3255 3254 ui.status(_('removing %s\n') % m.rel(f))
3256 3255
3257 3256 repo[None].forget(forget)
3258 3257 repo[None].remove(remove, unlink=not after)
3259 3258 return ret
3260 3259
3261 3260 def rename(ui, repo, *pats, **opts):
3262 3261 """rename files; equivalent of copy + remove
3263 3262
3264 3263 Mark dest as copies of sources; mark sources for deletion. If dest
3265 3264 is a directory, copies are put in that directory. If dest is a
3266 3265 file, there can only be one source.
3267 3266
3268 3267 By default, this command copies the contents of files as they
3269 3268 exist in the working directory. If invoked with -A/--after, the
3270 3269 operation is recorded, but no copying is performed.
3271 3270
3272 3271 This command takes effect at the next commit. To undo a rename
3273 3272 before that, see :hg:`revert`.
3274 3273
3275 3274 Returns 0 on success, 1 if errors are encountered.
3276 3275 """
3277 3276 wlock = repo.wlock(False)
3278 3277 try:
3279 3278 return cmdutil.copy(ui, repo, pats, opts, rename=True)
3280 3279 finally:
3281 3280 wlock.release()
3282 3281
3283 3282 def resolve(ui, repo, *pats, **opts):
3284 3283 """redo merges or set/view the merge status of files
3285 3284
3286 3285 Merges with unresolved conflicts are often the result of
3287 3286 non-interactive merging using the ``internal:merge`` configuration
3288 3287 setting, or a command-line merge tool like ``diff3``. The resolve
3289 3288 command is used to manage the files involved in a merge, after
3290 3289 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
3291 3290 working directory must have two parents).
3292 3291
3293 3292 The resolve command can be used in the following ways:
3294 3293
3295 3294 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
3296 3295 files, discarding any previous merge attempts. Re-merging is not
3297 3296 performed for files already marked as resolved. Use ``--all/-a``
3298 3297 to selects all unresolved files. ``--tool`` can be used to specify
3299 3298 the merge tool used for the given files. It overrides the HGMERGE
3300 3299 environment variable and your configuration files.
3301 3300
3302 3301 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
3303 3302 (e.g. after having manually fixed-up the files). The default is
3304 3303 to mark all unresolved files.
3305 3304
3306 3305 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
3307 3306 default is to mark all resolved files.
3308 3307
3309 3308 - :hg:`resolve -l`: list files which had or still have conflicts.
3310 3309 In the printed list, ``U`` = unresolved and ``R`` = resolved.
3311 3310
3312 3311 Note that Mercurial will not let you commit files with unresolved
3313 3312 merge conflicts. You must use :hg:`resolve -m ...` before you can
3314 3313 commit after a conflicting merge.
3315 3314
3316 3315 Returns 0 on success, 1 if any files fail a resolve attempt.
3317 3316 """
3318 3317
3319 3318 all, mark, unmark, show, nostatus = \
3320 3319 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
3321 3320
3322 3321 if (show and (mark or unmark)) or (mark and unmark):
3323 3322 raise util.Abort(_("too many options specified"))
3324 3323 if pats and all:
3325 3324 raise util.Abort(_("can't specify --all and patterns"))
3326 3325 if not (all or pats or show or mark or unmark):
3327 3326 raise util.Abort(_('no files or directories specified; '
3328 3327 'use --all to remerge all files'))
3329 3328
3330 3329 ms = mergemod.mergestate(repo)
3331 3330 m = cmdutil.match(repo, pats, opts)
3332 3331 ret = 0
3333 3332
3334 3333 for f in ms:
3335 3334 if m(f):
3336 3335 if show:
3337 3336 if nostatus:
3338 3337 ui.write("%s\n" % f)
3339 3338 else:
3340 3339 ui.write("%s %s\n" % (ms[f].upper(), f),
3341 3340 label='resolve.' +
3342 3341 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3343 3342 elif mark:
3344 3343 ms.mark(f, "r")
3345 3344 elif unmark:
3346 3345 ms.mark(f, "u")
3347 3346 else:
3348 3347 wctx = repo[None]
3349 3348 mctx = wctx.parents()[-1]
3350 3349
3351 3350 # backup pre-resolve (merge uses .orig for its own purposes)
3352 3351 a = repo.wjoin(f)
3353 3352 util.copyfile(a, a + ".resolve")
3354 3353
3355 3354 try:
3356 3355 # resolve file
3357 3356 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3358 3357 if ms.resolve(f, wctx, mctx):
3359 3358 ret = 1
3360 3359 finally:
3361 3360 ui.setconfig('ui', 'forcemerge', '')
3362 3361
3363 3362 # replace filemerge's .orig file with our resolve file
3364 3363 util.rename(a + ".resolve", a + ".orig")
3365 3364
3366 3365 ms.commit()
3367 3366 return ret
3368 3367
3369 3368 def revert(ui, repo, *pats, **opts):
3370 3369 """restore individual files or directories to an earlier state
3371 3370
3372 3371 .. note::
3373 3372 This command is most likely not what you are looking for.
3374 3373 Revert will partially overwrite content in the working
3375 3374 directory without changing the working directory parents. Use
3376 3375 :hg:`update -r rev` to check out earlier revisions, or
3377 3376 :hg:`update --clean .` to undo a merge which has added another
3378 3377 parent.
3379 3378
3380 3379 With no revision specified, revert the named files or directories
3381 3380 to the contents they had in the parent of the working directory.
3382 3381 This restores the contents of the affected files to an unmodified
3383 3382 state and unschedules adds, removes, copies, and renames. If the
3384 3383 working directory has two parents, you must explicitly specify a
3385 3384 revision.
3386 3385
3387 3386 Using the -r/--rev option, revert the given files or directories
3388 3387 to their contents as of a specific revision. This can be helpful
3389 3388 to "roll back" some or all of an earlier change. See :hg:`help
3390 3389 dates` for a list of formats valid for -d/--date.
3391 3390
3392 3391 Revert modifies the working directory. It does not commit any
3393 3392 changes, or change the parent of the working directory. If you
3394 3393 revert to a revision other than the parent of the working
3395 3394 directory, the reverted files will thus appear modified
3396 3395 afterwards.
3397 3396
3398 3397 If a file has been deleted, it is restored. Files scheduled for
3399 3398 addition are just unscheduled and left as they are. If the
3400 3399 executable mode of a file was changed, it is reset.
3401 3400
3402 3401 If names are given, all files matching the names are reverted.
3403 3402 If no arguments are given, no files are reverted.
3404 3403
3405 3404 Modified files are saved with a .orig suffix before reverting.
3406 3405 To disable these backups, use --no-backup.
3407 3406
3408 3407 Returns 0 on success.
3409 3408 """
3410 3409
3411 3410 if opts.get("date"):
3412 3411 if opts.get("rev"):
3413 3412 raise util.Abort(_("you can't specify a revision and a date"))
3414 3413 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
3415 3414
3416 3415 parent, p2 = repo.dirstate.parents()
3417 3416 if not opts.get('rev') and p2 != nullid:
3418 3417 raise util.Abort(_('uncommitted merge - '
3419 3418 'use "hg update", see "hg help revert"'))
3420 3419
3421 3420 if not pats and not opts.get('all'):
3422 3421 raise util.Abort(_('no files or directories specified; '
3423 3422 'use --all to revert the whole repo'))
3424 3423
3425 3424 ctx = cmdutil.revsingle(repo, opts.get('rev'))
3426 3425 node = ctx.node()
3427 3426 mf = ctx.manifest()
3428 3427 if node == parent:
3429 3428 pmf = mf
3430 3429 else:
3431 3430 pmf = None
3432 3431
3433 3432 # need all matching names in dirstate and manifest of target rev,
3434 3433 # so have to walk both. do not print errors if files exist in one
3435 3434 # but not other.
3436 3435
3437 3436 names = {}
3438 3437
3439 3438 wlock = repo.wlock()
3440 3439 try:
3441 3440 # walk dirstate.
3442 3441
3443 3442 m = cmdutil.match(repo, pats, opts)
3444 3443 m.bad = lambda x, y: False
3445 3444 for abs in repo.walk(m):
3446 3445 names[abs] = m.rel(abs), m.exact(abs)
3447 3446
3448 3447 # walk target manifest.
3449 3448
3450 3449 def badfn(path, msg):
3451 3450 if path in names:
3452 3451 return
3453 3452 path_ = path + '/'
3454 3453 for f in names:
3455 3454 if f.startswith(path_):
3456 3455 return
3457 3456 ui.warn("%s: %s\n" % (m.rel(path), msg))
3458 3457
3459 3458 m = cmdutil.match(repo, pats, opts)
3460 3459 m.bad = badfn
3461 3460 for abs in repo[node].walk(m):
3462 3461 if abs not in names:
3463 3462 names[abs] = m.rel(abs), m.exact(abs)
3464 3463
3465 3464 m = cmdutil.matchfiles(repo, names)
3466 3465 changes = repo.status(match=m)[:4]
3467 3466 modified, added, removed, deleted = map(set, changes)
3468 3467
3469 3468 # if f is a rename, also revert the source
3470 3469 cwd = repo.getcwd()
3471 3470 for f in added:
3472 3471 src = repo.dirstate.copied(f)
3473 3472 if src and src not in names and repo.dirstate[src] == 'r':
3474 3473 removed.add(src)
3475 3474 names[src] = (repo.pathto(src, cwd), True)
3476 3475
3477 3476 def removeforget(abs):
3478 3477 if repo.dirstate[abs] == 'a':
3479 3478 return _('forgetting %s\n')
3480 3479 return _('removing %s\n')
3481 3480
3482 3481 revert = ([], _('reverting %s\n'))
3483 3482 add = ([], _('adding %s\n'))
3484 3483 remove = ([], removeforget)
3485 3484 undelete = ([], _('undeleting %s\n'))
3486 3485
3487 3486 disptable = (
3488 3487 # dispatch table:
3489 3488 # file state
3490 3489 # action if in target manifest
3491 3490 # action if not in target manifest
3492 3491 # make backup if in target manifest
3493 3492 # make backup if not in target manifest
3494 3493 (modified, revert, remove, True, True),
3495 3494 (added, revert, remove, True, False),
3496 3495 (removed, undelete, None, False, False),
3497 3496 (deleted, revert, remove, False, False),
3498 3497 )
3499 3498
3500 3499 for abs, (rel, exact) in sorted(names.items()):
3501 3500 mfentry = mf.get(abs)
3502 3501 target = repo.wjoin(abs)
3503 3502 def handle(xlist, dobackup):
3504 3503 xlist[0].append(abs)
3505 3504 if (dobackup and not opts.get('no_backup') and
3506 3505 os.path.lexists(target)):
3507 3506 bakname = "%s.orig" % rel
3508 3507 ui.note(_('saving current version of %s as %s\n') %
3509 3508 (rel, bakname))
3510 3509 if not opts.get('dry_run'):
3511 3510 util.rename(target, bakname)
3512 3511 if ui.verbose or not exact:
3513 3512 msg = xlist[1]
3514 3513 if not isinstance(msg, basestring):
3515 3514 msg = msg(abs)
3516 3515 ui.status(msg % rel)
3517 3516 for table, hitlist, misslist, backuphit, backupmiss in disptable:
3518 3517 if abs not in table:
3519 3518 continue
3520 3519 # file has changed in dirstate
3521 3520 if mfentry:
3522 3521 handle(hitlist, backuphit)
3523 3522 elif misslist is not None:
3524 3523 handle(misslist, backupmiss)
3525 3524 break
3526 3525 else:
3527 3526 if abs not in repo.dirstate:
3528 3527 if mfentry:
3529 3528 handle(add, True)
3530 3529 elif exact:
3531 3530 ui.warn(_('file not managed: %s\n') % rel)
3532 3531 continue
3533 3532 # file has not changed in dirstate
3534 3533 if node == parent:
3535 3534 if exact:
3536 3535 ui.warn(_('no changes needed to %s\n') % rel)
3537 3536 continue
3538 3537 if pmf is None:
3539 3538 # only need parent manifest in this unlikely case,
3540 3539 # so do not read by default
3541 3540 pmf = repo[parent].manifest()
3542 3541 if abs in pmf:
3543 3542 if mfentry:
3544 3543 # if version of file is same in parent and target
3545 3544 # manifests, do nothing
3546 3545 if (pmf[abs] != mfentry or
3547 3546 pmf.flags(abs) != mf.flags(abs)):
3548 3547 handle(revert, False)
3549 3548 else:
3550 3549 handle(remove, False)
3551 3550
3552 3551 if not opts.get('dry_run'):
3553 3552 def checkout(f):
3554 3553 fc = ctx[f]
3555 3554 repo.wwrite(f, fc.data(), fc.flags())
3556 3555
3557 3556 audit_path = scmutil.path_auditor(repo.root)
3558 3557 for f in remove[0]:
3559 3558 if repo.dirstate[f] == 'a':
3560 3559 repo.dirstate.forget(f)
3561 3560 continue
3562 3561 audit_path(f)
3563 3562 try:
3564 3563 util.unlinkpath(repo.wjoin(f))
3565 3564 except OSError:
3566 3565 pass
3567 3566 repo.dirstate.remove(f)
3568 3567
3569 3568 normal = None
3570 3569 if node == parent:
3571 3570 # We're reverting to our parent. If possible, we'd like status
3572 3571 # to report the file as clean. We have to use normallookup for
3573 3572 # merges to avoid losing information about merged/dirty files.
3574 3573 if p2 != nullid:
3575 3574 normal = repo.dirstate.normallookup
3576 3575 else:
3577 3576 normal = repo.dirstate.normal
3578 3577 for f in revert[0]:
3579 3578 checkout(f)
3580 3579 if normal:
3581 3580 normal(f)
3582 3581
3583 3582 for f in add[0]:
3584 3583 checkout(f)
3585 3584 repo.dirstate.add(f)
3586 3585
3587 3586 normal = repo.dirstate.normallookup
3588 3587 if node == parent and p2 == nullid:
3589 3588 normal = repo.dirstate.normal
3590 3589 for f in undelete[0]:
3591 3590 checkout(f)
3592 3591 normal(f)
3593 3592
3594 3593 finally:
3595 3594 wlock.release()
3596 3595
3597 3596 def rollback(ui, repo, **opts):
3598 3597 """roll back the last transaction (dangerous)
3599 3598
3600 3599 This command should be used with care. There is only one level of
3601 3600 rollback, and there is no way to undo a rollback. It will also
3602 3601 restore the dirstate at the time of the last transaction, losing
3603 3602 any dirstate changes since that time. This command does not alter
3604 3603 the working directory.
3605 3604
3606 3605 Transactions are used to encapsulate the effects of all commands
3607 3606 that create new changesets or propagate existing changesets into a
3608 3607 repository. For example, the following commands are transactional,
3609 3608 and their effects can be rolled back:
3610 3609
3611 3610 - commit
3612 3611 - import
3613 3612 - pull
3614 3613 - push (with this repository as the destination)
3615 3614 - unbundle
3616 3615
3617 3616 This command is not intended for use on public repositories. Once
3618 3617 changes are visible for pull by other users, rolling a transaction
3619 3618 back locally is ineffective (someone else may already have pulled
3620 3619 the changes). Furthermore, a race is possible with readers of the
3621 3620 repository; for example an in-progress pull from the repository
3622 3621 may fail if a rollback is performed.
3623 3622
3624 3623 Returns 0 on success, 1 if no rollback data is available.
3625 3624 """
3626 3625 return repo.rollback(opts.get('dry_run'))
3627 3626
3628 3627 def root(ui, repo):
3629 3628 """print the root (top) of the current working directory
3630 3629
3631 3630 Print the root directory of the current repository.
3632 3631
3633 3632 Returns 0 on success.
3634 3633 """
3635 3634 ui.write(repo.root + "\n")
3636 3635
3637 3636 def serve(ui, repo, **opts):
3638 3637 """start stand-alone webserver
3639 3638
3640 3639 Start a local HTTP repository browser and pull server. You can use
3641 3640 this for ad-hoc sharing and browsing of repositories. It is
3642 3641 recommended to use a real web server to serve a repository for
3643 3642 longer periods of time.
3644 3643
3645 3644 Please note that the server does not implement access control.
3646 3645 This means that, by default, anybody can read from the server and
3647 3646 nobody can write to it by default. Set the ``web.allow_push``
3648 3647 option to ``*`` to allow everybody to push to the server. You
3649 3648 should use a real web server if you need to authenticate users.
3650 3649
3651 3650 By default, the server logs accesses to stdout and errors to
3652 3651 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3653 3652 files.
3654 3653
3655 3654 To have the server choose a free port number to listen on, specify
3656 3655 a port number of 0; in this case, the server will print the port
3657 3656 number it uses.
3658 3657
3659 3658 Returns 0 on success.
3660 3659 """
3661 3660
3662 3661 if opts["stdio"]:
3663 3662 if repo is None:
3664 3663 raise error.RepoError(_("There is no Mercurial repository here"
3665 3664 " (.hg not found)"))
3666 3665 s = sshserver.sshserver(ui, repo)
3667 3666 s.serve_forever()
3668 3667
3669 3668 # this way we can check if something was given in the command-line
3670 3669 if opts.get('port'):
3671 3670 opts['port'] = util.getport(opts.get('port'))
3672 3671
3673 3672 baseui = repo and repo.baseui or ui
3674 3673 optlist = ("name templates style address port prefix ipv6"
3675 3674 " accesslog errorlog certificate encoding")
3676 3675 for o in optlist.split():
3677 3676 val = opts.get(o, '')
3678 3677 if val in (None, ''): # should check against default options instead
3679 3678 continue
3680 3679 baseui.setconfig("web", o, val)
3681 3680 if repo and repo.ui != baseui:
3682 3681 repo.ui.setconfig("web", o, val)
3683 3682
3684 3683 o = opts.get('web_conf') or opts.get('webdir_conf')
3685 3684 if not o:
3686 3685 if not repo:
3687 3686 raise error.RepoError(_("There is no Mercurial repository"
3688 3687 " here (.hg not found)"))
3689 3688 o = repo.root
3690 3689
3691 3690 app = hgweb.hgweb(o, baseui=ui)
3692 3691
3693 3692 class service(object):
3694 3693 def init(self):
3695 3694 util.set_signal_handler()
3696 3695 self.httpd = hgweb.server.create_server(ui, app)
3697 3696
3698 3697 if opts['port'] and not ui.verbose:
3699 3698 return
3700 3699
3701 3700 if self.httpd.prefix:
3702 3701 prefix = self.httpd.prefix.strip('/') + '/'
3703 3702 else:
3704 3703 prefix = ''
3705 3704
3706 3705 port = ':%d' % self.httpd.port
3707 3706 if port == ':80':
3708 3707 port = ''
3709 3708
3710 3709 bindaddr = self.httpd.addr
3711 3710 if bindaddr == '0.0.0.0':
3712 3711 bindaddr = '*'
3713 3712 elif ':' in bindaddr: # IPv6
3714 3713 bindaddr = '[%s]' % bindaddr
3715 3714
3716 3715 fqaddr = self.httpd.fqaddr
3717 3716 if ':' in fqaddr:
3718 3717 fqaddr = '[%s]' % fqaddr
3719 3718 if opts['port']:
3720 3719 write = ui.status
3721 3720 else:
3722 3721 write = ui.write
3723 3722 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3724 3723 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3725 3724
3726 3725 def run(self):
3727 3726 self.httpd.serve_forever()
3728 3727
3729 3728 service = service()
3730 3729
3731 3730 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3732 3731
3733 3732 def status(ui, repo, *pats, **opts):
3734 3733 """show changed files in the working directory
3735 3734
3736 3735 Show status of files in the repository. If names are given, only
3737 3736 files that match are shown. Files that are clean or ignored or
3738 3737 the source of a copy/move operation, are not listed unless
3739 3738 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3740 3739 Unless options described with "show only ..." are given, the
3741 3740 options -mardu are used.
3742 3741
3743 3742 Option -q/--quiet hides untracked (unknown and ignored) files
3744 3743 unless explicitly requested with -u/--unknown or -i/--ignored.
3745 3744
3746 3745 .. note::
3747 3746 status may appear to disagree with diff if permissions have
3748 3747 changed or a merge has occurred. The standard diff format does
3749 3748 not report permission changes and diff only reports changes
3750 3749 relative to one merge parent.
3751 3750
3752 3751 If one revision is given, it is used as the base revision.
3753 3752 If two revisions are given, the differences between them are
3754 3753 shown. The --change option can also be used as a shortcut to list
3755 3754 the changed files of a revision from its first parent.
3756 3755
3757 3756 The codes used to show the status of files are::
3758 3757
3759 3758 M = modified
3760 3759 A = added
3761 3760 R = removed
3762 3761 C = clean
3763 3762 ! = missing (deleted by non-hg command, but still tracked)
3764 3763 ? = not tracked
3765 3764 I = ignored
3766 3765 = origin of the previous file listed as A (added)
3767 3766
3768 3767 Returns 0 on success.
3769 3768 """
3770 3769
3771 3770 revs = opts.get('rev')
3772 3771 change = opts.get('change')
3773 3772
3774 3773 if revs and change:
3775 3774 msg = _('cannot specify --rev and --change at the same time')
3776 3775 raise util.Abort(msg)
3777 3776 elif change:
3778 3777 node2 = repo.lookup(change)
3779 3778 node1 = repo[node2].p1().node()
3780 3779 else:
3781 3780 node1, node2 = cmdutil.revpair(repo, revs)
3782 3781
3783 3782 cwd = (pats and repo.getcwd()) or ''
3784 3783 end = opts.get('print0') and '\0' or '\n'
3785 3784 copy = {}
3786 3785 states = 'modified added removed deleted unknown ignored clean'.split()
3787 3786 show = [k for k in states if opts.get(k)]
3788 3787 if opts.get('all'):
3789 3788 show += ui.quiet and (states[:4] + ['clean']) or states
3790 3789 if not show:
3791 3790 show = ui.quiet and states[:4] or states[:5]
3792 3791
3793 3792 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3794 3793 'ignored' in show, 'clean' in show, 'unknown' in show,
3795 3794 opts.get('subrepos'))
3796 3795 changestates = zip(states, 'MAR!?IC', stat)
3797 3796
3798 3797 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3799 3798 ctxn = repo[nullid]
3800 3799 ctx1 = repo[node1]
3801 3800 ctx2 = repo[node2]
3802 3801 added = stat[1]
3803 3802 if node2 is None:
3804 3803 added = stat[0] + stat[1] # merged?
3805 3804
3806 3805 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3807 3806 if k in added:
3808 3807 copy[k] = v
3809 3808 elif v in added:
3810 3809 copy[v] = k
3811 3810
3812 3811 for state, char, files in changestates:
3813 3812 if state in show:
3814 3813 format = "%s %%s%s" % (char, end)
3815 3814 if opts.get('no_status'):
3816 3815 format = "%%s%s" % end
3817 3816
3818 3817 for f in files:
3819 3818 ui.write(format % repo.pathto(f, cwd),
3820 3819 label='status.' + state)
3821 3820 if f in copy:
3822 3821 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3823 3822 label='status.copied')
3824 3823
3825 3824 def summary(ui, repo, **opts):
3826 3825 """summarize working directory state
3827 3826
3828 3827 This generates a brief summary of the working directory state,
3829 3828 including parents, branch, commit status, and available updates.
3830 3829
3831 3830 With the --remote option, this will check the default paths for
3832 3831 incoming and outgoing changes. This can be time-consuming.
3833 3832
3834 3833 Returns 0 on success.
3835 3834 """
3836 3835
3837 3836 ctx = repo[None]
3838 3837 parents = ctx.parents()
3839 3838 pnode = parents[0].node()
3840 3839
3841 3840 for p in parents:
3842 3841 # label with log.changeset (instead of log.parent) since this
3843 3842 # shows a working directory parent *changeset*:
3844 3843 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3845 3844 label='log.changeset')
3846 3845 ui.write(' '.join(p.tags()), label='log.tag')
3847 3846 if p.bookmarks():
3848 3847 ui.write(' ' + ' '.join(p.bookmarks()), label='log.bookmark')
3849 3848 if p.rev() == -1:
3850 3849 if not len(repo):
3851 3850 ui.write(_(' (empty repository)'))
3852 3851 else:
3853 3852 ui.write(_(' (no revision checked out)'))
3854 3853 ui.write('\n')
3855 3854 if p.description():
3856 3855 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3857 3856 label='log.summary')
3858 3857
3859 3858 branch = ctx.branch()
3860 3859 bheads = repo.branchheads(branch)
3861 3860 m = _('branch: %s\n') % branch
3862 3861 if branch != 'default':
3863 3862 ui.write(m, label='log.branch')
3864 3863 else:
3865 3864 ui.status(m, label='log.branch')
3866 3865
3867 3866 st = list(repo.status(unknown=True))[:6]
3868 3867
3869 3868 c = repo.dirstate.copies()
3870 3869 copied, renamed = [], []
3871 3870 for d, s in c.iteritems():
3872 3871 if s in st[2]:
3873 3872 st[2].remove(s)
3874 3873 renamed.append(d)
3875 3874 else:
3876 3875 copied.append(d)
3877 3876 if d in st[1]:
3878 3877 st[1].remove(d)
3879 3878 st.insert(3, renamed)
3880 3879 st.insert(4, copied)
3881 3880
3882 3881 ms = mergemod.mergestate(repo)
3883 3882 st.append([f for f in ms if ms[f] == 'u'])
3884 3883
3885 3884 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3886 3885 st.append(subs)
3887 3886
3888 3887 labels = [ui.label(_('%d modified'), 'status.modified'),
3889 3888 ui.label(_('%d added'), 'status.added'),
3890 3889 ui.label(_('%d removed'), 'status.removed'),
3891 3890 ui.label(_('%d renamed'), 'status.copied'),
3892 3891 ui.label(_('%d copied'), 'status.copied'),
3893 3892 ui.label(_('%d deleted'), 'status.deleted'),
3894 3893 ui.label(_('%d unknown'), 'status.unknown'),
3895 3894 ui.label(_('%d ignored'), 'status.ignored'),
3896 3895 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3897 3896 ui.label(_('%d subrepos'), 'status.modified')]
3898 3897 t = []
3899 3898 for s, l in zip(st, labels):
3900 3899 if s:
3901 3900 t.append(l % len(s))
3902 3901
3903 3902 t = ', '.join(t)
3904 3903 cleanworkdir = False
3905 3904
3906 3905 if len(parents) > 1:
3907 3906 t += _(' (merge)')
3908 3907 elif branch != parents[0].branch():
3909 3908 t += _(' (new branch)')
3910 3909 elif (parents[0].extra().get('close') and
3911 3910 pnode in repo.branchheads(branch, closed=True)):
3912 3911 t += _(' (head closed)')
3913 3912 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
3914 3913 t += _(' (clean)')
3915 3914 cleanworkdir = True
3916 3915 elif pnode not in bheads:
3917 3916 t += _(' (new branch head)')
3918 3917
3919 3918 if cleanworkdir:
3920 3919 ui.status(_('commit: %s\n') % t.strip())
3921 3920 else:
3922 3921 ui.write(_('commit: %s\n') % t.strip())
3923 3922
3924 3923 # all ancestors of branch heads - all ancestors of parent = new csets
3925 3924 new = [0] * len(repo)
3926 3925 cl = repo.changelog
3927 3926 for a in [cl.rev(n) for n in bheads]:
3928 3927 new[a] = 1
3929 3928 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3930 3929 new[a] = 1
3931 3930 for a in [p.rev() for p in parents]:
3932 3931 if a >= 0:
3933 3932 new[a] = 0
3934 3933 for a in cl.ancestors(*[p.rev() for p in parents]):
3935 3934 new[a] = 0
3936 3935 new = sum(new)
3937 3936
3938 3937 if new == 0:
3939 3938 ui.status(_('update: (current)\n'))
3940 3939 elif pnode not in bheads:
3941 3940 ui.write(_('update: %d new changesets (update)\n') % new)
3942 3941 else:
3943 3942 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3944 3943 (new, len(bheads)))
3945 3944
3946 3945 if opts.get('remote'):
3947 3946 t = []
3948 3947 source, branches = hg.parseurl(ui.expandpath('default'))
3949 3948 other = hg.repository(hg.remoteui(repo, {}), source)
3950 3949 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3951 3950 ui.debug('comparing with %s\n' % url.hidepassword(source))
3952 3951 repo.ui.pushbuffer()
3953 3952 common, incoming, rheads = discovery.findcommonincoming(repo, other)
3954 3953 repo.ui.popbuffer()
3955 3954 if incoming:
3956 3955 t.append(_('1 or more incoming'))
3957 3956
3958 3957 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3959 3958 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3960 3959 other = hg.repository(hg.remoteui(repo, {}), dest)
3961 3960 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3962 3961 repo.ui.pushbuffer()
3963 3962 o = discovery.findoutgoing(repo, other)
3964 3963 repo.ui.popbuffer()
3965 3964 o = repo.changelog.nodesbetween(o, None)[0]
3966 3965 if o:
3967 3966 t.append(_('%d outgoing') % len(o))
3968 3967 if 'bookmarks' in other.listkeys('namespaces'):
3969 3968 lmarks = repo.listkeys('bookmarks')
3970 3969 rmarks = other.listkeys('bookmarks')
3971 3970 diff = set(rmarks) - set(lmarks)
3972 3971 if len(diff) > 0:
3973 3972 t.append(_('%d incoming bookmarks') % len(diff))
3974 3973 diff = set(lmarks) - set(rmarks)
3975 3974 if len(diff) > 0:
3976 3975 t.append(_('%d outgoing bookmarks') % len(diff))
3977 3976
3978 3977 if t:
3979 3978 ui.write(_('remote: %s\n') % (', '.join(t)))
3980 3979 else:
3981 3980 ui.status(_('remote: (synced)\n'))
3982 3981
3983 3982 def tag(ui, repo, name1, *names, **opts):
3984 3983 """add one or more tags for the current or given revision
3985 3984
3986 3985 Name a particular revision using <name>.
3987 3986
3988 3987 Tags are used to name particular revisions of the repository and are
3989 3988 very useful to compare different revisions, to go back to significant
3990 3989 earlier versions or to mark branch points as releases, etc. Changing
3991 3990 an existing tag is normally disallowed; use -f/--force to override.
3992 3991
3993 3992 If no revision is given, the parent of the working directory is
3994 3993 used, or tip if no revision is checked out.
3995 3994
3996 3995 To facilitate version control, distribution, and merging of tags,
3997 3996 they are stored as a file named ".hgtags" which is managed similarly
3998 3997 to other project files and can be hand-edited if necessary. This
3999 3998 also means that tagging creates a new commit. The file
4000 3999 ".hg/localtags" is used for local tags (not shared among
4001 4000 repositories).
4002 4001
4003 4002 Tag commits are usually made at the head of a branch. If the parent
4004 4003 of the working directory is not a branch head, :hg:`tag` aborts; use
4005 4004 -f/--force to force the tag commit to be based on a non-head
4006 4005 changeset.
4007 4006
4008 4007 See :hg:`help dates` for a list of formats valid for -d/--date.
4009 4008
4010 4009 Since tag names have priority over branch names during revision
4011 4010 lookup, using an existing branch name as a tag name is discouraged.
4012 4011
4013 4012 Returns 0 on success.
4014 4013 """
4015 4014
4016 4015 rev_ = "."
4017 4016 names = [t.strip() for t in (name1,) + names]
4018 4017 if len(names) != len(set(names)):
4019 4018 raise util.Abort(_('tag names must be unique'))
4020 4019 for n in names:
4021 4020 if n in ['tip', '.', 'null']:
4022 4021 raise util.Abort(_('the name \'%s\' is reserved') % n)
4023 4022 if not n:
4024 4023 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
4025 4024 if opts.get('rev') and opts.get('remove'):
4026 4025 raise util.Abort(_("--rev and --remove are incompatible"))
4027 4026 if opts.get('rev'):
4028 4027 rev_ = opts['rev']
4029 4028 message = opts.get('message')
4030 4029 if opts.get('remove'):
4031 4030 expectedtype = opts.get('local') and 'local' or 'global'
4032 4031 for n in names:
4033 4032 if not repo.tagtype(n):
4034 4033 raise util.Abort(_('tag \'%s\' does not exist') % n)
4035 4034 if repo.tagtype(n) != expectedtype:
4036 4035 if expectedtype == 'global':
4037 4036 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
4038 4037 else:
4039 4038 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
4040 4039 rev_ = nullid
4041 4040 if not message:
4042 4041 # we don't translate commit messages
4043 4042 message = 'Removed tag %s' % ', '.join(names)
4044 4043 elif not opts.get('force'):
4045 4044 for n in names:
4046 4045 if n in repo.tags():
4047 4046 raise util.Abort(_('tag \'%s\' already exists '
4048 4047 '(use -f to force)') % n)
4049 4048 if not opts.get('local'):
4050 4049 p1, p2 = repo.dirstate.parents()
4051 4050 if p2 != nullid:
4052 4051 raise util.Abort(_('uncommitted merge'))
4053 4052 bheads = repo.branchheads()
4054 4053 if not opts.get('force') and bheads and p1 not in bheads:
4055 4054 raise util.Abort(_('not at a branch head (use -f to force)'))
4056 4055 r = cmdutil.revsingle(repo, rev_).node()
4057 4056
4058 4057 if not message:
4059 4058 # we don't translate commit messages
4060 4059 message = ('Added tag %s for changeset %s' %
4061 4060 (', '.join(names), short(r)))
4062 4061
4063 4062 date = opts.get('date')
4064 4063 if date:
4065 4064 date = util.parsedate(date)
4066 4065
4067 4066 if opts.get('edit'):
4068 4067 message = ui.edit(message, ui.username())
4069 4068
4070 4069 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
4071 4070
4072 4071 def tags(ui, repo):
4073 4072 """list repository tags
4074 4073
4075 4074 This lists both regular and local tags. When the -v/--verbose
4076 4075 switch is used, a third column "local" is printed for local tags.
4077 4076
4078 4077 Returns 0 on success.
4079 4078 """
4080 4079
4081 4080 hexfunc = ui.debugflag and hex or short
4082 4081 tagtype = ""
4083 4082
4084 4083 for t, n in reversed(repo.tagslist()):
4085 4084 if ui.quiet:
4086 4085 ui.write("%s\n" % t)
4087 4086 continue
4088 4087
4089 4088 hn = hexfunc(n)
4090 4089 r = "%5d:%s" % (repo.changelog.rev(n), hn)
4091 4090 spaces = " " * (30 - encoding.colwidth(t))
4092 4091
4093 4092 if ui.verbose:
4094 4093 if repo.tagtype(t) == 'local':
4095 4094 tagtype = " local"
4096 4095 else:
4097 4096 tagtype = ""
4098 4097 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
4099 4098
4100 4099 def tip(ui, repo, **opts):
4101 4100 """show the tip revision
4102 4101
4103 4102 The tip revision (usually just called the tip) is the changeset
4104 4103 most recently added to the repository (and therefore the most
4105 4104 recently changed head).
4106 4105
4107 4106 If you have just made a commit, that commit will be the tip. If
4108 4107 you have just pulled changes from another repository, the tip of
4109 4108 that repository becomes the current tip. The "tip" tag is special
4110 4109 and cannot be renamed or assigned to a different changeset.
4111 4110
4112 4111 Returns 0 on success.
4113 4112 """
4114 4113 displayer = cmdutil.show_changeset(ui, repo, opts)
4115 4114 displayer.show(repo[len(repo) - 1])
4116 4115 displayer.close()
4117 4116
4118 4117 def unbundle(ui, repo, fname1, *fnames, **opts):
4119 4118 """apply one or more changegroup files
4120 4119
4121 4120 Apply one or more compressed changegroup files generated by the
4122 4121 bundle command.
4123 4122
4124 4123 Returns 0 on success, 1 if an update has unresolved files.
4125 4124 """
4126 4125 fnames = (fname1,) + fnames
4127 4126
4128 4127 lock = repo.lock()
4129 4128 wc = repo['.']
4130 4129 try:
4131 4130 for fname in fnames:
4132 4131 f = url.open(ui, fname)
4133 4132 gen = changegroup.readbundle(f, fname)
4134 4133 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
4135 4134 lock=lock)
4136 4135 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
4137 4136 finally:
4138 4137 lock.release()
4139 4138 return postincoming(ui, repo, modheads, opts.get('update'), None)
4140 4139
4141 4140 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
4142 4141 """update working directory (or switch revisions)
4143 4142
4144 4143 Update the repository's working directory to the specified
4145 4144 changeset. If no changeset is specified, update to the tip of the
4146 4145 current named branch.
4147 4146
4148 4147 If the changeset is not a descendant of the working directory's
4149 4148 parent, the update is aborted. With the -c/--check option, the
4150 4149 working directory is checked for uncommitted changes; if none are
4151 4150 found, the working directory is updated to the specified
4152 4151 changeset.
4153 4152
4154 4153 The following rules apply when the working directory contains
4155 4154 uncommitted changes:
4156 4155
4157 4156 1. If neither -c/--check nor -C/--clean is specified, and if
4158 4157 the requested changeset is an ancestor or descendant of
4159 4158 the working directory's parent, the uncommitted changes
4160 4159 are merged into the requested changeset and the merged
4161 4160 result is left uncommitted. If the requested changeset is
4162 4161 not an ancestor or descendant (that is, it is on another
4163 4162 branch), the update is aborted and the uncommitted changes
4164 4163 are preserved.
4165 4164
4166 4165 2. With the -c/--check option, the update is aborted and the
4167 4166 uncommitted changes are preserved.
4168 4167
4169 4168 3. With the -C/--clean option, uncommitted changes are discarded and
4170 4169 the working directory is updated to the requested changeset.
4171 4170
4172 4171 Use null as the changeset to remove the working directory (like
4173 4172 :hg:`clone -U`).
4174 4173
4175 4174 If you want to update just one file to an older changeset, use
4176 4175 :hg:`revert`.
4177 4176
4178 4177 See :hg:`help dates` for a list of formats valid for -d/--date.
4179 4178
4180 4179 Returns 0 on success, 1 if there are unresolved files.
4181 4180 """
4182 4181 if rev and node:
4183 4182 raise util.Abort(_("please specify just one revision"))
4184 4183
4185 4184 if rev is None or rev == '':
4186 4185 rev = node
4187 4186
4188 4187 # if we defined a bookmark, we have to remember the original bookmark name
4189 4188 brev = rev
4190 4189 rev = cmdutil.revsingle(repo, rev, rev).rev()
4191 4190
4192 4191 if check and clean:
4193 4192 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
4194 4193
4195 4194 if check:
4196 4195 # we could use dirty() but we can ignore merge and branch trivia
4197 4196 c = repo[None]
4198 4197 if c.modified() or c.added() or c.removed():
4199 4198 raise util.Abort(_("uncommitted local changes"))
4200 4199
4201 4200 if date:
4202 4201 if rev is not None:
4203 4202 raise util.Abort(_("you can't specify a revision and a date"))
4204 4203 rev = cmdutil.finddate(ui, repo, date)
4205 4204
4206 4205 if clean or check:
4207 4206 ret = hg.clean(repo, rev)
4208 4207 else:
4209 4208 ret = hg.update(repo, rev)
4210 4209
4211 4210 if brev in repo._bookmarks:
4212 4211 bookmarks.setcurrent(repo, brev)
4213 4212
4214 4213 return ret
4215 4214
4216 4215 def verify(ui, repo):
4217 4216 """verify the integrity of the repository
4218 4217
4219 4218 Verify the integrity of the current repository.
4220 4219
4221 4220 This will perform an extensive check of the repository's
4222 4221 integrity, validating the hashes and checksums of each entry in
4223 4222 the changelog, manifest, and tracked files, as well as the
4224 4223 integrity of their crosslinks and indices.
4225 4224
4226 4225 Returns 0 on success, 1 if errors are encountered.
4227 4226 """
4228 4227 return hg.verify(repo)
4229 4228
4230 4229 def version_(ui):
4231 4230 """output version and copyright information"""
4232 4231 ui.write(_("Mercurial Distributed SCM (version %s)\n")
4233 4232 % util.version())
4234 4233 ui.status(_(
4235 4234 "(see http://mercurial.selenic.com for more information)\n"
4236 4235 "\nCopyright (C) 2005-2011 Matt Mackall and others\n"
4237 4236 "This is free software; see the source for copying conditions. "
4238 4237 "There is NO\nwarranty; "
4239 4238 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
4240 4239 ))
4241 4240
4242 4241 # Command options and aliases are listed here, alphabetically
4243 4242
4244 4243 globalopts = [
4245 4244 ('R', 'repository', '',
4246 4245 _('repository root directory or name of overlay bundle file'),
4247 4246 _('REPO')),
4248 4247 ('', 'cwd', '',
4249 4248 _('change working directory'), _('DIR')),
4250 4249 ('y', 'noninteractive', None,
4251 4250 _('do not prompt, assume \'yes\' for any required answers')),
4252 4251 ('q', 'quiet', None, _('suppress output')),
4253 4252 ('v', 'verbose', None, _('enable additional output')),
4254 4253 ('', 'config', [],
4255 4254 _('set/override config option (use \'section.name=value\')'),
4256 4255 _('CONFIG')),
4257 4256 ('', 'debug', None, _('enable debugging output')),
4258 4257 ('', 'debugger', None, _('start debugger')),
4259 4258 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
4260 4259 _('ENCODE')),
4261 4260 ('', 'encodingmode', encoding.encodingmode,
4262 4261 _('set the charset encoding mode'), _('MODE')),
4263 4262 ('', 'traceback', None, _('always print a traceback on exception')),
4264 4263 ('', 'time', None, _('time how long the command takes')),
4265 4264 ('', 'profile', None, _('print command execution profile')),
4266 4265 ('', 'version', None, _('output version information and exit')),
4267 4266 ('h', 'help', None, _('display help and exit')),
4268 4267 ]
4269 4268
4270 4269 dryrunopts = [('n', 'dry-run', None,
4271 4270 _('do not perform actions, just print output'))]
4272 4271
4273 4272 remoteopts = [
4274 4273 ('e', 'ssh', '',
4275 4274 _('specify ssh command to use'), _('CMD')),
4276 4275 ('', 'remotecmd', '',
4277 4276 _('specify hg command to run on the remote side'), _('CMD')),
4278 4277 ('', 'insecure', None,
4279 4278 _('do not verify server certificate (ignoring web.cacerts config)')),
4280 4279 ]
4281 4280
4282 4281 walkopts = [
4283 4282 ('I', 'include', [],
4284 4283 _('include names matching the given patterns'), _('PATTERN')),
4285 4284 ('X', 'exclude', [],
4286 4285 _('exclude names matching the given patterns'), _('PATTERN')),
4287 4286 ]
4288 4287
4289 4288 commitopts = [
4290 4289 ('m', 'message', '',
4291 4290 _('use text as commit message'), _('TEXT')),
4292 4291 ('l', 'logfile', '',
4293 4292 _('read commit message from file'), _('FILE')),
4294 4293 ]
4295 4294
4296 4295 commitopts2 = [
4297 4296 ('d', 'date', '',
4298 4297 _('record the specified date as commit date'), _('DATE')),
4299 4298 ('u', 'user', '',
4300 4299 _('record the specified user as committer'), _('USER')),
4301 4300 ]
4302 4301
4303 4302 templateopts = [
4304 4303 ('', 'style', '',
4305 4304 _('display using template map file'), _('STYLE')),
4306 4305 ('', 'template', '',
4307 4306 _('display with template'), _('TEMPLATE')),
4308 4307 ]
4309 4308
4310 4309 logopts = [
4311 4310 ('p', 'patch', None, _('show patch')),
4312 4311 ('g', 'git', None, _('use git extended diff format')),
4313 4312 ('l', 'limit', '',
4314 4313 _('limit number of changes displayed'), _('NUM')),
4315 4314 ('M', 'no-merges', None, _('do not show merges')),
4316 4315 ('', 'stat', None, _('output diffstat-style summary of changes')),
4317 4316 ] + templateopts
4318 4317
4319 4318 diffopts = [
4320 4319 ('a', 'text', None, _('treat all files as text')),
4321 4320 ('g', 'git', None, _('use git extended diff format')),
4322 4321 ('', 'nodates', None, _('omit dates from diff headers'))
4323 4322 ]
4324 4323
4325 4324 diffopts2 = [
4326 4325 ('p', 'show-function', None, _('show which function each change is in')),
4327 4326 ('', 'reverse', None, _('produce a diff that undoes the changes')),
4328 4327 ('w', 'ignore-all-space', None,
4329 4328 _('ignore white space when comparing lines')),
4330 4329 ('b', 'ignore-space-change', None,
4331 4330 _('ignore changes in the amount of white space')),
4332 4331 ('B', 'ignore-blank-lines', None,
4333 4332 _('ignore changes whose lines are all blank')),
4334 4333 ('U', 'unified', '',
4335 4334 _('number of lines of context to show'), _('NUM')),
4336 4335 ('', 'stat', None, _('output diffstat-style summary of changes')),
4337 4336 ]
4338 4337
4339 4338 similarityopts = [
4340 4339 ('s', 'similarity', '',
4341 4340 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
4342 4341 ]
4343 4342
4344 4343 subrepoopts = [
4345 4344 ('S', 'subrepos', None,
4346 4345 _('recurse into subrepositories'))
4347 4346 ]
4348 4347
4349 4348 table = {
4350 4349 "^add": (add, walkopts + subrepoopts + dryrunopts,
4351 4350 _('[OPTION]... [FILE]...')),
4352 4351 "addremove":
4353 4352 (addremove, similarityopts + walkopts + dryrunopts,
4354 4353 _('[OPTION]... [FILE]...')),
4355 4354 "^annotate|blame":
4356 4355 (annotate,
4357 4356 [('r', 'rev', '',
4358 4357 _('annotate the specified revision'), _('REV')),
4359 4358 ('', 'follow', None,
4360 4359 _('follow copies/renames and list the filename (DEPRECATED)')),
4361 4360 ('', 'no-follow', None, _("don't follow copies and renames")),
4362 4361 ('a', 'text', None, _('treat all files as text')),
4363 4362 ('u', 'user', None, _('list the author (long with -v)')),
4364 4363 ('f', 'file', None, _('list the filename')),
4365 4364 ('d', 'date', None, _('list the date (short with -q)')),
4366 4365 ('n', 'number', None, _('list the revision number (default)')),
4367 4366 ('c', 'changeset', None, _('list the changeset')),
4368 4367 ('l', 'line-number', None,
4369 4368 _('show line number at the first appearance'))
4370 4369 ] + walkopts,
4371 4370 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
4372 4371 "archive":
4373 4372 (archive,
4374 4373 [('', 'no-decode', None, _('do not pass files through decoders')),
4375 4374 ('p', 'prefix', '',
4376 4375 _('directory prefix for files in archive'), _('PREFIX')),
4377 4376 ('r', 'rev', '',
4378 4377 _('revision to distribute'), _('REV')),
4379 4378 ('t', 'type', '',
4380 4379 _('type of distribution to create'), _('TYPE')),
4381 4380 ] + subrepoopts + walkopts,
4382 4381 _('[OPTION]... DEST')),
4383 4382 "backout":
4384 4383 (backout,
4385 4384 [('', 'merge', None,
4386 4385 _('merge with old dirstate parent after backout')),
4387 4386 ('', 'parent', '',
4388 4387 _('parent to choose when backing out merge'), _('REV')),
4389 4388 ('t', 'tool', '',
4390 4389 _('specify merge tool')),
4391 4390 ('r', 'rev', '',
4392 4391 _('revision to backout'), _('REV')),
4393 4392 ] + walkopts + commitopts + commitopts2,
4394 4393 _('[OPTION]... [-r] REV')),
4395 4394 "bisect":
4396 4395 (bisect,
4397 4396 [('r', 'reset', False, _('reset bisect state')),
4398 4397 ('g', 'good', False, _('mark changeset good')),
4399 4398 ('b', 'bad', False, _('mark changeset bad')),
4400 4399 ('s', 'skip', False, _('skip testing changeset')),
4401 4400 ('e', 'extend', False, _('extend the bisect range')),
4402 4401 ('c', 'command', '',
4403 4402 _('use command to check changeset state'), _('CMD')),
4404 4403 ('U', 'noupdate', False, _('do not update to target'))],
4405 4404 _("[-gbsr] [-U] [-c CMD] [REV]")),
4406 4405 "bookmarks":
4407 4406 (bookmark,
4408 4407 [('f', 'force', False, _('force')),
4409 4408 ('r', 'rev', '', _('revision'), _('REV')),
4410 4409 ('d', 'delete', False, _('delete a given bookmark')),
4411 4410 ('m', 'rename', '', _('rename a given bookmark'), _('NAME'))],
4412 4411 _('hg bookmarks [-f] [-d] [-m NAME] [-r REV] [NAME]')),
4413 4412 "branch":
4414 4413 (branch,
4415 4414 [('f', 'force', None,
4416 4415 _('set branch name even if it shadows an existing branch')),
4417 4416 ('C', 'clean', None, _('reset branch name to parent branch name'))],
4418 4417 _('[-fC] [NAME]')),
4419 4418 "branches":
4420 4419 (branches,
4421 4420 [('a', 'active', False,
4422 4421 _('show only branches that have unmerged heads')),
4423 4422 ('c', 'closed', False,
4424 4423 _('show normal and closed branches'))],
4425 4424 _('[-ac]')),
4426 4425 "bundle":
4427 4426 (bundle,
4428 4427 [('f', 'force', None,
4429 4428 _('run even when the destination is unrelated')),
4430 4429 ('r', 'rev', [],
4431 4430 _('a changeset intended to be added to the destination'),
4432 4431 _('REV')),
4433 4432 ('b', 'branch', [],
4434 4433 _('a specific branch you would like to bundle'),
4435 4434 _('BRANCH')),
4436 4435 ('', 'base', [],
4437 4436 _('a base changeset assumed to be available at the destination'),
4438 4437 _('REV')),
4439 4438 ('a', 'all', None, _('bundle all changesets in the repository')),
4440 4439 ('t', 'type', 'bzip2',
4441 4440 _('bundle compression type to use'), _('TYPE')),
4442 4441 ] + remoteopts,
4443 4442 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
4444 4443 "cat":
4445 4444 (cat,
4446 4445 [('o', 'output', '',
4447 4446 _('print output to file with formatted name'), _('FORMAT')),
4448 4447 ('r', 'rev', '',
4449 4448 _('print the given revision'), _('REV')),
4450 4449 ('', 'decode', None, _('apply any matching decode filter')),
4451 4450 ] + walkopts,
4452 4451 _('[OPTION]... FILE...')),
4453 4452 "^clone":
4454 4453 (clone,
4455 4454 [('U', 'noupdate', None,
4456 4455 _('the clone will include an empty working copy (only a repository)')),
4457 4456 ('u', 'updaterev', '',
4458 4457 _('revision, tag or branch to check out'), _('REV')),
4459 4458 ('r', 'rev', [],
4460 4459 _('include the specified changeset'), _('REV')),
4461 4460 ('b', 'branch', [],
4462 4461 _('clone only the specified branch'), _('BRANCH')),
4463 4462 ('', 'pull', None, _('use pull protocol to copy metadata')),
4464 4463 ('', 'uncompressed', None,
4465 4464 _('use uncompressed transfer (fast over LAN)')),
4466 4465 ] + remoteopts,
4467 4466 _('[OPTION]... SOURCE [DEST]')),
4468 4467 "^commit|ci":
4469 4468 (commit,
4470 4469 [('A', 'addremove', None,
4471 4470 _('mark new/missing files as added/removed before committing')),
4472 4471 ('', 'close-branch', None,
4473 4472 _('mark a branch as closed, hiding it from the branch list')),
4474 4473 ] + walkopts + commitopts + commitopts2,
4475 4474 _('[OPTION]... [FILE]...')),
4476 4475 "copy|cp":
4477 4476 (copy,
4478 4477 [('A', 'after', None, _('record a copy that has already occurred')),
4479 4478 ('f', 'force', None,
4480 4479 _('forcibly copy over an existing managed file')),
4481 4480 ] + walkopts + dryrunopts,
4482 4481 _('[OPTION]... [SOURCE]... DEST')),
4483 4482 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
4484 4483 "debugbuilddag":
4485 4484 (debugbuilddag,
4486 4485 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
4487 4486 ('a', 'appended-file', None, _('add single file all revs append to')),
4488 4487 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
4489 4488 ('n', 'new-file', None, _('add new file at each rev')),
4490 4489 ],
4491 4490 _('[OPTION]... TEXT')),
4492 4491 "debugbundle":
4493 4492 (debugbundle,
4494 4493 [('a', 'all', None, _('show all details')),
4495 4494 ],
4496 4495 _('FILE')),
4497 4496 "debugcheckstate": (debugcheckstate, [], ''),
4498 4497 "debugcommands": (debugcommands, [], _('[COMMAND]')),
4499 4498 "debugcomplete":
4500 4499 (debugcomplete,
4501 4500 [('o', 'options', None, _('show the command options'))],
4502 4501 _('[-o] CMD')),
4503 4502 "debugdag":
4504 4503 (debugdag,
4505 4504 [('t', 'tags', None, _('use tags as labels')),
4506 4505 ('b', 'branches', None, _('annotate with branch names')),
4507 4506 ('', 'dots', None, _('use dots for runs')),
4508 4507 ('s', 'spaces', None, _('separate elements by spaces')),
4509 4508 ],
4510 4509 _('[OPTION]... [FILE [REV]...]')),
4511 4510 "debugdate":
4512 4511 (debugdate,
4513 4512 [('e', 'extended', None, _('try extended date formats'))],
4514 4513 _('[-e] DATE [RANGE]')),
4515 4514 "debugdata": (debugdata, [], _('FILE REV')),
4516 4515 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
4517 4516 "debuggetbundle":
4518 4517 (debuggetbundle,
4519 4518 [('H', 'head', [], _('id of head node'), _('ID')),
4520 4519 ('C', 'common', [], _('id of common node'), _('ID')),
4521 4520 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
4522 4521 ],
4523 4522 _('REPO FILE [-H|-C ID]...')),
4524 4523 "debugignore": (debugignore, [], ''),
4525 4524 "debugindex": (debugindex,
4526 4525 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
4527 4526 _('FILE')),
4528 4527 "debugindexdot": (debugindexdot, [], _('FILE')),
4529 4528 "debuginstall": (debuginstall, [], ''),
4530 4529 "debugknown": (debugknown, [], _('REPO ID...')),
4531 4530 "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
4532 4531 "debugrebuildstate":
4533 4532 (debugrebuildstate,
4534 4533 [('r', 'rev', '',
4535 4534 _('revision to rebuild to'), _('REV'))],
4536 4535 _('[-r REV] [REV]')),
4537 4536 "debugrename":
4538 4537 (debugrename,
4539 4538 [('r', 'rev', '',
4540 4539 _('revision to debug'), _('REV'))],
4541 4540 _('[-r REV] FILE')),
4542 4541 "debugrevspec":
4543 4542 (debugrevspec, [], ('REVSPEC')),
4544 4543 "debugsetparents":
4545 4544 (debugsetparents, [], _('REV1 [REV2]')),
4546 4545 "debugstate":
4547 4546 (debugstate,
4548 4547 [('', 'nodates', None, _('do not display the saved mtime')),
4549 4548 ('', 'datesort', None, _('sort by saved mtime'))],
4550 4549 _('[OPTION]...')),
4551 4550 "debugsub":
4552 4551 (debugsub,
4553 4552 [('r', 'rev', '',
4554 4553 _('revision to check'), _('REV'))],
4555 4554 _('[-r REV] [REV]')),
4556 4555 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
4557 4556 "debugwireargs":
4558 4557 (debugwireargs,
4559 4558 [('', 'three', '', 'three'),
4560 4559 ('', 'four', '', 'four'),
4561 4560 ('', 'five', '', 'five'),
4562 4561 ] + remoteopts,
4563 4562 _('REPO [OPTIONS]... [ONE [TWO]]')),
4564 4563 "^diff":
4565 4564 (diff,
4566 4565 [('r', 'rev', [],
4567 4566 _('revision'), _('REV')),
4568 4567 ('c', 'change', '',
4569 4568 _('change made by revision'), _('REV'))
4570 4569 ] + diffopts + diffopts2 + walkopts + subrepoopts,
4571 4570 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
4572 4571 "^export":
4573 4572 (export,
4574 4573 [('o', 'output', '',
4575 4574 _('print output to file with formatted name'), _('FORMAT')),
4576 4575 ('', 'switch-parent', None, _('diff against the second parent')),
4577 4576 ('r', 'rev', [],
4578 4577 _('revisions to export'), _('REV')),
4579 4578 ] + diffopts,
4580 4579 _('[OPTION]... [-o OUTFILESPEC] REV...')),
4581 4580 "^forget":
4582 4581 (forget,
4583 4582 [] + walkopts,
4584 4583 _('[OPTION]... FILE...')),
4585 4584 "grep":
4586 4585 (grep,
4587 4586 [('0', 'print0', None, _('end fields with NUL')),
4588 4587 ('', 'all', None, _('print all revisions that match')),
4589 4588 ('a', 'text', None, _('treat all files as text')),
4590 4589 ('f', 'follow', None,
4591 4590 _('follow changeset history,'
4592 4591 ' or file history across copies and renames')),
4593 4592 ('i', 'ignore-case', None, _('ignore case when matching')),
4594 4593 ('l', 'files-with-matches', None,
4595 4594 _('print only filenames and revisions that match')),
4596 4595 ('n', 'line-number', None, _('print matching line numbers')),
4597 4596 ('r', 'rev', [],
4598 4597 _('only search files changed within revision range'), _('REV')),
4599 4598 ('u', 'user', None, _('list the author (long with -v)')),
4600 4599 ('d', 'date', None, _('list the date (short with -q)')),
4601 4600 ] + walkopts,
4602 4601 _('[OPTION]... PATTERN [FILE]...')),
4603 4602 "heads":
4604 4603 (heads,
4605 4604 [('r', 'rev', '',
4606 4605 _('show only heads which are descendants of STARTREV'),
4607 4606 _('STARTREV')),
4608 4607 ('t', 'topo', False, _('show topological heads only')),
4609 4608 ('a', 'active', False,
4610 4609 _('show active branchheads only (DEPRECATED)')),
4611 4610 ('c', 'closed', False,
4612 4611 _('show normal and closed branch heads')),
4613 4612 ] + templateopts,
4614 4613 _('[-ac] [-r STARTREV] [REV]...')),
4615 4614 "help": (help_, [], _('[TOPIC]')),
4616 4615 "identify|id":
4617 4616 (identify,
4618 4617 [('r', 'rev', '',
4619 4618 _('identify the specified revision'), _('REV')),
4620 4619 ('n', 'num', None, _('show local revision number')),
4621 4620 ('i', 'id', None, _('show global revision id')),
4622 4621 ('b', 'branch', None, _('show branch')),
4623 4622 ('t', 'tags', None, _('show tags')),
4624 4623 ('B', 'bookmarks', None, _('show bookmarks'))],
4625 4624 _('[-nibtB] [-r REV] [SOURCE]')),
4626 4625 "import|patch":
4627 4626 (import_,
4628 4627 [('p', 'strip', 1,
4629 4628 _('directory strip option for patch. This has the same '
4630 4629 'meaning as the corresponding patch option'),
4631 4630 _('NUM')),
4632 4631 ('b', 'base', '',
4633 4632 _('base path'), _('PATH')),
4634 4633 ('f', 'force', None,
4635 4634 _('skip check for outstanding uncommitted changes')),
4636 4635 ('', 'no-commit', None,
4637 4636 _("don't commit, just update the working directory")),
4638 4637 ('', 'exact', None,
4639 4638 _('apply patch to the nodes from which it was generated')),
4640 4639 ('', 'import-branch', None,
4641 4640 _('use any branch information in patch (implied by --exact)'))] +
4642 4641 commitopts + commitopts2 + similarityopts,
4643 4642 _('[OPTION]... PATCH...')),
4644 4643 "incoming|in":
4645 4644 (incoming,
4646 4645 [('f', 'force', None,
4647 4646 _('run even if remote repository is unrelated')),
4648 4647 ('n', 'newest-first', None, _('show newest record first')),
4649 4648 ('', 'bundle', '',
4650 4649 _('file to store the bundles into'), _('FILE')),
4651 4650 ('r', 'rev', [],
4652 4651 _('a remote changeset intended to be added'), _('REV')),
4653 4652 ('B', 'bookmarks', False, _("compare bookmarks")),
4654 4653 ('b', 'branch', [],
4655 4654 _('a specific branch you would like to pull'), _('BRANCH')),
4656 4655 ] + logopts + remoteopts + subrepoopts,
4657 4656 _('[-p] [-n] [-M] [-f] [-r REV]...'
4658 4657 ' [--bundle FILENAME] [SOURCE]')),
4659 4658 "^init":
4660 4659 (init,
4661 4660 remoteopts,
4662 4661 _('[-e CMD] [--remotecmd CMD] [DEST]')),
4663 4662 "locate":
4664 4663 (locate,
4665 4664 [('r', 'rev', '',
4666 4665 _('search the repository as it is in REV'), _('REV')),
4667 4666 ('0', 'print0', None,
4668 4667 _('end filenames with NUL, for use with xargs')),
4669 4668 ('f', 'fullpath', None,
4670 4669 _('print complete paths from the filesystem root')),
4671 4670 ] + walkopts,
4672 4671 _('[OPTION]... [PATTERN]...')),
4673 4672 "^log|history":
4674 4673 (log,
4675 4674 [('f', 'follow', None,
4676 4675 _('follow changeset history,'
4677 4676 ' or file history across copies and renames')),
4678 4677 ('', 'follow-first', None,
4679 4678 _('only follow the first parent of merge changesets')),
4680 4679 ('d', 'date', '',
4681 4680 _('show revisions matching date spec'), _('DATE')),
4682 4681 ('C', 'copies', None, _('show copied files')),
4683 4682 ('k', 'keyword', [],
4684 4683 _('do case-insensitive search for a given text'), _('TEXT')),
4685 4684 ('r', 'rev', [],
4686 4685 _('show the specified revision or range'), _('REV')),
4687 4686 ('', 'removed', None, _('include revisions where files were removed')),
4688 4687 ('m', 'only-merges', None, _('show only merges')),
4689 4688 ('u', 'user', [],
4690 4689 _('revisions committed by user'), _('USER')),
4691 4690 ('', 'only-branch', [],
4692 4691 _('show only changesets within the given named branch (DEPRECATED)'),
4693 4692 _('BRANCH')),
4694 4693 ('b', 'branch', [],
4695 4694 _('show changesets within the given named branch'), _('BRANCH')),
4696 4695 ('P', 'prune', [],
4697 4696 _('do not display revision or any of its ancestors'), _('REV')),
4698 4697 ] + logopts + walkopts,
4699 4698 _('[OPTION]... [FILE]')),
4700 4699 "manifest":
4701 4700 (manifest,
4702 4701 [('r', 'rev', '',
4703 4702 _('revision to display'), _('REV'))],
4704 4703 _('[-r REV]')),
4705 4704 "^merge":
4706 4705 (merge,
4707 4706 [('f', 'force', None, _('force a merge with outstanding changes')),
4708 4707 ('t', 'tool', '', _('specify merge tool')),
4709 4708 ('r', 'rev', '',
4710 4709 _('revision to merge'), _('REV')),
4711 4710 ('P', 'preview', None,
4712 4711 _('review revisions to merge (no merge is performed)'))],
4713 4712 _('[-P] [-f] [[-r] REV]')),
4714 4713 "outgoing|out":
4715 4714 (outgoing,
4716 4715 [('f', 'force', None,
4717 4716 _('run even when the destination is unrelated')),
4718 4717 ('r', 'rev', [],
4719 4718 _('a changeset intended to be included in the destination'),
4720 4719 _('REV')),
4721 4720 ('n', 'newest-first', None, _('show newest record first')),
4722 4721 ('B', 'bookmarks', False, _("compare bookmarks")),
4723 4722 ('b', 'branch', [],
4724 4723 _('a specific branch you would like to push'), _('BRANCH')),
4725 4724 ] + logopts + remoteopts + subrepoopts,
4726 4725 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
4727 4726 "parents":
4728 4727 (parents,
4729 4728 [('r', 'rev', '',
4730 4729 _('show parents of the specified revision'), _('REV')),
4731 4730 ] + templateopts,
4732 4731 _('[-r REV] [FILE]')),
4733 4732 "paths": (paths, [], _('[NAME]')),
4734 4733 "^pull":
4735 4734 (pull,
4736 4735 [('u', 'update', None,
4737 4736 _('update to new branch head if changesets were pulled')),
4738 4737 ('f', 'force', None,
4739 4738 _('run even when remote repository is unrelated')),
4740 4739 ('r', 'rev', [],
4741 4740 _('a remote changeset intended to be added'), _('REV')),
4742 4741 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4743 4742 ('b', 'branch', [],
4744 4743 _('a specific branch you would like to pull'), _('BRANCH')),
4745 4744 ] + remoteopts,
4746 4745 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
4747 4746 "^push":
4748 4747 (push,
4749 4748 [('f', 'force', None, _('force push')),
4750 4749 ('r', 'rev', [],
4751 4750 _('a changeset intended to be included in the destination'),
4752 4751 _('REV')),
4753 4752 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4754 4753 ('b', 'branch', [],
4755 4754 _('a specific branch you would like to push'), _('BRANCH')),
4756 4755 ('', 'new-branch', False, _('allow pushing a new branch')),
4757 4756 ] + remoteopts,
4758 4757 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
4759 4758 "recover": (recover, []),
4760 4759 "^remove|rm":
4761 4760 (remove,
4762 4761 [('A', 'after', None, _('record delete for missing files')),
4763 4762 ('f', 'force', None,
4764 4763 _('remove (and delete) file even if added or modified')),
4765 4764 ] + walkopts,
4766 4765 _('[OPTION]... FILE...')),
4767 4766 "rename|move|mv":
4768 4767 (rename,
4769 4768 [('A', 'after', None, _('record a rename that has already occurred')),
4770 4769 ('f', 'force', None,
4771 4770 _('forcibly copy over an existing managed file')),
4772 4771 ] + walkopts + dryrunopts,
4773 4772 _('[OPTION]... SOURCE... DEST')),
4774 4773 "resolve":
4775 4774 (resolve,
4776 4775 [('a', 'all', None, _('select all unresolved files')),
4777 4776 ('l', 'list', None, _('list state of files needing merge')),
4778 4777 ('m', 'mark', None, _('mark files as resolved')),
4779 4778 ('u', 'unmark', None, _('mark files as unresolved')),
4780 4779 ('t', 'tool', '', _('specify merge tool')),
4781 4780 ('n', 'no-status', None, _('hide status prefix'))]
4782 4781 + walkopts,
4783 4782 _('[OPTION]... [FILE]...')),
4784 4783 "revert":
4785 4784 (revert,
4786 4785 [('a', 'all', None, _('revert all changes when no arguments given')),
4787 4786 ('d', 'date', '',
4788 4787 _('tipmost revision matching date'), _('DATE')),
4789 4788 ('r', 'rev', '',
4790 4789 _('revert to the specified revision'), _('REV')),
4791 4790 ('', 'no-backup', None, _('do not save backup copies of files')),
4792 4791 ] + walkopts + dryrunopts,
4793 4792 _('[OPTION]... [-r REV] [NAME]...')),
4794 4793 "rollback": (rollback, dryrunopts),
4795 4794 "root": (root, []),
4796 4795 "^serve":
4797 4796 (serve,
4798 4797 [('A', 'accesslog', '',
4799 4798 _('name of access log file to write to'), _('FILE')),
4800 4799 ('d', 'daemon', None, _('run server in background')),
4801 4800 ('', 'daemon-pipefds', '',
4802 4801 _('used internally by daemon mode'), _('NUM')),
4803 4802 ('E', 'errorlog', '',
4804 4803 _('name of error log file to write to'), _('FILE')),
4805 4804 # use string type, then we can check if something was passed
4806 4805 ('p', 'port', '',
4807 4806 _('port to listen on (default: 8000)'), _('PORT')),
4808 4807 ('a', 'address', '',
4809 4808 _('address to listen on (default: all interfaces)'), _('ADDR')),
4810 4809 ('', 'prefix', '',
4811 4810 _('prefix path to serve from (default: server root)'), _('PREFIX')),
4812 4811 ('n', 'name', '',
4813 4812 _('name to show in web pages (default: working directory)'),
4814 4813 _('NAME')),
4815 4814 ('', 'web-conf', '',
4816 4815 _('name of the hgweb config file (see "hg help hgweb")'),
4817 4816 _('FILE')),
4818 4817 ('', 'webdir-conf', '',
4819 4818 _('name of the hgweb config file (DEPRECATED)'), _('FILE')),
4820 4819 ('', 'pid-file', '',
4821 4820 _('name of file to write process ID to'), _('FILE')),
4822 4821 ('', 'stdio', None, _('for remote clients')),
4823 4822 ('t', 'templates', '',
4824 4823 _('web templates to use'), _('TEMPLATE')),
4825 4824 ('', 'style', '',
4826 4825 _('template style to use'), _('STYLE')),
4827 4826 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4828 4827 ('', 'certificate', '',
4829 4828 _('SSL certificate file'), _('FILE'))],
4830 4829 _('[OPTION]...')),
4831 4830 "showconfig|debugconfig":
4832 4831 (showconfig,
4833 4832 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4834 4833 _('[-u] [NAME]...')),
4835 4834 "^summary|sum":
4836 4835 (summary,
4837 4836 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4838 4837 "^status|st":
4839 4838 (status,
4840 4839 [('A', 'all', None, _('show status of all files')),
4841 4840 ('m', 'modified', None, _('show only modified files')),
4842 4841 ('a', 'added', None, _('show only added files')),
4843 4842 ('r', 'removed', None, _('show only removed files')),
4844 4843 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4845 4844 ('c', 'clean', None, _('show only files without changes')),
4846 4845 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4847 4846 ('i', 'ignored', None, _('show only ignored files')),
4848 4847 ('n', 'no-status', None, _('hide status prefix')),
4849 4848 ('C', 'copies', None, _('show source of copied files')),
4850 4849 ('0', 'print0', None,
4851 4850 _('end filenames with NUL, for use with xargs')),
4852 4851 ('', 'rev', [],
4853 4852 _('show difference from revision'), _('REV')),
4854 4853 ('', 'change', '',
4855 4854 _('list the changed files of a revision'), _('REV')),
4856 4855 ] + walkopts + subrepoopts,
4857 4856 _('[OPTION]... [FILE]...')),
4858 4857 "tag":
4859 4858 (tag,
4860 4859 [('f', 'force', None, _('force tag')),
4861 4860 ('l', 'local', None, _('make the tag local')),
4862 4861 ('r', 'rev', '',
4863 4862 _('revision to tag'), _('REV')),
4864 4863 ('', 'remove', None, _('remove a tag')),
4865 4864 # -l/--local is already there, commitopts cannot be used
4866 4865 ('e', 'edit', None, _('edit commit message')),
4867 4866 ('m', 'message', '',
4868 4867 _('use <text> as commit message'), _('TEXT')),
4869 4868 ] + commitopts2,
4870 4869 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4871 4870 "tags": (tags, [], ''),
4872 4871 "tip":
4873 4872 (tip,
4874 4873 [('p', 'patch', None, _('show patch')),
4875 4874 ('g', 'git', None, _('use git extended diff format')),
4876 4875 ] + templateopts,
4877 4876 _('[-p] [-g]')),
4878 4877 "unbundle":
4879 4878 (unbundle,
4880 4879 [('u', 'update', None,
4881 4880 _('update to new branch head if changesets were unbundled'))],
4882 4881 _('[-u] FILE...')),
4883 4882 "^update|up|checkout|co":
4884 4883 (update,
4885 4884 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4886 4885 ('c', 'check', None,
4887 4886 _('update across branches if no uncommitted changes')),
4888 4887 ('d', 'date', '',
4889 4888 _('tipmost revision matching date'), _('DATE')),
4890 4889 ('r', 'rev', '',
4891 4890 _('revision'), _('REV'))],
4892 4891 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4893 4892 "verify": (verify, []),
4894 4893 "version": (version_, []),
4895 4894 }
4896 4895
4897 4896 norepo = ("clone init version help debugcommands debugcomplete"
4898 4897 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
4899 4898 " debugknown debuggetbundle debugbundle")
4900 4899 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
4901 4900 " debugdata debugindex debugindexdot")
@@ -1,833 +1,831 b''
1 1 #
2 2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
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 import os, mimetypes, re, cgi, copy
9 9 import webutil
10 10 from mercurial import error, encoding, archival, templater, templatefilters
11 11 from mercurial.node import short, hex
12 12 from mercurial.util import binary
13 13 from common import paritygen, staticfile, get_contact, ErrorResponse
14 14 from common import HTTP_OK, HTTP_FORBIDDEN, HTTP_NOT_FOUND
15 15 from mercurial import graphmod
16 16 from mercurial import help as helpmod
17 17 from mercurial.i18n import _
18 18
19 19 # __all__ is populated with the allowed commands. Be sure to add to it if
20 20 # you're adding a new command, or the new command won't work.
21 21
22 22 __all__ = [
23 23 'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev',
24 24 'manifest', 'tags', 'bookmarks', 'branches', 'summary', 'filediff', 'diff',
25 25 'annotate', 'filelog', 'archive', 'static', 'graph', 'help',
26 26 ]
27 27
28 28 def log(web, req, tmpl):
29 29 if 'file' in req.form and req.form['file'][0]:
30 30 return filelog(web, req, tmpl)
31 31 else:
32 32 return changelog(web, req, tmpl)
33 33
34 34 def rawfile(web, req, tmpl):
35 35 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
36 36 if not path:
37 37 content = manifest(web, req, tmpl)
38 38 req.respond(HTTP_OK, web.ctype)
39 39 return content
40 40
41 41 try:
42 42 fctx = webutil.filectx(web.repo, req)
43 43 except error.LookupError, inst:
44 44 try:
45 45 content = manifest(web, req, tmpl)
46 46 req.respond(HTTP_OK, web.ctype)
47 47 return content
48 48 except ErrorResponse:
49 49 raise inst
50 50
51 51 path = fctx.path()
52 52 text = fctx.data()
53 53 mt = mimetypes.guess_type(path)[0]
54 54 if mt is None:
55 55 mt = binary(text) and 'application/octet-stream' or 'text/plain'
56 56 if mt.startswith('text/'):
57 57 mt += '; charset="%s"' % encoding.encoding
58 58
59 59 req.respond(HTTP_OK, mt, path, len(text))
60 60 return [text]
61 61
62 62 def _filerevision(web, tmpl, fctx):
63 63 f = fctx.path()
64 64 text = fctx.data()
65 65 parity = paritygen(web.stripecount)
66 66
67 67 if binary(text):
68 68 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
69 69 text = '(binary:%s)' % mt
70 70
71 71 def lines():
72 72 for lineno, t in enumerate(text.splitlines(True)):
73 73 yield {"line": t,
74 74 "lineid": "l%d" % (lineno + 1),
75 75 "linenumber": "% 6d" % (lineno + 1),
76 76 "parity": parity.next()}
77 77
78 78 return tmpl("filerevision",
79 79 file=f,
80 80 path=webutil.up(f),
81 81 text=lines(),
82 82 rev=fctx.rev(),
83 node=hex(fctx.node()),
83 node=fctx.hex(),
84 84 author=fctx.user(),
85 85 date=fctx.date(),
86 86 desc=fctx.description(),
87 87 branch=webutil.nodebranchnodefault(fctx),
88 88 parent=webutil.parents(fctx),
89 89 child=webutil.children(fctx),
90 90 rename=webutil.renamelink(fctx),
91 91 permissions=fctx.manifest().flags(f))
92 92
93 93 def file(web, req, tmpl):
94 94 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
95 95 if not path:
96 96 return manifest(web, req, tmpl)
97 97 try:
98 98 return _filerevision(web, tmpl, webutil.filectx(web.repo, req))
99 99 except error.LookupError, inst:
100 100 try:
101 101 return manifest(web, req, tmpl)
102 102 except ErrorResponse:
103 103 raise inst
104 104
105 105 def _search(web, req, tmpl):
106 106
107 107 query = req.form['rev'][0]
108 108 revcount = web.maxchanges
109 109 if 'revcount' in req.form:
110 110 revcount = int(req.form.get('revcount', [revcount])[0])
111 111 revcount = max(revcount, 1)
112 112 tmpl.defaults['sessionvars']['revcount'] = revcount
113 113
114 114 lessvars = copy.copy(tmpl.defaults['sessionvars'])
115 115 lessvars['revcount'] = max(revcount / 2, 1)
116 116 lessvars['rev'] = query
117 117 morevars = copy.copy(tmpl.defaults['sessionvars'])
118 118 morevars['revcount'] = revcount * 2
119 119 morevars['rev'] = query
120 120
121 121 def changelist(**map):
122 122 count = 0
123 123 qw = query.lower().split()
124 124
125 125 def revgen():
126 126 for i in xrange(len(web.repo) - 1, 0, -100):
127 127 l = []
128 128 for j in xrange(max(0, i - 100), i + 1):
129 129 ctx = web.repo[j]
130 130 l.append(ctx)
131 131 l.reverse()
132 132 for e in l:
133 133 yield e
134 134
135 135 for ctx in revgen():
136 136 miss = 0
137 137 for q in qw:
138 138 if not (q in ctx.user().lower() or
139 139 q in ctx.description().lower() or
140 140 q in " ".join(ctx.files()).lower()):
141 141 miss = 1
142 142 break
143 143 if miss:
144 144 continue
145 145
146 146 count += 1
147 147 n = ctx.node()
148 148 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
149 149 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
150 150
151 151 yield tmpl('searchentry',
152 152 parity=parity.next(),
153 153 author=ctx.user(),
154 154 parent=webutil.parents(ctx),
155 155 child=webutil.children(ctx),
156 156 changelogtag=showtags,
157 157 desc=ctx.description(),
158 158 date=ctx.date(),
159 159 files=files,
160 160 rev=ctx.rev(),
161 161 node=hex(n),
162 162 tags=webutil.nodetagsdict(web.repo, n),
163 163 bookmarks=webutil.nodebookmarksdict(web.repo, n),
164 164 inbranch=webutil.nodeinbranch(web.repo, ctx),
165 165 branches=webutil.nodebranchdict(web.repo, ctx))
166 166
167 167 if count >= revcount:
168 168 break
169 169
170 170 tip = web.repo['tip']
171 171 parity = paritygen(web.stripecount)
172 172
173 173 return tmpl('search', query=query, node=tip.hex(),
174 174 entries=changelist, archives=web.archivelist("tip"),
175 175 morevars=morevars, lessvars=lessvars)
176 176
177 177 def changelog(web, req, tmpl, shortlog=False):
178 178
179 179 if 'node' in req.form:
180 180 ctx = webutil.changectx(web.repo, req)
181 181 else:
182 182 if 'rev' in req.form:
183 183 hi = req.form['rev'][0]
184 184 else:
185 185 hi = len(web.repo) - 1
186 186 try:
187 187 ctx = web.repo[hi]
188 188 except error.RepoError:
189 189 return _search(web, req, tmpl) # XXX redirect to 404 page?
190 190
191 191 def changelist(limit=0, **map):
192 192 l = [] # build a list in forward order for efficiency
193 193 for i in xrange(start, end):
194 194 ctx = web.repo[i]
195 195 n = ctx.node()
196 196 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
197 197 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
198 198
199 199 l.insert(0, {"parity": parity.next(),
200 200 "author": ctx.user(),
201 201 "parent": webutil.parents(ctx, i - 1),
202 202 "child": webutil.children(ctx, i + 1),
203 203 "changelogtag": showtags,
204 204 "desc": ctx.description(),
205 205 "date": ctx.date(),
206 206 "files": files,
207 207 "rev": i,
208 208 "node": hex(n),
209 209 "tags": webutil.nodetagsdict(web.repo, n),
210 210 "bookmarks": webutil.nodebookmarksdict(web.repo, n),
211 211 "inbranch": webutil.nodeinbranch(web.repo, ctx),
212 212 "branches": webutil.nodebranchdict(web.repo, ctx)
213 213 })
214 214
215 215 if limit > 0:
216 216 l = l[:limit]
217 217
218 218 for e in l:
219 219 yield e
220 220
221 221 revcount = shortlog and web.maxshortchanges or web.maxchanges
222 222 if 'revcount' in req.form:
223 223 revcount = int(req.form.get('revcount', [revcount])[0])
224 224 revcount = max(revcount, 1)
225 225 tmpl.defaults['sessionvars']['revcount'] = revcount
226 226
227 227 lessvars = copy.copy(tmpl.defaults['sessionvars'])
228 228 lessvars['revcount'] = max(revcount / 2, 1)
229 229 morevars = copy.copy(tmpl.defaults['sessionvars'])
230 230 morevars['revcount'] = revcount * 2
231 231
232 232 count = len(web.repo)
233 233 pos = ctx.rev()
234 234 start = max(0, pos - revcount + 1)
235 235 end = min(count, start + revcount)
236 236 pos = end - 1
237 237 parity = paritygen(web.stripecount, offset=start - end)
238 238
239 239 changenav = webutil.revnavgen(pos, revcount, count, web.repo.changectx)
240 240
241 241 return tmpl(shortlog and 'shortlog' or 'changelog', changenav=changenav,
242 node=hex(ctx.node()), rev=pos, changesets=count,
242 node=ctx.hex(), rev=pos, changesets=count,
243 243 entries=lambda **x: changelist(limit=0,**x),
244 244 latestentry=lambda **x: changelist(limit=1,**x),
245 245 archives=web.archivelist("tip"), revcount=revcount,
246 246 morevars=morevars, lessvars=lessvars)
247 247
248 248 def shortlog(web, req, tmpl):
249 249 return changelog(web, req, tmpl, shortlog = True)
250 250
251 251 def changeset(web, req, tmpl):
252 252 ctx = webutil.changectx(web.repo, req)
253 253 showtags = webutil.showtag(web.repo, tmpl, 'changesettag', ctx.node())
254 254 showbookmarks = webutil.showbookmark(web.repo, tmpl, 'changesetbookmark',
255 255 ctx.node())
256 256 showbranch = webutil.nodebranchnodefault(ctx)
257 257
258 258 files = []
259 259 parity = paritygen(web.stripecount)
260 260 for f in ctx.files():
261 261 template = f in ctx and 'filenodelink' or 'filenolink'
262 262 files.append(tmpl(template,
263 263 node=ctx.hex(), file=f,
264 264 parity=parity.next()))
265 265
266 266 parity = paritygen(web.stripecount)
267 267 style = web.config('web', 'style', 'paper')
268 268 if 'style' in req.form:
269 269 style = req.form['style'][0]
270 270
271 271 diffs = webutil.diffs(web.repo, tmpl, ctx, None, parity, style)
272 272 return tmpl('changeset',
273 273 diff=diffs,
274 274 rev=ctx.rev(),
275 275 node=ctx.hex(),
276 276 parent=webutil.parents(ctx),
277 277 child=webutil.children(ctx),
278 278 changesettag=showtags,
279 279 changesetbookmark=showbookmarks,
280 280 changesetbranch=showbranch,
281 281 author=ctx.user(),
282 282 desc=ctx.description(),
283 283 date=ctx.date(),
284 284 files=files,
285 285 archives=web.archivelist(ctx.hex()),
286 286 tags=webutil.nodetagsdict(web.repo, ctx.node()),
287 287 bookmarks=webutil.nodebookmarksdict(web.repo, ctx.node()),
288 288 branch=webutil.nodebranchnodefault(ctx),
289 289 inbranch=webutil.nodeinbranch(web.repo, ctx),
290 290 branches=webutil.nodebranchdict(web.repo, ctx))
291 291
292 292 rev = changeset
293 293
294 294 def manifest(web, req, tmpl):
295 295 ctx = webutil.changectx(web.repo, req)
296 296 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
297 297 mf = ctx.manifest()
298 298 node = ctx.node()
299 299
300 300 files = {}
301 301 dirs = {}
302 302 parity = paritygen(web.stripecount)
303 303
304 304 if path and path[-1] != "/":
305 305 path += "/"
306 306 l = len(path)
307 307 abspath = "/" + path
308 308
309 309 for f, n in mf.iteritems():
310 310 if f[:l] != path:
311 311 continue
312 312 remain = f[l:]
313 313 elements = remain.split('/')
314 314 if len(elements) == 1:
315 315 files[remain] = f
316 316 else:
317 317 h = dirs # need to retain ref to dirs (root)
318 318 for elem in elements[0:-1]:
319 319 if elem not in h:
320 320 h[elem] = {}
321 321 h = h[elem]
322 322 if len(h) > 1:
323 323 break
324 324 h[None] = None # denotes files present
325 325
326 326 if mf and not files and not dirs:
327 327 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
328 328
329 329 def filelist(**map):
330 330 for f in sorted(files):
331 331 full = files[f]
332 332
333 333 fctx = ctx.filectx(full)
334 334 yield {"file": full,
335 335 "parity": parity.next(),
336 336 "basename": f,
337 337 "date": fctx.date(),
338 338 "size": fctx.size(),
339 339 "permissions": mf.flags(full)}
340 340
341 341 def dirlist(**map):
342 342 for d in sorted(dirs):
343 343
344 344 emptydirs = []
345 345 h = dirs[d]
346 346 while isinstance(h, dict) and len(h) == 1:
347 347 k, v = h.items()[0]
348 348 if v:
349 349 emptydirs.append(k)
350 350 h = v
351 351
352 352 path = "%s%s" % (abspath, d)
353 353 yield {"parity": parity.next(),
354 354 "path": path,
355 355 "emptydirs": "/".join(emptydirs),
356 356 "basename": d}
357 357
358 358 return tmpl("manifest",
359 359 rev=ctx.rev(),
360 360 node=hex(node),
361 361 path=abspath,
362 362 up=webutil.up(abspath),
363 363 upparity=parity.next(),
364 364 fentries=filelist,
365 365 dentries=dirlist,
366 366 archives=web.archivelist(hex(node)),
367 367 tags=webutil.nodetagsdict(web.repo, node),
368 368 bookmarks=webutil.nodebookmarksdict(web.repo, node),
369 369 inbranch=webutil.nodeinbranch(web.repo, ctx),
370 370 branches=webutil.nodebranchdict(web.repo, ctx))
371 371
372 372 def tags(web, req, tmpl):
373 373 i = web.repo.tagslist()
374 374 i.reverse()
375 375 parity = paritygen(web.stripecount)
376 376
377 377 def entries(notip=False, limit=0, **map):
378 378 count = 0
379 379 for k, n in i:
380 380 if notip and k == "tip":
381 381 continue
382 382 if limit > 0 and count >= limit:
383 383 continue
384 384 count = count + 1
385 385 yield {"parity": parity.next(),
386 386 "tag": k,
387 387 "date": web.repo[n].date(),
388 388 "node": hex(n)}
389 389
390 390 return tmpl("tags",
391 391 node=hex(web.repo.changelog.tip()),
392 392 entries=lambda **x: entries(False, 0, **x),
393 393 entriesnotip=lambda **x: entries(True, 0, **x),
394 394 latestentry=lambda **x: entries(True, 1, **x))
395 395
396 396 def bookmarks(web, req, tmpl):
397 397 i = web.repo._bookmarks.items()
398 398 parity = paritygen(web.stripecount)
399 399
400 400 def entries(limit=0, **map):
401 401 count = 0
402 402 for k, n in sorted(i):
403 403 if limit > 0 and count >= limit:
404 404 continue
405 405 count = count + 1
406 406 yield {"parity": parity.next(),
407 407 "bookmark": k,
408 408 "date": web.repo[n].date(),
409 409 "node": hex(n)}
410 410
411 411 return tmpl("bookmarks",
412 412 node=hex(web.repo.changelog.tip()),
413 413 entries=lambda **x: entries(0, **x),
414 414 latestentry=lambda **x: entries(1, **x))
415 415
416 416 def branches(web, req, tmpl):
417 417 tips = (web.repo[n] for t, n in web.repo.branchtags().iteritems())
418 418 heads = web.repo.heads()
419 419 parity = paritygen(web.stripecount)
420 420 sortkey = lambda ctx: ('close' not in ctx.extra(), ctx.rev())
421 421
422 422 def entries(limit, **map):
423 423 count = 0
424 424 for ctx in sorted(tips, key=sortkey, reverse=True):
425 425 if limit > 0 and count >= limit:
426 426 return
427 427 count += 1
428 428 if ctx.node() not in heads:
429 429 status = 'inactive'
430 430 elif not web.repo.branchheads(ctx.branch()):
431 431 status = 'closed'
432 432 else:
433 433 status = 'open'
434 434 yield {'parity': parity.next(),
435 435 'branch': ctx.branch(),
436 436 'status': status,
437 437 'node': ctx.hex(),
438 438 'date': ctx.date()}
439 439
440 440 return tmpl('branches', node=hex(web.repo.changelog.tip()),
441 441 entries=lambda **x: entries(0, **x),
442 442 latestentry=lambda **x: entries(1, **x))
443 443
444 444 def summary(web, req, tmpl):
445 445 i = web.repo.tagslist()
446 446 i.reverse()
447 447
448 448 def tagentries(**map):
449 449 parity = paritygen(web.stripecount)
450 450 count = 0
451 451 for k, n in i:
452 452 if k == "tip": # skip tip
453 453 continue
454 454
455 455 count += 1
456 456 if count > 10: # limit to 10 tags
457 457 break
458 458
459 459 yield tmpl("tagentry",
460 460 parity=parity.next(),
461 461 tag=k,
462 462 node=hex(n),
463 463 date=web.repo[n].date())
464 464
465 465 def bookmarks(**map):
466 466 parity = paritygen(web.stripecount)
467 467 b = web.repo._bookmarks.items()
468 468 for k, n in sorted(b)[:10]: # limit to 10 bookmarks
469 469 yield {'parity': parity.next(),
470 470 'bookmark': k,
471 471 'date': web.repo[n].date(),
472 472 'node': hex(n)}
473 473
474 474 def branches(**map):
475 475 parity = paritygen(web.stripecount)
476 476
477 477 b = web.repo.branchtags()
478 478 l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.iteritems()]
479 479 for r, n, t in sorted(l):
480 480 yield {'parity': parity.next(),
481 481 'branch': t,
482 482 'node': hex(n),
483 483 'date': web.repo[n].date()}
484 484
485 485 def changelist(**map):
486 486 parity = paritygen(web.stripecount, offset=start - end)
487 487 l = [] # build a list in forward order for efficiency
488 488 for i in xrange(start, end):
489 489 ctx = web.repo[i]
490 490 n = ctx.node()
491 491 hn = hex(n)
492 492
493 493 l.insert(0, tmpl(
494 494 'shortlogentry',
495 495 parity=parity.next(),
496 496 author=ctx.user(),
497 497 desc=ctx.description(),
498 498 date=ctx.date(),
499 499 rev=i,
500 500 node=hn,
501 501 tags=webutil.nodetagsdict(web.repo, n),
502 502 bookmarks=webutil.nodebookmarksdict(web.repo, n),
503 503 inbranch=webutil.nodeinbranch(web.repo, ctx),
504 504 branches=webutil.nodebranchdict(web.repo, ctx)))
505 505
506 506 yield l
507 507
508 508 tip = web.repo['tip']
509 509 count = len(web.repo)
510 510 start = max(0, count - web.maxchanges)
511 511 end = min(count, start + web.maxchanges)
512 512
513 513 return tmpl("summary",
514 514 desc=web.config("web", "description", "unknown"),
515 515 owner=get_contact(web.config) or "unknown",
516 516 lastchange=tip.date(),
517 517 tags=tagentries,
518 518 bookmarks=bookmarks,
519 519 branches=branches,
520 520 shortlog=changelist,
521 521 node=tip.hex(),
522 522 archives=web.archivelist("tip"))
523 523
524 524 def filediff(web, req, tmpl):
525 525 fctx, ctx = None, None
526 526 try:
527 527 fctx = webutil.filectx(web.repo, req)
528 528 except LookupError:
529 529 ctx = webutil.changectx(web.repo, req)
530 530 path = webutil.cleanpath(web.repo, req.form['file'][0])
531 531 if path not in ctx.files():
532 532 raise
533 533
534 534 if fctx is not None:
535 535 n = fctx.node()
536 536 path = fctx.path()
537 537 else:
538 538 n = ctx.node()
539 539 # path already defined in except clause
540 540
541 541 parity = paritygen(web.stripecount)
542 542 style = web.config('web', 'style', 'paper')
543 543 if 'style' in req.form:
544 544 style = req.form['style'][0]
545 545
546 546 diffs = webutil.diffs(web.repo, tmpl, fctx or ctx, [path], parity, style)
547 547 rename = fctx and webutil.renamelink(fctx) or []
548 548 ctx = fctx and fctx or ctx
549 549 return tmpl("filediff",
550 550 file=path,
551 551 node=hex(n),
552 552 rev=ctx.rev(),
553 553 date=ctx.date(),
554 554 desc=ctx.description(),
555 555 author=ctx.user(),
556 556 rename=rename,
557 557 branch=webutil.nodebranchnodefault(ctx),
558 558 parent=webutil.parents(ctx),
559 559 child=webutil.children(ctx),
560 560 diff=diffs)
561 561
562 562 diff = filediff
563 563
564 564 def annotate(web, req, tmpl):
565 565 fctx = webutil.filectx(web.repo, req)
566 566 f = fctx.path()
567 567 parity = paritygen(web.stripecount)
568 568
569 569 def annotate(**map):
570 570 last = None
571 571 if binary(fctx.data()):
572 572 mt = (mimetypes.guess_type(fctx.path())[0]
573 573 or 'application/octet-stream')
574 574 lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
575 575 '(binary:%s)' % mt)])
576 576 else:
577 577 lines = enumerate(fctx.annotate(follow=True, linenumber=True))
578 578 for lineno, ((f, targetline), l) in lines:
579 579 fnode = f.filenode()
580 580
581 581 if last != fnode:
582 582 last = fnode
583 583
584 584 yield {"parity": parity.next(),
585 "node": hex(f.node()),
585 "node": f.hex(),
586 586 "rev": f.rev(),
587 587 "author": f.user(),
588 588 "desc": f.description(),
589 589 "file": f.path(),
590 590 "targetline": targetline,
591 591 "line": l,
592 592 "lineid": "l%d" % (lineno + 1),
593 593 "linenumber": "% 6d" % (lineno + 1),
594 594 "revdate": f.date()}
595 595
596 596 return tmpl("fileannotate",
597 597 file=f,
598 598 annotate=annotate,
599 599 path=webutil.up(f),
600 600 rev=fctx.rev(),
601 node=hex(fctx.node()),
601 node=fctx.hex(),
602 602 author=fctx.user(),
603 603 date=fctx.date(),
604 604 desc=fctx.description(),
605 605 rename=webutil.renamelink(fctx),
606 606 branch=webutil.nodebranchnodefault(fctx),
607 607 parent=webutil.parents(fctx),
608 608 child=webutil.children(fctx),
609 609 permissions=fctx.manifest().flags(f))
610 610
611 611 def filelog(web, req, tmpl):
612 612
613 613 try:
614 614 fctx = webutil.filectx(web.repo, req)
615 615 f = fctx.path()
616 616 fl = fctx.filelog()
617 617 except error.LookupError:
618 618 f = webutil.cleanpath(web.repo, req.form['file'][0])
619 619 fl = web.repo.file(f)
620 620 numrevs = len(fl)
621 621 if not numrevs: # file doesn't exist at all
622 622 raise
623 623 rev = webutil.changectx(web.repo, req).rev()
624 624 first = fl.linkrev(0)
625 625 if rev < first: # current rev is from before file existed
626 626 raise
627 627 frev = numrevs - 1
628 628 while fl.linkrev(frev) > rev:
629 629 frev -= 1
630 630 fctx = web.repo.filectx(f, fl.linkrev(frev))
631 631
632 632 revcount = web.maxshortchanges
633 633 if 'revcount' in req.form:
634 634 revcount = int(req.form.get('revcount', [revcount])[0])
635 635 revcount = max(revcount, 1)
636 636 tmpl.defaults['sessionvars']['revcount'] = revcount
637 637
638 638 lessvars = copy.copy(tmpl.defaults['sessionvars'])
639 639 lessvars['revcount'] = max(revcount / 2, 1)
640 640 morevars = copy.copy(tmpl.defaults['sessionvars'])
641 641 morevars['revcount'] = revcount * 2
642 642
643 643 count = fctx.filerev() + 1
644 644 start = max(0, fctx.filerev() - revcount + 1) # first rev on this page
645 645 end = min(count, start + revcount) # last rev on this page
646 646 parity = paritygen(web.stripecount, offset=start - end)
647 647
648 648 def entries(limit=0, **map):
649 649 l = []
650 650
651 651 repo = web.repo
652 652 for i in xrange(start, end):
653 653 iterfctx = fctx.filectx(i)
654 654
655 655 l.insert(0, {"parity": parity.next(),
656 656 "filerev": i,
657 657 "file": f,
658 "node": hex(iterfctx.node()),
658 "node": iterfctx.hex(),
659 659 "author": iterfctx.user(),
660 660 "date": iterfctx.date(),
661 661 "rename": webutil.renamelink(iterfctx),
662 662 "parent": webutil.parents(iterfctx),
663 663 "child": webutil.children(iterfctx),
664 664 "desc": iterfctx.description(),
665 665 "tags": webutil.nodetagsdict(repo, iterfctx.node()),
666 666 "bookmarks": webutil.nodebookmarksdict(
667 667 repo, iterfctx.node()),
668 668 "branch": webutil.nodebranchnodefault(iterfctx),
669 669 "inbranch": webutil.nodeinbranch(repo, iterfctx),
670 670 "branches": webutil.nodebranchdict(repo, iterfctx)})
671 671
672 672 if limit > 0:
673 673 l = l[:limit]
674 674
675 675 for e in l:
676 676 yield e
677 677
678 678 nodefunc = lambda x: fctx.filectx(fileid=x)
679 679 nav = webutil.revnavgen(end - 1, revcount, count, nodefunc)
680 return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
680 return tmpl("filelog", file=f, node=fctx.hex(), nav=nav,
681 681 entries=lambda **x: entries(limit=0, **x),
682 682 latestentry=lambda **x: entries(limit=1, **x),
683 683 revcount=revcount, morevars=morevars, lessvars=lessvars)
684 684
685 685 def archive(web, req, tmpl):
686 686 type_ = req.form.get('type', [None])[0]
687 687 allowed = web.configlist("web", "allow_archive")
688 688 key = req.form['node'][0]
689 689
690 690 if type_ not in web.archives:
691 691 msg = 'Unsupported archive type: %s' % type_
692 692 raise ErrorResponse(HTTP_NOT_FOUND, msg)
693 693
694 694 if not ((type_ in allowed or
695 695 web.configbool("web", "allow" + type_, False))):
696 696 msg = 'Archive type not allowed: %s' % type_
697 697 raise ErrorResponse(HTTP_FORBIDDEN, msg)
698 698
699 699 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
700 700 cnode = web.repo.lookup(key)
701 701 arch_version = key
702 702 if cnode == key or key == 'tip':
703 703 arch_version = short(cnode)
704 704 name = "%s-%s" % (reponame, arch_version)
705 705 mimetype, artype, extension, encoding = web.archive_specs[type_]
706 706 headers = [
707 707 ('Content-Type', mimetype),
708 708 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
709 709 ]
710 710 if encoding:
711 711 headers.append(('Content-Encoding', encoding))
712 712 req.header(headers)
713 713 req.respond(HTTP_OK)
714 714 archival.archive(web.repo, req, cnode, artype, prefix=name)
715 715 return []
716 716
717 717
718 718 def static(web, req, tmpl):
719 719 fname = req.form['file'][0]
720 720 # a repo owner may set web.static in .hg/hgrc to get any file
721 721 # readable by the user running the CGI script
722 722 static = web.config("web", "static", None, untrusted=False)
723 723 if not static:
724 724 tp = web.templatepath or templater.templatepath()
725 725 if isinstance(tp, str):
726 726 tp = [tp]
727 727 static = [os.path.join(p, 'static') for p in tp]
728 728 return [staticfile(static, fname, req)]
729 729
730 730 def graph(web, req, tmpl):
731 731
732 732 rev = webutil.changectx(web.repo, req).rev()
733 733 bg_height = 39
734 734 revcount = web.maxshortchanges
735 735 if 'revcount' in req.form:
736 736 revcount = int(req.form.get('revcount', [revcount])[0])
737 737 revcount = max(revcount, 1)
738 738 tmpl.defaults['sessionvars']['revcount'] = revcount
739 739
740 740 lessvars = copy.copy(tmpl.defaults['sessionvars'])
741 741 lessvars['revcount'] = max(revcount / 2, 1)
742 742 morevars = copy.copy(tmpl.defaults['sessionvars'])
743 743 morevars['revcount'] = revcount * 2
744 744
745 745 max_rev = len(web.repo) - 1
746 746 revcount = min(max_rev, revcount)
747 747 revnode = web.repo.changelog.node(rev)
748 748 revnode_hex = hex(revnode)
749 749 uprev = min(max_rev, rev + revcount)
750 750 downrev = max(0, rev - revcount)
751 751 count = len(web.repo)
752 752 changenav = webutil.revnavgen(rev, revcount, count, web.repo.changectx)
753 753 startrev = rev
754 754 # if starting revision is less than 60 set it to uprev
755 755 if rev < web.maxshortchanges:
756 756 startrev = uprev
757 757
758 758 dag = graphmod.dagwalker(web.repo, range(startrev, downrev - 1, -1))
759 759 tree = list(graphmod.colored(dag))
760 760 canvasheight = (len(tree) + 1) * bg_height - 27
761 761 data = []
762 762 for (id, type, ctx, vtx, edges) in tree:
763 763 if type != graphmod.CHANGESET:
764 764 continue
765 node = short(ctx.node())
765 node = str(ctx)
766 766 age = templatefilters.age(ctx.date())
767 767 desc = templatefilters.firstline(ctx.description())
768 768 desc = cgi.escape(templatefilters.nonempty(desc))
769 769 user = cgi.escape(templatefilters.person(ctx.user()))
770 770 branch = ctx.branch()
771 771 branch = branch, web.repo.branchtags().get(branch) == ctx.node()
772 772 data.append((node, vtx, edges, desc, user, age, branch, ctx.tags(),
773 773 ctx.bookmarks()))
774 774
775 775 return tmpl('graph', rev=rev, revcount=revcount, uprev=uprev,
776 776 lessvars=lessvars, morevars=morevars, downrev=downrev,
777 777 canvasheight=canvasheight, jsdata=data, bg_height=bg_height,
778 778 node=revnode_hex, changenav=changenav)
779 779
780 780 def _getdoc(e):
781 781 doc = e[0].__doc__
782 782 if doc:
783 783 doc = doc.split('\n')[0]
784 784 else:
785 785 doc = _('(no help text available)')
786 786 return doc
787 787
788 788 def help(web, req, tmpl):
789 789 from mercurial import commands # avoid cycle
790 790
791 791 topicname = req.form.get('node', [None])[0]
792 792 if not topicname:
793 topic = []
794
795 793 def topics(**map):
796 794 for entries, summary, _ in helpmod.helptable:
797 795 entries = sorted(entries, key=len)
798 796 yield {'topic': entries[-1], 'summary': summary}
799 797
800 798 early, other = [], []
801 799 primary = lambda s: s.split('|')[0]
802 800 for c, e in commands.table.iteritems():
803 801 doc = _getdoc(e)
804 802 if 'DEPRECATED' in doc or c.startswith('debug'):
805 803 continue
806 804 cmd = primary(c)
807 805 if cmd.startswith('^'):
808 806 early.append((cmd[1:], doc))
809 807 else:
810 808 other.append((cmd, doc))
811 809
812 810 early.sort()
813 811 other.sort()
814 812
815 813 def earlycommands(**map):
816 814 for c, doc in early:
817 815 yield {'topic': c, 'summary': doc}
818 816
819 817 def othercommands(**map):
820 818 for c, doc in other:
821 819 yield {'topic': c, 'summary': doc}
822 820
823 821 return tmpl('helptopics', topics=topics, earlycommands=earlycommands,
824 822 othercommands=othercommands, title='Index')
825 823
826 824 u = webutil.wsgiui()
827 825 u.pushbuffer()
828 826 try:
829 827 commands.help_(u, topicname)
830 828 except error.UnknownCommand:
831 829 raise ErrorResponse(HTTP_NOT_FOUND)
832 830 doc = u.popbuffer()
833 831 return tmpl('help', topic=topicname, doc=doc)
@@ -1,233 +1,233 b''
1 1 # hgweb/webutil.py - utility library for the web interface.
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, copy
10 10 from mercurial import match, patch, scmutil, error, ui
11 11 from mercurial.node import hex, nullid
12 12
13 13 def up(p):
14 14 if p[0] != "/":
15 15 p = "/" + p
16 16 if p[-1] == "/":
17 17 p = p[:-1]
18 18 up = os.path.dirname(p)
19 19 if up == "/":
20 20 return "/"
21 21 return up + "/"
22 22
23 23 def revnavgen(pos, pagelen, limit, nodefunc):
24 24 def seq(factor, limit=None):
25 25 if limit:
26 26 yield limit
27 27 if limit >= 20 and limit <= 40:
28 28 yield 50
29 29 else:
30 30 yield 1 * factor
31 31 yield 3 * factor
32 32 for f in seq(factor * 10):
33 33 yield f
34 34
35 35 navbefore = []
36 36 navafter = []
37 37
38 38 last = 0
39 39 for f in seq(1, pagelen):
40 40 if f < pagelen or f <= last:
41 41 continue
42 42 if f > limit:
43 43 break
44 44 last = f
45 45 if pos + f < limit:
46 46 navafter.append(("+%d" % f, hex(nodefunc(pos + f).node())))
47 47 if pos - f >= 0:
48 48 navbefore.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
49 49
50 50 navafter.append(("tip", "tip"))
51 51 try:
52 52 navbefore.insert(0, ("(0)", hex(nodefunc('0').node())))
53 53 except error.RepoError:
54 54 pass
55 55
56 56 def gen(l):
57 57 def f(**map):
58 58 for label, node in l:
59 59 yield {"label": label, "node": node}
60 60 return f
61 61
62 62 return (dict(before=gen(navbefore), after=gen(navafter)),)
63 63
64 64 def _siblings(siblings=[], hiderev=None):
65 65 siblings = [s for s in siblings if s.node() != nullid]
66 66 if len(siblings) == 1 and siblings[0].rev() == hiderev:
67 67 return
68 68 for s in siblings:
69 d = {'node': hex(s.node()), 'rev': s.rev()}
69 d = {'node': s.hex(), 'rev': s.rev()}
70 70 d['user'] = s.user()
71 71 d['date'] = s.date()
72 72 d['description'] = s.description()
73 73 d['branch'] = s.branch()
74 74 if hasattr(s, 'path'):
75 75 d['file'] = s.path()
76 76 yield d
77 77
78 78 def parents(ctx, hide=None):
79 79 return _siblings(ctx.parents(), hide)
80 80
81 81 def children(ctx, hide=None):
82 82 return _siblings(ctx.children(), hide)
83 83
84 84 def renamelink(fctx):
85 85 r = fctx.renamed()
86 86 if r:
87 87 return [dict(file=r[0], node=hex(r[1]))]
88 88 return []
89 89
90 90 def nodetagsdict(repo, node):
91 91 return [{"name": i} for i in repo.nodetags(node)]
92 92
93 93 def nodebookmarksdict(repo, node):
94 94 return [{"name": i} for i in repo.nodebookmarks(node)]
95 95
96 96 def nodebranchdict(repo, ctx):
97 97 branches = []
98 98 branch = ctx.branch()
99 99 # If this is an empty repo, ctx.node() == nullid,
100 100 # ctx.branch() == 'default', but branchtags() is
101 101 # an empty dict. Using dict.get avoids a traceback.
102 102 if repo.branchtags().get(branch) == ctx.node():
103 103 branches.append({"name": branch})
104 104 return branches
105 105
106 106 def nodeinbranch(repo, ctx):
107 107 branches = []
108 108 branch = ctx.branch()
109 109 if branch != 'default' and repo.branchtags().get(branch) != ctx.node():
110 110 branches.append({"name": branch})
111 111 return branches
112 112
113 113 def nodebranchnodefault(ctx):
114 114 branches = []
115 115 branch = ctx.branch()
116 116 if branch != 'default':
117 117 branches.append({"name": branch})
118 118 return branches
119 119
120 120 def showtag(repo, tmpl, t1, node=nullid, **args):
121 121 for t in repo.nodetags(node):
122 122 yield tmpl(t1, tag=t, **args)
123 123
124 124 def showbookmark(repo, tmpl, t1, node=nullid, **args):
125 125 for t in repo.nodebookmarks(node):
126 126 yield tmpl(t1, bookmark=t, **args)
127 127
128 128 def cleanpath(repo, path):
129 129 path = path.lstrip('/')
130 130 return scmutil.canonpath(repo.root, '', path)
131 131
132 132 def changectx(repo, req):
133 133 changeid = "tip"
134 134 if 'node' in req.form:
135 135 changeid = req.form['node'][0]
136 136 elif 'manifest' in req.form:
137 137 changeid = req.form['manifest'][0]
138 138
139 139 try:
140 140 ctx = repo[changeid]
141 141 except error.RepoError:
142 142 man = repo.manifest
143 143 ctx = repo[man.linkrev(man.rev(man.lookup(changeid)))]
144 144
145 145 return ctx
146 146
147 147 def filectx(repo, req):
148 148 path = cleanpath(repo, req.form['file'][0])
149 149 if 'node' in req.form:
150 150 changeid = req.form['node'][0]
151 151 else:
152 152 changeid = req.form['filenode'][0]
153 153 try:
154 154 fctx = repo[changeid][path]
155 155 except error.RepoError:
156 156 fctx = repo.filectx(path, fileid=changeid)
157 157
158 158 return fctx
159 159
160 160 def listfilediffs(tmpl, files, node, max):
161 161 for f in files[:max]:
162 162 yield tmpl('filedifflink', node=hex(node), file=f)
163 163 if len(files) > max:
164 164 yield tmpl('fileellipses')
165 165
166 166 def diffs(repo, tmpl, ctx, files, parity, style):
167 167
168 168 def countgen():
169 169 start = 1
170 170 while True:
171 171 yield start
172 172 start += 1
173 173
174 174 blockcount = countgen()
175 175 def prettyprintlines(diff):
176 176 blockno = blockcount.next()
177 177 for lineno, l in enumerate(diff.splitlines(True)):
178 178 lineno = "%d.%d" % (blockno, lineno + 1)
179 179 if l.startswith('+'):
180 180 ltype = "difflineplus"
181 181 elif l.startswith('-'):
182 182 ltype = "difflineminus"
183 183 elif l.startswith('@'):
184 184 ltype = "difflineat"
185 185 else:
186 186 ltype = "diffline"
187 187 yield tmpl(ltype,
188 188 line=l,
189 189 lineid="l%s" % lineno,
190 190 linenumber="% 8s" % lineno)
191 191
192 192 if files:
193 193 m = match.exact(repo.root, repo.getcwd(), files)
194 194 else:
195 195 m = match.always(repo.root, repo.getcwd())
196 196
197 197 diffopts = patch.diffopts(repo.ui, untrusted=True)
198 198 parents = ctx.parents()
199 199 node1 = parents and parents[0].node() or nullid
200 200 node2 = ctx.node()
201 201
202 202 block = []
203 203 for chunk in patch.diff(repo, node1, node2, m, opts=diffopts):
204 204 if chunk.startswith('diff') and block:
205 205 yield tmpl('diffblock', parity=parity.next(),
206 206 lines=prettyprintlines(''.join(block)))
207 207 block = []
208 208 if chunk.startswith('diff') and style != 'raw':
209 209 chunk = ''.join(chunk.splitlines(True)[1:])
210 210 block.append(chunk)
211 211 yield tmpl('diffblock', parity=parity.next(),
212 212 lines=prettyprintlines(''.join(block)))
213 213
214 214 class sessionvars(object):
215 215 def __init__(self, vars, start='?'):
216 216 self.start = start
217 217 self.vars = vars
218 218 def __getitem__(self, key):
219 219 return self.vars[key]
220 220 def __setitem__(self, key, value):
221 221 self.vars[key] = value
222 222 def __copy__(self):
223 223 return sessionvars(copy.copy(self.vars), self.start)
224 224 def __iter__(self):
225 225 separator = self.start
226 226 for key, value in self.vars.iteritems():
227 227 yield {'name': key, 'value': str(value), 'separator': separator}
228 228 separator = '&'
229 229
230 230 class wsgiui(ui.ui):
231 231 # default termwidth breaks under mod_wsgi
232 232 def termwidth(self):
233 233 return 80
General Comments 0
You need to be logged in to leave comments. Login now