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