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